Skip to content

Commit

Permalink
Implement Inventory Transactions
Browse files Browse the repository at this point in the history
Changes:
* Implement a transaction based system for resolving inventory changes. A new transaction is created which has all the actions to resolve it, with each action optionally requiring a confirmation step. For example all Click/Drops should only proceed after receiving an associated confirm packet otherwise out of order updates occur. This is also done asynchronously.
* Provide better support for overriding functionality in child inventory translator classes by splitting the steps of resolution out separately.
* Provide better support of refreshing of inventory by using an invalid click and then rejecting the returned packet after syncing the inventory.
* Remove the forced refresh from all translators as they now take care of their updates in an ordered fashion. The only refresh left is now used only when the resolution system knows it may leak items (for example picking up an arbitrary amount from the furnace output is not supported on java).
* Support resolving inventory changes that have more than 2 actions. This may resolve crafting recipes on some platforms and handle large inventory changes better
* If geysers knowledge of an inventory item does not match that of the server and it is rejected we will resync before the retrying the action.
* Added more documentation on whats happening in the inventory resolution.

Note that this is a required merge for GeyserMC#536 which will have this merged in already. There are further optimizations that can be done and are in general marked with a @todo once the current resolution system is tested enough.
  • Loading branch information
bundabrg committed Jun 12, 2020
1 parent 7fcd8f2 commit 406e154
Show file tree
Hide file tree
Showing 27 changed files with 1,183 additions and 613 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@
import com.nukkitx.math.vector.Vector3i;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.geysermc.connector.network.translators.inventory.action.Transaction;

import java.util.concurrent.atomic.AtomicInteger;

@ToString
public class Inventory {

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import lombok.Setter;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.action.Transaction;

public class InventoryCache {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.inventory.action.Execute;
import org.geysermc.connector.network.translators.inventory.action.Transaction;
import org.geysermc.connector.utils.InventoryUtils;

@Translator(packet = ContainerClosePacket.class)
Expand All @@ -49,10 +51,21 @@ public void translate(ContainerClosePacket packet, GeyserSession session) {
}
}

if (windowId == 0 || (openInventory != null && openInventory.getId() == windowId)) {
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
session.getDownstream().getSession().send(closeWindowPacket);
InventoryUtils.closeInventory(session, windowId);
byte finalWindowId = windowId;
Runnable runnable = () -> {
if (finalWindowId == 0 || (openInventory != null && openInventory.getId() == finalWindowId)) {
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(finalWindowId);
session.getDownstream().getSession().send(closeWindowPacket);
InventoryUtils.closeInventory(session, finalWindowId);
} else if (openInventory != null && openInventory.getId() != finalWindowId) {
InventoryUtils.openInventory(session, openInventory);
}
};

if (Transaction.CURRENT_TRANSACTION != null) {
Transaction.CURRENT_TRANSACTION.add(new Execute(runnable));
} else {
runnable.run();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@
import com.nukkitx.protocol.bedrock.data.ItemData;
import org.geysermc.connector.inventory.Inventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.inventory.action.Transaction;
import org.geysermc.connector.network.translators.inventory.action.Execute;
import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater;

import java.util.List;
import java.util.stream.Collectors;

public class AnvilInventoryTranslator extends BlockInventoryTranslator {
public AnvilInventoryTranslator() {
Expand Down Expand Up @@ -73,49 +76,8 @@ public int javaSlotToBedrock(int slot) {
}

@Override
public SlotType getSlotType(int javaSlot) {
if (javaSlot == 2)
return SlotType.OUTPUT;
return SlotType.NORMAL;
}

@Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
InventoryActionData anvilResult = null;
InventoryActionData anvilInput = null;
for (InventoryActionData action : actions) {
if (action.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL) {
//useless packet
return;
} else if (action.getSource().getContainerId() == ContainerId.ANVIL_RESULT) {
anvilResult = action;
} else if (bedrockSlotToJava(action) == 0) {
anvilInput = action;
}
}
ItemData itemName = null;
if (anvilResult != null) {
itemName = anvilResult.getFromItem();
} else if (anvilInput != null) {
itemName = anvilInput.getToItem();
}
if (itemName != null) {
String rename;
com.nukkitx.nbt.tag.CompoundTag tag = itemName.getTag();
if (tag != null) {
rename = tag.getCompound("display").getString("Name");
} else {
rename = "";
}
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
session.sendDownstreamPacket(renameItemPacket);
}
if (anvilResult != null) {
//client will send another packet to grab anvil output
return;
}

super.translateActions(session, inventory, actions);
public boolean isOutput(InventoryActionData action) {
return action.getSlot() == 50;
}

@Override
Expand Down Expand Up @@ -143,4 +105,31 @@ public void updateSlot(GeyserSession session, Inventory inventory, int slot) {
}
super.updateSlot(session, inventory, slot);
}

@Override
public void translateActions(GeyserSession session, Inventory inventory, List<InventoryActionData> actions) {
// Ignore these packets
if (actions.stream().anyMatch(a -> a.getSource().getContainerId() == ContainerId.ANVIL_RESULT
|| a.getSource().getContainerId() == ContainerId.ANVIL_MATERIAL)) {
return;
}

super.translateActions(session, inventory, actions);
}

@Override
protected void processAction(Transaction transaction, ActionData cursor, ActionData from, ActionData to) {
// If from is the output we add a rename packet
if (isOutput(from.action)) {
transaction.add(new Execute(() -> {
ItemData item = from.action.getFromItem();
com.nukkitx.nbt.tag.CompoundTag tag = item.getTag();
String rename = tag != null ? tag.getCompound("display").getString("Name") : "";
ClientRenameItemPacket renameItemPacket = new ClientRenameItemPacket(rename);
transaction.getSession().sendDownstreamPacket(renameItemPacket);
}));
}

super.processAction(transaction, cursor, from, to);
}
}
Loading

0 comments on commit 406e154

Please sign in to comment.