Class SwapManager
Modules call beginSwap(net.minecraft.world.item.Item, boolean) before using an item and endSwap(boolean)
when done to restore inventory state.
-
Nested Class Summary
Nested Classes -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic booleanLog every withSwap decision (caller, target, outcome) to the client log. -
Method Summary
Modifier and TypeMethodDescriptionbooleanbeginOffhandSwap(int invSlot) Swaps an item from the given inventory slot into the offhand.booleanMulti-tick swap that never moves the CLIENT's selection: only the server's held slot changes (a bareSetCarriedItem), so the local hand keeps rendering — and the local player keeps using — whatever they actually hold.booleanbeginSwap(InventoryUtil.FindItemResult result, boolean instant) Swaps the item at the given result slot into the main hand.booleanbeginSwap(InventoryUtil.FindItemResult result, boolean instant, boolean asymmetric) Swaps the item at the given result slot into the main hand.booleanbeginSwap(net.minecraft.world.item.Item item, boolean instant) Finds the given item and swaps it into the main hand.booleanbeginSwap(net.minecraft.world.item.Item item, boolean instant, boolean asymmetric) Finds the given item and swaps it into the main hand.booleanbeginSwapSlot(int hotbarSlot, boolean instant) Swaps a specific hotbar slot into the main hand.booleancanSwap(net.minecraft.world.item.Item item) Returns true if the given item can be swapped to under the current mode.voidRestores the offhand by swapping back.voidendSwap(boolean instant) Restores inventory state from a previousbeginSwap(net.minecraft.world.item.Item, boolean).static SwapManagerget()getSlot(net.minecraft.world.item.Item item) Returns the best slot for the given item under the current swap mode.voidinit()Register tick handler.booleanReturns true only when a container swap is active (a slot-spoof swap sends a SetCarriedItem packet, not a container click, so it never collides with another container click stream / stateId).booleanReturns true if the current instant swap used a container click (as opposed to hotbar slot spoofing).booleanReturns true if an offhand swap is currently active.booleanTrue while abeginSilentHold(me.hackware.api.util.InventoryUtil.FindItemResult)owns the server's slot without moving the client's.booleanReturns true if any silent swap (container or slot spoof) is currently active.booleanisSlotLocked(int slotIndex) Returns true if the given inventory slot is currently locked by a silent swap.booleanWhether any begin/end swap window (instant or multi-tick, silent or not) is currently open.voidreset()Clears all swap state.voidintsilentBudgetLeft(int priority) Carried-slot swaps still available this tick for a caller atpriority.voidStands in for vanilla's per-tickensureHasSentCarriedItem()(redirected inMultiPlayerGameModeMixin).booleanwithSwap(InventoryUtil.FindItemResult target, int priority, Runnable action) Putstargetin the active hand, runsaction, and restores — a hotbar/offhand target through the budgeted carried-slot route, a main-inventory target through container clicks (which also work mid-use).booleanwithSwap(InventoryUtil.FindItemResult target, Runnable action) booleanwouldContainerSwap(InventoryUtil.FindItemResult result, boolean instant) Predicts whetherbeginSwap(FindItemResult, boolean)would move the item with a container SWAP click (didSilentSwap) rather than a slot change / spoof — WITHOUT performing any swap.
-
Field Details
-
swapLogging
public static boolean swapLoggingLog every withSwap decision (caller, target, outcome) to the client log.
-
-
Method Details
-
get
-
init
public void init()Register tick handler. Call once during mod init. -
getSwapMode
-
setSwapMode
-
beginSwap
public boolean beginSwap(net.minecraft.world.item.Item item, boolean instant) Finds the given item and swaps it into the main hand.- Parameters:
item- the item to swap toinstant- true for a single-tick swap (e.g. throw pearl), false for multi-tick (e.g. hold bow)- Returns:
- true if the swap succeeded and the item is now in the main hand
-
beginSwap
public boolean beginSwap(net.minecraft.world.item.Item item, boolean instant, boolean asymmetric) Finds the given item and swaps it into the main hand.- Parameters:
item- the item to swap toinstant- true for a single-tick swap (e.g. throw pearl), false for multi-tick (e.g. hold bow)asymmetric- when true and the item has to be moved out of the deep inventory into a hotbar slot, it is left in the hotbar onendSwap(boolean)instead of being swapped back to its original slot. Only affects the container-swap (deep-inventory) path; hotbar items are unaffected.- Returns:
- true if the swap succeeded and the item is now in the main hand
-
beginSwap
Swaps the item at the given result slot into the main hand. -
beginSwap
Swaps the item at the given result slot into the main hand.- Parameters:
asymmetric- seebeginSwap(Item, boolean, boolean)
-
beginSilentHold
Multi-tick swap that never moves the CLIENT's selection: only the server's held slot changes (a bareSetCarriedItem), so the local hand keeps rendering — and the local player keeps using — whatever they actually hold. Released withendSwap(false).This is the swap for server-side computations that span a whole server tick and so cannot be covered by an instant begin/end pair — the delayed-destroy completion, whose progress
ServerPlayerGameMode.tick()recomputes asgetDestroyProgress(held) × (elapsed + 1)from the item held at that moment. Packet-triggered computations (a STOP handled inline) stay on instant swaps, which are already invisible.Deep-inventory items fall back to the ordinary multi-tick swap: the item genuinely has to move into the hotbar, which no amount of slot spoofing hides.
carriedIndexkeeps tracking what we actually sent, so vanilla'sensureHasSentCarriedItem()pulls the server back to the real slot the moment the player attacks or uses an item — the hit lands with the real weapon andonTick(me.hackware.api.event.impl.TickStartEvent)re-asserts the tool next tick. The hold costs at most a delayed completion tick, never a wrong-item interaction. Vanilla's unconditional per-tick re-sync is routed throughvanillaCarriedItemSync()so it doesn't undo the hold on every tick.- Returns:
- true if the tool is now held server-side
-
isSilentHoldActive
public boolean isSilentHoldActive()True while abeginSilentHold(me.hackware.api.util.InventoryUtil.FindItemResult)owns the server's slot without moving the client's. -
vanillaCarriedItemSync
public void vanillaCarriedItemSync()Stands in for vanilla's per-tickensureHasSentCarriedItem()(redirected inMultiPlayerGameModeMixin). A silent hold owns the server's slot for its whole duration, so the unconditional re-sync is skipped while one is active — otherwise vanilla would restore the real slot every single tick and the hold could never span a server tick. With no hold active this is vanilla's behaviour exactly: send the client's selection, and only when the server isn't already on it. -
endSwap
public void endSwap(boolean instant) Restores inventory state from a previousbeginSwap(net.minecraft.world.item.Item, boolean). -
beginSwapSlot
public boolean beginSwapSlot(int hotbarSlot, boolean instant) Swaps a specific hotbar slot into the main hand. Use when the caller already knows the target slot (e.g. AutoTool).- Parameters:
hotbarSlot- the hotbar slot (0-8)instant- true for sub-tick, false for multi-tick hold- Returns:
- true if the swap succeeded
-
beginOffhandSwap
public boolean beginOffhandSwap(int invSlot) Swaps an item from the given inventory slot into the offhand. Uses container SWAP with button 40 (offhand slot).- Parameters:
invSlot- the inventory slot index (0-35)- Returns:
- true if the swap was performed
-
endOffhandSwap
public void endOffhandSwap()Restores the offhand by swapping back. -
isOffhandSwapped
public boolean isOffhandSwapped()Returns true if an offhand swap is currently active. -
canSwap
public boolean canSwap(net.minecraft.world.item.Item item) Returns true if the given item can be swapped to under the current mode. -
getSlot
Returns the best slot for the given item under the current swap mode. -
wouldContainerSwap
Predicts whetherbeginSwap(FindItemResult, boolean)would move the item with a container SWAP click (didSilentSwap) rather than a slot change / spoof — WITHOUT performing any swap.Lets callers in a hot path (e.g. crystal placement) bail BEFORE issuing a container click. A container click goes through handleInventoryMouseClick, which the click-slot limiter can cancel mid begin/end pair under burst, stranding the held item in the source slot (the ghost item). Pre-checking avoids even the transient begin→bail→end churn.
-
isSilentSwapActive
public boolean isSilentSwapActive()Returns true if any silent swap (container or slot spoof) is currently active. -
withSwap
-
withSwap
Putstargetin the active hand, runsaction, and restores — a hotbar/offhand target through the budgeted carried-slot route, a main-inventory target through container clicks (which also work mid-use). Returns whether the action ran; an already-held target short-circuits with no swap spent. -
silentBudgetLeft
public int silentBudgetLeft(int priority) Carried-slot swaps still available this tick for a caller atpriority. -
isSwapInProgress
public boolean isSwapInProgress()Whether any begin/end swap window (instant or multi-tick, silent or not) is currently open. UnlikeisSilentSwapActive()this is also true for the plain selection-move path — the signal that an action happening right now runs with a weapon another module chose. -
isContainerSwapActive
public boolean isContainerSwapActive()Returns true only when a container swap is active (a slot-spoof swap sends a SetCarriedItem packet, not a container click, so it never collides with another container click stream / stateId). Safe to read off-thread. -
isInstantSwapContainerBased
public boolean isInstantSwapContainerBased()Returns true if the current instant swap used a container click (as opposed to hotbar slot spoofing). Callers that consume items (e.g. block placement, crystal placement) should bump the client stateId afterendSwap(boolean)when this returns true; the server'sbroadcastChangeswill send a count correction that increments the server stateId, and without a matching client bump the next container click would carry a stale id → full inventory resync → ghost items. -
isSlotLocked
public boolean isSlotLocked(int slotIndex) Returns true if the given inventory slot is currently locked by a silent swap. -
reset
public void reset()Clears all swap state. Called on disconnect so stale locks don't persist across server reconnects and block all future swaps.
-