Init
This commit is contained in:
430
content/world/entities/inventory/transactions.en.md
Normal file
430
content/world/entities/inventory/transactions.en.md
Normal file
@@ -0,0 +1,430 @@
|
||||
---
|
||||
title: Transactions
|
||||
type: docs
|
||||
weight: 3
|
||||
---
|
||||
|
||||
Transactions track the results of inventory operations in Hytale. Every modification to an `ItemContainer` returns a Transaction object that describes what happened.
|
||||
|
||||
## Transaction Interface
|
||||
|
||||
All transactions implement the base `Transaction` interface:
|
||||
|
||||
```java
|
||||
public interface Transaction {
|
||||
// Did the operation succeed?
|
||||
boolean succeeded();
|
||||
|
||||
// Was a specific slot modified?
|
||||
boolean wasSlotModified(short slot);
|
||||
}
|
||||
```
|
||||
|
||||
## Transaction Types
|
||||
|
||||
### SlotTransaction
|
||||
|
||||
Tracks changes to a single slot:
|
||||
|
||||
```java
|
||||
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
|
||||
|
||||
if (transaction.succeeded()) {
|
||||
// What was in the slot before
|
||||
ItemStack before = transaction.getSlotBefore();
|
||||
|
||||
// What is in the slot now
|
||||
ItemStack after = transaction.getSlotAfter();
|
||||
|
||||
// The item that was removed/output
|
||||
ItemStack output = transaction.getOutput();
|
||||
|
||||
// Which slot was affected
|
||||
short slot = transaction.getSlot();
|
||||
|
||||
// What type of action (ADD, REMOVE, REPLACE)
|
||||
ActionType action = transaction.getAction();
|
||||
}
|
||||
```
|
||||
|
||||
### ItemStackSlotTransaction
|
||||
|
||||
Extended slot transaction with additional details:
|
||||
|
||||
```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();
|
||||
|
||||
// Check options used
|
||||
boolean wasFiltered = transaction.isFilter();
|
||||
boolean wasAllOrNothing = transaction.isAllOrNothing();
|
||||
}
|
||||
```
|
||||
|
||||
### ItemStackTransaction
|
||||
|
||||
Tracks operations that may affect multiple slots:
|
||||
|
||||
```java
|
||||
ItemStack toAdd = new ItemStack("stone", 128);
|
||||
ItemStackTransaction transaction = container.addItemStack(toAdd);
|
||||
|
||||
if (transaction.succeeded()) {
|
||||
// Items that couldn't fit (null if all fit)
|
||||
ItemStack remainder = transaction.getRemainder();
|
||||
|
||||
// Original item we tried to add
|
||||
ItemStack query = transaction.getQuery();
|
||||
|
||||
// List of all slot transactions that occurred
|
||||
List<ItemStackSlotTransaction> slotTransactions = transaction.getSlotTransactions();
|
||||
|
||||
for (ItemStackSlotTransaction slotTx : slotTransactions) {
|
||||
getLogger().at(Level.INFO).log("Modified slot " + slotTx.getSlot());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MoveTransaction
|
||||
|
||||
Tracks moving items between containers:
|
||||
|
||||
```java
|
||||
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
|
||||
(short) 0,
|
||||
32,
|
||||
hotbar,
|
||||
(short) 0
|
||||
);
|
||||
|
||||
if (transaction.succeeded()) {
|
||||
// Transaction for removing from source
|
||||
SlotTransaction removeTransaction = transaction.getRemoveTransaction();
|
||||
|
||||
// Transaction for adding to destination
|
||||
SlotTransaction addTransaction = transaction.getAddTransaction();
|
||||
|
||||
// The destination container
|
||||
ItemContainer destination = transaction.getOtherContainer();
|
||||
|
||||
// Direction of the move
|
||||
MoveType moveType = transaction.getMoveType();
|
||||
}
|
||||
```
|
||||
|
||||
### ListTransaction
|
||||
|
||||
Wraps multiple 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
|
||||
|
||||
Operations are categorized by action type:
|
||||
|
||||
```java
|
||||
public enum ActionType {
|
||||
SET, // Items set (add=true, remove=false, destroy=true)
|
||||
ADD, // Items added to slot (add=true, remove=false, destroy=false)
|
||||
REMOVE, // Items removed from slot (add=false, remove=true, destroy=false)
|
||||
REPLACE // Slot contents replaced (add=true, remove=true, destroy=false)
|
||||
}
|
||||
|
||||
// Check action characteristics
|
||||
if (action.isAdd()) { /* operation adds items */ }
|
||||
if (action.isRemove()) { /* operation removes items */ }
|
||||
if (action.isDestroy()) { /* operation destroys slot contents */ }
|
||||
```
|
||||
|
||||
## MoveType
|
||||
|
||||
Direction of move operations:
|
||||
|
||||
```java
|
||||
public enum MoveType {
|
||||
MOVE_TO_SELF, // Items being moved TO this container
|
||||
MOVE_FROM_SELF // Items being moved FROM this container
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Check Before Modify
|
||||
|
||||
```java
|
||||
// Safe pattern: verify first, then execute
|
||||
public boolean safeTransfer(ItemContainer from, ItemContainer to, String itemId, int amount) {
|
||||
ItemStack toRemove = new ItemStack(itemId, amount);
|
||||
|
||||
// Check both operations can succeed
|
||||
if (!from.canRemoveItemStack(toRemove)) {
|
||||
return false; // Not enough items
|
||||
}
|
||||
|
||||
if (!to.canAddItemStack(toRemove)) {
|
||||
return false; // No space
|
||||
}
|
||||
|
||||
// Execute removal
|
||||
ItemStackTransaction removeResult = from.removeItemStack(toRemove);
|
||||
if (!removeResult.succeeded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute addition
|
||||
ItemStack removed = removeResult.getQuery();
|
||||
ItemStackTransaction addResult = to.addItemStack(removed);
|
||||
|
||||
// Handle any remainder
|
||||
ItemStack remainder = addResult.getRemainder();
|
||||
if (!ItemStack.isEmpty(remainder)) {
|
||||
// Put remainder back
|
||||
from.addItemStack(remainder);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### Handling Remainders
|
||||
|
||||
```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("Inventory full!"));
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack remainder = result.getRemainder();
|
||||
if (!ItemStack.isEmpty(remainder)) {
|
||||
player.sendMessage(Message.raw(
|
||||
"Only received " + (quantity - remainder.getQuantity()) + " items, " +
|
||||
"inventory full!"
|
||||
));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Atomic Operations
|
||||
|
||||
```java
|
||||
// allOrNothing=true ensures partial operations don't happen
|
||||
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);
|
||||
|
||||
// Check both operations can succeed fully
|
||||
if (!storage.canRemoveItemStack(currency)) {
|
||||
player.sendMessage(Message.raw("Not enough gold!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!storage.canAddItemStack(item)) {
|
||||
player.sendMessage(Message.raw("Inventory full!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove currency with allOrNothing=true
|
||||
ItemStackTransaction removeResult = storage.removeItemStack(currency, true, true);
|
||||
if (!removeResult.succeeded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add item
|
||||
ItemStackTransaction addResult = storage.addItemStack(item, true, false, true);
|
||||
if (!addResult.succeeded()) {
|
||||
// Rollback: return the currency
|
||||
storage.addItemStack(currency);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### Tracking Changes
|
||||
|
||||
```java
|
||||
// Use wasSlotModified to check specific slots
|
||||
public void onContainerChange(ItemContainer.ItemContainerChangeEvent event) {
|
||||
Transaction transaction = event.transaction();
|
||||
|
||||
// Check if hotbar slot 0 was affected
|
||||
if (transaction.wasSlotModified((short) 0)) {
|
||||
getLogger().at(Level.INFO).log("First slot was modified!");
|
||||
}
|
||||
|
||||
// Check all hotbar slots
|
||||
for (short i = 0; i < 9; i++) {
|
||||
if (transaction.wasSlotModified(i)) {
|
||||
getLogger().at(Level.INFO).log("Hotbar slot " + i + " modified");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Transaction Options
|
||||
|
||||
Many operations accept optional parameters:
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `allOrNothing` | If true, operation fails if not all items can be processed | `false` |
|
||||
| `fullStacks` | If true, only add to empty slots (not partial stacks) | `false` |
|
||||
| `exactAmount` | If true, must remove exact quantity requested | `true` |
|
||||
| `filter` | If true, respect slot filters | `true` |
|
||||
|
||||
```java
|
||||
// Default behavior
|
||||
container.addItemStack(item);
|
||||
|
||||
// With options
|
||||
container.addItemStack(item, true, false, true); // allOrNothing, fullStacks, filter
|
||||
container.removeItemStack(item, true, true); // allOrNothing, filter
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
{{< callout type="info" >}}
|
||||
**Transaction Tips:**
|
||||
- Always check `succeeded()` before accessing results
|
||||
- Handle remainders when adding items
|
||||
- Use `canAddItemStack()`/`canRemoveItemStack()` for pre-validation
|
||||
- Use `allOrNothing=true` for critical operations
|
||||
- Check `wasSlotModified()` to track specific slot changes
|
||||
{{< /callout >}}
|
||||
|
||||
```java
|
||||
// Good: Check success and handle remainder
|
||||
ItemStackTransaction tx = container.addItemStack(item);
|
||||
if (tx.succeeded()) {
|
||||
ItemStack remainder = tx.getRemainder();
|
||||
if (!ItemStack.isEmpty(remainder)) {
|
||||
// Handle leftover items
|
||||
}
|
||||
}
|
||||
|
||||
// Bad: Assume success
|
||||
container.addItemStack(item); // Might fail silently!
|
||||
```
|
||||
|
||||
## Transaction API Reference
|
||||
|
||||
### Transaction Interface
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `succeeded()` | `boolean` | True if operation succeeded |
|
||||
| `wasSlotModified(short)` | `boolean` | True if specific slot was modified |
|
||||
|
||||
### SlotTransaction
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `getSlot()` | `short` | The affected slot index |
|
||||
| `getAction()` | `ActionType` | Type of action performed |
|
||||
| `getSlotBefore()` | `ItemStack?` | Contents before operation |
|
||||
| `getSlotAfter()` | `ItemStack?` | Contents after operation |
|
||||
| `getOutput()` | `ItemStack?` | Items removed/output |
|
||||
| `isAllOrNothing()` | `boolean` | allOrNothing parameter used |
|
||||
| `isExactAmount()` | `boolean` | exactAmount parameter used |
|
||||
| `isFilter()` | `boolean` | filter parameter used |
|
||||
|
||||
| Static | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `FAILED_ADD` | `SlotTransaction` | Pre-built failed add transaction |
|
||||
|
||||
### ItemStackSlotTransaction (extends SlotTransaction)
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `isAddToExistingSlot()` | `boolean` | True if added to existing stack |
|
||||
| `getQuery()` | `ItemStack?` | Original item requested |
|
||||
| `getRemainder()` | `ItemStack?` | Items that couldn't fit |
|
||||
|
||||
### ItemStackTransaction
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `getAction()` | `ActionType?` | Type of action performed |
|
||||
| `getQuery()` | `ItemStack?` | Original item requested |
|
||||
| `getRemainder()` | `ItemStack?` | Items that couldn't fit |
|
||||
| `isAllOrNothing()` | `boolean` | allOrNothing parameter used |
|
||||
| `isFilter()` | `boolean` | filter parameter used |
|
||||
| `getSlotTransactions()` | `List<ItemStackSlotTransaction>` | All slot transactions |
|
||||
|
||||
| Static | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `FAILED_ADD` | `ItemStackTransaction` | Pre-built failed add transaction |
|
||||
|
||||
### MoveTransaction<T extends Transaction>
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `getRemoveTransaction()` | `SlotTransaction` | Transaction for removal |
|
||||
| `getMoveType()` | `MoveType` | Direction of move |
|
||||
| `getOtherContainer()` | `ItemContainer` | The other container involved |
|
||||
| `getAddTransaction()` | `T` | Transaction for addition |
|
||||
| `toInverted(ItemContainer)` | `MoveTransaction<T>` | Create inverted view for other container |
|
||||
|
||||
### ListTransaction<T extends Transaction>
|
||||
|
||||
| Method | Returns | Description |
|
||||
|--------|---------|-------------|
|
||||
| `getList()` | `List<T>` | All transactions in list |
|
||||
| `size()` | `int` | Number of transactions |
|
||||
|
||||
| Static | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `EMPTY_SUCCESSFUL_TRANSACTION` | `ListTransaction<?>` | Pre-built empty success |
|
||||
| `EMPTY_FAILED_TRANSACTION` | `ListTransaction<?>` | Pre-built empty failure |
|
||||
| `getEmptyTransaction(boolean)` | `ListTransaction<T>` | Get empty success/failure |
|
||||
|
||||
### ActionType Enum
|
||||
|
||||
| Value | isAdd | isRemove | isDestroy | Description |
|
||||
|-------|-------|----------|-----------|-------------|
|
||||
| `SET` | true | false | true | Set slot contents |
|
||||
| `ADD` | true | false | false | Add items to slot |
|
||||
| `REMOVE` | false | true | false | Remove items from slot |
|
||||
| `REPLACE` | true | true | false | Replace slot contents |
|
||||
|
||||
### MoveType Enum
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `MOVE_TO_SELF` | Items moving to this container |
|
||||
| `MOVE_FROM_SELF` | Items moving from this container |
|
||||
Reference in New Issue
Block a user