---
title: Transactions
type: docs
weight: 3
---

Les transactions suivent les résultats des opérations d'inventaire dans Hytale. Chaque modification d'un `ItemContainer` retourne un objet Transaction qui décrit ce qui s'est passé.

## Interface Transaction

Toutes les transactions implémentent l'interface de base `Transaction` :

```java
public interface Transaction {
    // L'opération a-t-elle réussi ?
    boolean succeeded();

    // Un slot spécifique a-t-il été modifié ?
    boolean wasSlotModified(short slot);
}
```

## Types de Transactions

### SlotTransaction

Suit les changements d'un seul slot :

```java
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);

if (transaction.succeeded()) {
    // Ce qui était dans le slot avant
    ItemStack before = transaction.getSlotBefore();

    // Ce qui est dans le slot maintenant
    ItemStack after = transaction.getSlotAfter();

    // L'objet qui a été retiré/sorti
    ItemStack output = transaction.getOutput();

    // Quel slot a été affecté
    short slot = transaction.getSlot();

    // Quel type d'action (ADD, REMOVE, REPLACE)
    ActionType action = transaction.getAction();
}
```

### ItemStackSlotTransaction

Transaction de slot étendue avec des détails supplémentaires :

```java
ItemStackSlotTransaction transaction = container.setItemStackForSlot(
    (short) 0,
    new ItemStack("iron_sword", 1)
);

if (transaction.succeeded()) {
    short slot = transaction.getSlot();
    ItemStack before = transaction.getSlotBefore();
    ItemStack after = transaction.getSlotAfter();

    // Vérifier les options utilisées
    boolean wasFiltered = transaction.isFilter();
    boolean wasAllOrNothing = transaction.isAllOrNothing();
}
```

### ItemStackTransaction

Suit les opérations qui peuvent affecter plusieurs slots :

```java
ItemStack toAdd = new ItemStack("stone", 128);
ItemStackTransaction transaction = container.addItemStack(toAdd);

if (transaction.succeeded()) {
    // Objets qui n'ont pas pu rentrer (null si tous ont rentré)
    ItemStack remainder = transaction.getRemainder();

    // Objet original qu'on a essayé d'ajouter
    ItemStack query = transaction.getQuery();

    // Liste de toutes les transactions de slot qui ont eu lieu
    List<ItemStackSlotTransaction> slotTransactions = transaction.getSlotTransactions();

    for (ItemStackSlotTransaction slotTx : slotTransactions) {
        getLogger().at(Level.INFO).log("Slot modifié " + slotTx.getSlot());
    }
}
```

### MoveTransaction

Suit le déplacement d'objets entre conteneurs :

```java
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
    (short) 0,
    32,
    hotbar,
    (short) 0
);

if (transaction.succeeded()) {
    // Transaction pour le retrait de la source
    SlotTransaction removeTransaction = transaction.getRemoveTransaction();

    // Transaction pour l'ajout à la destination
    SlotTransaction addTransaction = transaction.getAddTransaction();

    // Le conteneur de destination
    ItemContainer destination = transaction.getOtherContainer();

    // Direction du déplacement
    MoveType moveType = transaction.getMoveType();
}
```

### ListTransaction

Encapsule plusieurs transactions :

```java
List<ItemStack> items = List.of(
    new ItemStack("stone", 64),
    new ItemStack("wood", 64)
);

ListTransaction<ItemStackTransaction> transaction = container.addItemStacks(items);

if (transaction.succeeded()) {
    List<ItemStackTransaction> results = transaction.getList();

    for (ItemStackTransaction result : results) {
        if (result.succeeded()) {
            ItemStack remainder = result.getRemainder();
            // ...
        }
    }
}
```

## ActionType

Les opérations sont catégorisées par type d'action :

```java
public enum ActionType {
    SET,      // Objets définis (add=true, remove=false, destroy=true)
    ADD,      // Objets ajoutés au slot (add=true, remove=false, destroy=false)
    REMOVE,   // Objets retirés du slot (add=false, remove=true, destroy=false)
    REPLACE   // Contenu du slot remplacé (add=true, remove=true, destroy=false)
}

// Vérifier les caractéristiques de l'action
if (action.isAdd()) { /* opération ajoute des objets */ }
if (action.isRemove()) { /* opération retire des objets */ }
if (action.isDestroy()) { /* opération détruit le contenu du slot */ }
```

## MoveType

Direction des opérations de déplacement :

```java
public enum MoveType {
    MOVE_TO_SELF,   // Objets déplacés VERS ce conteneur
    MOVE_FROM_SELF  // Objets déplacés DEPUIS ce conteneur
}
```

## Patterns Courants

### Vérifier Avant de Modifier

```java
// Pattern sûr : vérifier d'abord, puis exécuter
public boolean safeTransfer(ItemContainer from, ItemContainer to, String itemId, int amount) {
    ItemStack toRemove = new ItemStack(itemId, amount);

    // Vérifier que les deux opérations peuvent réussir
    if (!from.canRemoveItemStack(toRemove)) {
        return false;  // Pas assez d'objets
    }

    if (!to.canAddItemStack(toRemove)) {
        return false;  // Pas de place
    }

    // Exécuter le retrait
    ItemStackTransaction removeResult = from.removeItemStack(toRemove);
    if (!removeResult.succeeded()) {
        return false;
    }

    // Exécuter l'ajout
    ItemStack removed = removeResult.getQuery();
    ItemStackTransaction addResult = to.addItemStack(removed);

    // Gérer les restes éventuels
    ItemStack remainder = addResult.getRemainder();
    if (!ItemStack.isEmpty(remainder)) {
        // Remettre les restes
        from.addItemStack(remainder);
    }

    return true;
}
```

### Gérer les Restes

```java
public void giveItemSafe(Player player, String itemId, int quantity) {
    ItemContainer storage = player.getInventory().getStorage();
    ItemStack item = new ItemStack(itemId, quantity);

    ItemStackTransaction result = storage.addItemStack(item);

    if (!result.succeeded()) {
        player.sendMessage(Message.raw("Inventaire plein !"));
        return;
    }

    ItemStack remainder = result.getRemainder();
    if (!ItemStack.isEmpty(remainder)) {
        player.sendMessage(Message.raw(
            "Seulement reçu " + (quantity - remainder.getQuantity()) + " objets, " +
            "inventaire plein !"
        ));
    }
}
```

### Opérations Atomiques

```java
// allOrNothing=true garantit que les opérations partielles ne se produisent pas
public boolean buyItem(Player player, String itemId, int price, int quantity) {
    Inventory inv = player.getInventory();
    ItemContainer storage = inv.getStorage();

    ItemStack currency = new ItemStack("gold_coin", price);
    ItemStack item = new ItemStack(itemId, quantity);

    // Vérifier que les deux opérations peuvent réussir complètement
    if (!storage.canRemoveItemStack(currency)) {
        player.sendMessage(Message.raw("Pas assez d'or !"));
        return false;
    }

    if (!storage.canAddItemStack(item)) {
        player.sendMessage(Message.raw("Inventaire plein !"));
        return false;
    }

    // Retirer la monnaie avec allOrNothing=true
    ItemStackTransaction removeResult = storage.removeItemStack(currency, true, true);
    if (!removeResult.succeeded()) {
        return false;
    }

    // Ajouter l'objet
    ItemStackTransaction addResult = storage.addItemStack(item, true, false, true);
    if (!addResult.succeeded()) {
        // Annuler : rendre la monnaie
        storage.addItemStack(currency);
        return false;
    }

    return true;
}
```

### Suivre les Changements

```java
// Utiliser wasSlotModified pour vérifier des slots spécifiques
public void onContainerChange(ItemContainer.ItemContainerChangeEvent event) {
    Transaction transaction = event.transaction();

    // Vérifier si le slot 0 de la hotbar a été affecté
    if (transaction.wasSlotModified((short) 0)) {
        getLogger().at(Level.INFO).log("Premier slot modifié !");
    }

    // Vérifier tous les slots de la hotbar
    for (short i = 0; i < 9; i++) {
        if (transaction.wasSlotModified(i)) {
            getLogger().at(Level.INFO).log("Slot hotbar " + i + " modifié");
        }
    }
}
```

## Options de Transaction

De nombreuses opérations acceptent des paramètres optionnels :

| Paramètre | Description | Par défaut |
|-----------|-------------|---------|
| `allOrNothing` | Si vrai, l'opération échoue si tous les objets ne peuvent pas être traités | `false` |
| `fullStacks` | Si vrai, ajoute seulement aux slots vides (pas aux piles partielles) | `false` |
| `exactAmount` | Si vrai, doit retirer exactement la quantité demandée | `true` |
| `filter` | Si vrai, respecte les filtres de slot | `true` |

```java
// Comportement par défaut
container.addItemStack(item);

// Avec options
container.addItemStack(item, true, false, true);  // allOrNothing, fullStacks, filter
container.removeItemStack(item, true, true);       // allOrNothing, filter
```

## Bonnes Pratiques

{{< callout type="info" >}}
**Conseils pour les Transactions :**
- Toujours vérifier `succeeded()` avant d'accéder aux résultats
- Gérer les restes lors de l'ajout d'objets
- Utiliser `canAddItemStack()`/`canRemoveItemStack()` pour la pré-validation
- Utiliser `allOrNothing=true` pour les opérations critiques
- Vérifier `wasSlotModified()` pour suivre les changements de slots spécifiques
{{< /callout >}}

```java
// Bon : Vérifier le succès et gérer les restes
ItemStackTransaction tx = container.addItemStack(item);
if (tx.succeeded()) {
    ItemStack remainder = tx.getRemainder();
    if (!ItemStack.isEmpty(remainder)) {
        // Gérer les objets restants
    }
}

// Mauvais : Supposer le succès
container.addItemStack(item);  // Pourrait échouer silencieusement !
```

## Référence API Transaction

### Interface Transaction

| Méthode | Retour | Description |
|--------|---------|-------------|
| `succeeded()` | `boolean` | True si l'opération a réussi |
| `wasSlotModified(short)` | `boolean` | True si le slot spécifique a été modifié |

### SlotTransaction

| Méthode | Retour | Description |
|--------|---------|-------------|
| `getSlot()` | `short` | Index du slot affecté |
| `getAction()` | `ActionType` | Type d'action effectuée |
| `getSlotBefore()` | `ItemStack?` | Contenu avant l'opération |
| `getSlotAfter()` | `ItemStack?` | Contenu après l'opération |
| `getOutput()` | `ItemStack?` | Objets retirés/sortis |
| `isAllOrNothing()` | `boolean` | Paramètre allOrNothing utilisé |
| `isExactAmount()` | `boolean` | Paramètre exactAmount utilisé |
| `isFilter()` | `boolean` | Paramètre filter utilisé |

| Statique | Type | Description |
|----------|------|-------------|
| `FAILED_ADD` | `SlotTransaction` | Transaction d'ajout échouée pré-construite |

### ItemStackSlotTransaction (étend SlotTransaction)

| Méthode | Retour | Description |
|--------|---------|-------------|
| `isAddToExistingSlot()` | `boolean` | True si ajouté à une pile existante |
| `getQuery()` | `ItemStack?` | Objet original demandé |
| `getRemainder()` | `ItemStack?` | Objets qui n'ont pas pu rentrer |

### ItemStackTransaction

| Méthode | Retour | Description |
|--------|---------|-------------|
| `getAction()` | `ActionType?` | Type d'action effectuée |
| `getQuery()` | `ItemStack?` | Objet original demandé |
| `getRemainder()` | `ItemStack?` | Objets qui n'ont pas pu rentrer |
| `isAllOrNothing()` | `boolean` | Paramètre allOrNothing utilisé |
| `isFilter()` | `boolean` | Paramètre filter utilisé |
| `getSlotTransactions()` | `List<ItemStackSlotTransaction>` | Toutes les transactions de slot |

| Statique | Type | Description |
|----------|------|-------------|
| `FAILED_ADD` | `ItemStackTransaction` | Transaction d'ajout échouée pré-construite |

### MoveTransaction<T extends Transaction>

| Méthode | Retour | Description |
|--------|---------|-------------|
| `getRemoveTransaction()` | `SlotTransaction` | Transaction de retrait |
| `getMoveType()` | `MoveType` | Direction du déplacement |
| `getOtherContainer()` | `ItemContainer` | L'autre conteneur impliqué |
| `getAddTransaction()` | `T` | Transaction d'ajout |
| `toInverted(ItemContainer)` | `MoveTransaction<T>` | Créer vue inversée pour l'autre conteneur |

### ListTransaction<T extends Transaction>

| Méthode | Retour | Description |
|--------|---------|-------------|
| `getList()` | `List<T>` | Toutes les transactions de la liste |
| `size()` | `int` | Nombre de transactions |

| Statique | Type | Description |
|----------|------|-------------|
| `EMPTY_SUCCESSFUL_TRANSACTION` | `ListTransaction<?>` | Succès vide pré-construit |
| `EMPTY_FAILED_TRANSACTION` | `ListTransaction<?>` | Échec vide pré-construit |
| `getEmptyTransaction(boolean)` | `ListTransaction<T>` | Obtenir succès/échec vide |

### Enum ActionType

| Valeur | isAdd | isRemove | isDestroy | Description |
|--------|-------|----------|-----------|-------------|
| `SET` | true | false | true | Définir le contenu du slot |
| `ADD` | true | false | false | Ajouter des objets au slot |
| `REMOVE` | false | true | false | Retirer des objets du slot |
| `REPLACE` | true | true | false | Remplacer le contenu du slot |

### Enum MoveType

| Valeur | Description |
|--------|-------------|
| `MOVE_TO_SELF` | Objets déplacés vers ce conteneur |
| `MOVE_FROM_SELF` | Objets déplacés depuis ce conteneur |
