Inventories!

This commit is contained in:
LOOHP 2022-12-09 16:41:59 +00:00
parent 949d2f34d7
commit 35c6b748c9
29 changed files with 1685 additions and 83 deletions

View File

@ -27,6 +27,7 @@ import com.loohp.limbo.commands.DefaultCommands;
import com.loohp.limbo.consolegui.GUI; import com.loohp.limbo.consolegui.GUI;
import com.loohp.limbo.events.EventsManager; import com.loohp.limbo.events.EventsManager;
import com.loohp.limbo.file.ServerProperties; import com.loohp.limbo.file.ServerProperties;
import com.loohp.limbo.inventory.AnvilInventory;
import com.loohp.limbo.inventory.CustomInventory; import com.loohp.limbo.inventory.CustomInventory;
import com.loohp.limbo.inventory.Inventory; import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.inventory.InventoryHolder; import com.loohp.limbo.inventory.InventoryHolder;
@ -626,7 +627,15 @@ public final class Limbo {
} }
public Inventory createInventory(Component title, InventoryType type, InventoryHolder holder) { public Inventory createInventory(Component title, InventoryType type, InventoryHolder holder) {
throw new UnsupportedOperationException("This function has not been implemented yet."); if (!type.isCreatable()) {
throw new UnsupportedOperationException("This InventoryType cannot be created.");
}
switch (type) {
case ANVIL:
return new AnvilInventory(title, holder);
default:
throw new UnsupportedOperationException("This InventoryType has not been implemented yet.");
}
} }
} }

View File

@ -0,0 +1,53 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.InventoryView;
public class AnvilRenameInputEvent extends InventoryEvent implements Cancellable {
private boolean cancelled;
private String input;
public AnvilRenameInputEvent(InventoryView inventoryView, String input) {
super(inventoryView, inventoryView.getTopInventory());
this.input = input;
this.cancelled = false;
}
public void setInput(String input) {
this.input = input;
}
public String getInput() {
return input;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
@Override
public boolean isCancelled() {
return cancelled;
}
}

View File

@ -20,16 +20,86 @@
package com.loohp.limbo.events.inventory; package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable; import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.Inventory; import com.loohp.limbo.inventory.ClickType;
import com.loohp.limbo.inventory.InventoryAction;
import com.loohp.limbo.inventory.InventoryType;
import com.loohp.limbo.inventory.InventoryView; import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.player.Player; import com.loohp.limbo.inventory.ItemStack;
public class InventoryClickEvent extends InventoryEvent implements Cancellable { public class InventoryClickEvent extends InventoryEvent implements Cancellable {
private boolean cancelled; private boolean cancelled;
private final ClickType click;
private final InventoryAction action;
private InventoryType.SlotType type;
private int whichSlot;
private int rawSlot;
private ItemStack current;
private int hotbarKey;
public InventoryClickEvent(Player player, InventoryView inventoryView, Inventory clickedInventory) { public InventoryClickEvent(InventoryView view, InventoryType.SlotType type, int rawSlot, ClickType click, InventoryAction action) {
super(player, inventoryView, clickedInventory); super(view, view.getInventory(rawSlot));
this.type = type;
this.rawSlot = rawSlot;
this.whichSlot = view.convertSlot(rawSlot);
this.click = click;
this.action = action;
this.current = null;
this.hotbarKey = -1;
this.cancelled = false;
}
public InventoryClickEvent(InventoryView view, InventoryType.SlotType type, int rawSlot, ClickType click, InventoryAction action, int hotbarKey) {
this(view, type, rawSlot, click, action);
this.hotbarKey = hotbarKey;
}
public ClickType getClick() {
return click;
}
public InventoryAction getAction() {
return action;
}
public InventoryType.SlotType getType() {
return type;
}
public int getWhichSlot() {
return whichSlot;
}
public int getRawSlot() {
return rawSlot;
}
public int getHotbarKey() {
return hotbarKey;
}
public ItemStack getCarriedItem() {
return getView().getCarriedItem();
}
@Deprecated
public void setCarriedItem(ItemStack stack) {
getView().setCarriedItem(stack);
}
public ItemStack getCurrentItem() {
if (type == InventoryType.SlotType.OUTSIDE) {
return current;
}
return getView().getItem(rawSlot);
}
public void setCurrentItem(ItemStack stack) {
if (type == InventoryType.SlotType.OUTSIDE) {
current = stack;
} else {
getView().setItem(rawSlot, stack);
}
} }
@Override @Override

View File

@ -20,12 +20,11 @@
package com.loohp.limbo.events.inventory; package com.loohp.limbo.events.inventory;
import com.loohp.limbo.inventory.InventoryView; import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.player.Player;
public class InventoryCloseEvent extends InventoryEvent { public class InventoryCloseEvent extends InventoryEvent {
public InventoryCloseEvent(Player player, InventoryView inventoryView) { public InventoryCloseEvent(InventoryView inventoryView) {
super(player, inventoryView, inventoryView.getTopInventory()); super(inventoryView, inventoryView.getTopInventory());
} }
} }

View File

@ -22,8 +22,6 @@ package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable; import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.InventoryView; import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.inventory.ItemStack; import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.player.Player;
import com.loohp.limbo.player.PlayerInventory;
public class InventoryCreativeEvent extends InventoryEvent implements Cancellable { public class InventoryCreativeEvent extends InventoryEvent implements Cancellable {
@ -31,8 +29,8 @@ public class InventoryCreativeEvent extends InventoryEvent implements Cancellabl
private final int slot; private final int slot;
private ItemStack newItem; private ItemStack newItem;
public InventoryCreativeEvent(Player player, InventoryView inventoryView, PlayerInventory playerInventory, int slot, ItemStack newItem) { public InventoryCreativeEvent(InventoryView inventoryView, int slot, ItemStack newItem) {
super(player, inventoryView, playerInventory); super(inventoryView, inventoryView.getBottomInventory());
this.slot = slot; this.slot = slot;
this.newItem = newItem; this.newItem = newItem;
this.cancelled = false; this.cancelled = false;

View File

@ -0,0 +1,97 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.DragType;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.inventory.ItemStack;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class InventoryDragEvent extends InventoryEvent implements Cancellable {
private boolean cancelled;
private final DragType type;
private final Map<Integer, ItemStack> addedItems;
private final Set<Integer> containerSlots;
private final ItemStack oldCarried;
private ItemStack newCarried;
public InventoryDragEvent(InventoryView view, ItemStack newCarried, ItemStack oldCarried, boolean right, Map<Integer, ItemStack> slots) {
super(view, view.getInventory(view.convertSlot(slots.keySet().iterator().next())));
this.type = right ? DragType.SINGLE : DragType.EVEN;
this.newCarried = newCarried;
this.oldCarried = oldCarried;
this.addedItems = Collections.unmodifiableMap(slots);
Set<Integer> containerSlots = new HashSet<>();
for (Integer slot : slots.keySet()) {
containerSlots.add(view.convertSlot(slot));
}
this.containerSlots = Collections.unmodifiableSet(containerSlots);
this.cancelled = false;
}
public Map<Integer, ItemStack> getNewItems() {
return addedItems;
}
public Set<Integer> getRawSlots() {
return addedItems.keySet();
}
public Set<Integer> getInventorySlots() {
return containerSlots;
}
public ItemStack get() {
return newCarried;
}
public ItemStack getCarriedItem() {
return newCarried;
}
public void setCarriedItem(ItemStack newCursor) {
this.newCarried = newCursor;
}
public ItemStack getOldCarriedItem() {
return oldCarried.clone();
}
public DragType getType() {
return type;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@ -26,21 +26,19 @@ import com.loohp.limbo.player.Player;
public class InventoryEvent extends Event { public class InventoryEvent extends Event {
private final Player player;
private final InventoryView inventoryView; private final InventoryView inventoryView;
private final Inventory clickedInventory; private final Inventory clickedInventory;
public InventoryEvent(Player player, InventoryView inventoryView, Inventory clickedInventory) { public InventoryEvent(InventoryView inventoryView, Inventory clickedInventory) {
this.player = player;
this.inventoryView = inventoryView; this.inventoryView = inventoryView;
this.clickedInventory = clickedInventory; this.clickedInventory = clickedInventory;
} }
public Player getPlayer() { public Player getPlayer() {
return player; return inventoryView.getPlayer();
} }
public InventoryView getInventoryView() { public InventoryView getView() {
return inventoryView; return inventoryView;
} }

View File

@ -21,14 +21,13 @@ package com.loohp.limbo.events.inventory;
import com.loohp.limbo.events.Cancellable; import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.InventoryView; import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.player.Player;
public class InventoryOpenEvent extends InventoryEvent implements Cancellable { public class InventoryOpenEvent extends InventoryEvent implements Cancellable {
private boolean cancelled; private boolean cancelled;
public InventoryOpenEvent(Player player, InventoryView inventoryView) { public InventoryOpenEvent(InventoryView inventoryView) {
super(player, inventoryView, inventoryView.getTopInventory()); super(inventoryView, inventoryView.getTopInventory());
this.cancelled = false; this.cancelled = false;
} }

View File

@ -24,22 +24,23 @@ import com.loohp.limbo.player.Player;
public class PlayerSelectedSlotChangeEvent extends PlayerEvent implements Cancellable { public class PlayerSelectedSlotChangeEvent extends PlayerEvent implements Cancellable {
private boolean cancel = false; private boolean cancelled;
private byte slot; private byte slot;
public PlayerSelectedSlotChangeEvent(Player player, byte slot) { public PlayerSelectedSlotChangeEvent(Player player, byte slot) {
super(player); super(player);
this.slot = slot; this.slot = slot;
this.cancelled = false;
} }
@Override @Override
public void setCancelled(boolean cancelled) { public void setCancelled(boolean cancelled) {
this.cancel = cancelled; this.cancelled = cancelled;
} }
@Override @Override
public boolean isCancelled() { public boolean isCancelled() {
return cancel; return cancelled;
} }
public byte getSlot() { public byte getSlot() {

View File

@ -0,0 +1,65 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.events.player;
import com.loohp.limbo.events.Cancellable;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.player.Player;
public class PlayerSwapHandItemsEvent extends PlayerEvent implements Cancellable {
private boolean cancelled;
private ItemStack mainHandItem;
private ItemStack offHandItem;
public PlayerSwapHandItemsEvent(Player player, ItemStack mainHandItem, ItemStack offHandItem) {
super(player);
this.mainHandItem = mainHandItem;
this.offHandItem = offHandItem;
this.cancelled = false;
}
public ItemStack getMainHandItem() {
return mainHandItem;
}
public void setMainHandItem(ItemStack mainHandItem) {
this.mainHandItem = mainHandItem;
}
public ItemStack getOffHandItem() {
return offHandItem;
}
public void setOffHandItem(ItemStack offHandItem) {
this.offHandItem = offHandItem;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
@Override
public boolean isCancelled() {
return cancelled;
}
}

View File

@ -19,6 +19,7 @@
package com.loohp.limbo.inventory; package com.loohp.limbo.inventory;
import com.loohp.limbo.location.Location;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot; import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowItems; import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowItems;
import com.loohp.limbo.player.Player; import com.loohp.limbo.player.Player;
@ -109,6 +110,11 @@ public abstract class AbstractInventory implements Inventory {
} }
} }
@Override
public Location getLocation() {
return inventoryHolder == null ? null : inventoryHolder.getLocation();
}
@Override @Override
public int getSize() { public int getSize() {
return inventory.length(); return inventory.length();
@ -311,7 +317,7 @@ public abstract class AbstractInventory implements Inventory {
public boolean contains(ItemStack item) { public boolean contains(ItemStack item) {
for (int i = 0; i < inventory.length(); i++) { for (int i = 0; i < inventory.length(); i++) {
ItemStack itemStack = getItem(i); ItemStack itemStack = getItem(i);
if (itemStack.equals(item)) { if (Objects.equals(itemStack, item)) {
return true; return true;
} }
} }
@ -525,6 +531,9 @@ public abstract class AbstractInventory implements Inventory {
@Deprecated @Deprecated
public void a(int index, ItemStack itemStack) { public void a(int index, ItemStack itemStack) {
if (itemStack != null && itemStack.type().equals(ItemStack.AIR.type())) {
itemStack = null;
}
inventory.inventory.set(index, itemStack); inventory.inventory.set(index, itemStack);
} }

View File

@ -0,0 +1,43 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.inventory;
import net.kyori.adventure.text.Component;
public class AnvilInventory extends AbstractInventory implements TitledInventory {
public static final Component DEFAULT_TITLE = Component.translatable("container.repair");
private Component title;
public AnvilInventory(Component title, InventoryHolder inventoryHolder) {
super(InventoryType.ANVIL.getDefaultSize(), inventoryHolder, InventoryType.ANVIL, null, null);
this.title = title == null ? DEFAULT_TITLE : title;
}
public void setTitle(Component title) {
this.title = title;
}
@Override
public Component getTitle() {
return title;
}
}

View File

@ -0,0 +1,119 @@
package com.loohp.limbo.inventory;
/**
* What the client did to trigger this action (not the result).
*/
public enum ClickType {
/**
* The left (or primary) mouse button.
*/
LEFT,
/**
* Holding shift while pressing the left mouse button.
*/
SHIFT_LEFT,
/**
* The right mouse button.
*/
RIGHT,
/**
* Holding shift while pressing the right mouse button.
*/
SHIFT_RIGHT,
/**
* Clicking the left mouse button on the grey area around the inventory.
*/
WINDOW_BORDER_LEFT,
/**
* Clicking the right mouse button on the grey area around the inventory.
*/
WINDOW_BORDER_RIGHT,
/**
* The middle mouse button, or a "scrollwheel click".
*/
MIDDLE,
/**
* One of the number keys 1-9, correspond to slots on the hotbar.
*/
NUMBER_KEY,
/**
* Pressing the left mouse button twice in quick succession.
*/
DOUBLE_CLICK,
/**
* The "Drop" key (defaults to Q).
*/
DROP,
/**
* Holding Ctrl while pressing the "Drop" key (defaults to Q).
*/
CONTROL_DROP,
/**
* Any action done with the Creative inventory open.
*/
CREATIVE,
/**
* The "swap item with offhand" key (defaults to F).
*/
SWAP_OFFHAND,
/**
* A type of inventory manipulation not yet recognized by Bukkit.
* <p>
* This is only for transitional purposes on a new Minecraft update, and
* should never be relied upon.
* <p>
* Any ClickType.UNKNOWN is called on a best-effort basis.
*/
UNKNOWN,
;
/**
* Gets whether this ClickType represents the pressing of a key on a
* keyboard.
*
* @return true if this ClickType represents the pressing of a key
*/
public boolean isKeyboardClick() {
return (this == ClickType.NUMBER_KEY) || (this == ClickType.DROP) || (this == ClickType.CONTROL_DROP);
}
/**
* Gets whether this ClickType represents an action that can only be
* performed by a Player in creative mode.
*
* @return true if this action requires Creative mode
*/
public boolean isCreativeAction() {
// Why use middle click?
return (this == ClickType.MIDDLE) || (this == ClickType.CREATIVE);
}
/**
* Gets whether this ClickType represents a right click.
*
* @return true if this ClickType represents a right click
*/
public boolean isRightClick() {
return (this == ClickType.RIGHT) || (this == ClickType.SHIFT_RIGHT);
}
/**
* Gets whether this ClickType represents a left click.
*
* @return true if this ClickType represents a left click
*/
public boolean isLeftClick() {
return (this == ClickType.LEFT) || (this == ClickType.SHIFT_LEFT) || (this == ClickType.DOUBLE_CLICK) || (this == ClickType.CREATIVE);
}
/**
* Gets whether this ClickType indicates that the shift key was pressed
* down when the click was made.
*
* @return true if the action uses Shift.
*/
public boolean isShiftClick() {
return (this == ClickType.SHIFT_LEFT) || (this == ClickType.SHIFT_RIGHT) || (this == ClickType.CONTROL_DROP);
}
}

View File

@ -49,9 +49,4 @@ public class CustomInventory extends AbstractInventory implements TitledInventor
this.title = title; this.title = title;
} }
@Override
public Location getLocation() {
return null;
}
} }

View File

@ -0,0 +1,17 @@
package com.loohp.limbo.inventory;
/**
* Represents the effect of a drag that will be applied to an Inventory in an
* InventoryDragEvent.
*/
public enum DragType {
/**
* One item from the cursor is placed in each selected slot.
*/
SINGLE,
/**
* The cursor is split evenly across all selected slots, not to exceed the
* Material's max stack size, with the remainder going to the cursor.
*/
EVEN,
}

View File

@ -0,0 +1,95 @@
package com.loohp.limbo.inventory;
/**
* An estimation of what the result will be.
*/
public enum InventoryAction {
/**
* Nothing will happen from the click.
* <p>
* There may be cases where nothing will happen and this is value is not
* provided, but it is guaranteed that this value is accurate when given.
*/
NOTHING,
/**
* All of the items on the clicked slot are moved to the cursor.
*/
PICKUP_ALL,
/**
* Some of the items on the clicked slot are moved to the cursor.
*/
PICKUP_SOME,
/**
* Half of the items on the clicked slot are moved to the cursor.
*/
PICKUP_HALF,
/**
* One of the items on the clicked slot are moved to the cursor.
*/
PICKUP_ONE,
/**
* All of the items on the cursor are moved to the clicked slot.
*/
PLACE_ALL,
/**
* Some of the items from the cursor are moved to the clicked slot
* (usually up to the max stack size).
*/
PLACE_SOME,
/**
* A single item from the cursor is moved to the clicked slot.
*/
PLACE_ONE,
/**
* The clicked item and the cursor are exchanged.
*/
SWAP_WITH_CURSOR,
/**
* The entire cursor item is dropped.
*/
DROP_ALL_CURSOR,
/**
* One item is dropped from the cursor.
*/
DROP_ONE_CURSOR,
/**
* The entire clicked slot is dropped.
*/
DROP_ALL_SLOT,
/**
* One item is dropped from the clicked slot.
*/
DROP_ONE_SLOT,
/**
* The item is moved to the opposite inventory if a space is found.
*/
MOVE_TO_OTHER_INVENTORY,
/**
* The clicked item is moved to the hotbar, and the item currently there
* is re-added to the player's inventory.
*
* The hotbar includes the player's off hand.
*/
HOTBAR_MOVE_AND_READD,
/**
* The clicked slot and the picked hotbar slot are swapped.
*
* The hotbar includes the player's off hand.
*/
HOTBAR_SWAP,
/**
* A max-size stack of the clicked item is put on the cursor.
*/
CLONE_STACK,
/**
* The inventory is searched for the same material, and they are put on
* the cursor up to {@link ItemStack#getMaxStackSize()}.
*/
COLLECT_TO_CURSOR,
/**
* An unrecognized ClickType.
*/
UNKNOWN,
;
}

View File

@ -19,10 +19,14 @@
package com.loohp.limbo.inventory; package com.loohp.limbo.inventory;
import com.loohp.limbo.location.Location;
public interface InventoryHolder { public interface InventoryHolder {
Inventory getInventory(); Inventory getInventory();
InventoryHolder getHolder(); InventoryHolder getHolder();
Location getLocation();
} }

View File

@ -19,8 +19,11 @@
package com.loohp.limbo.inventory; package com.loohp.limbo.inventory;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowData; import com.loohp.limbo.network.protocol.packets.PacketPlayOutWindowData;
import com.loohp.limbo.player.Player; import com.loohp.limbo.player.Player;
import com.loohp.limbo.player.PlayerInventory;
import net.kyori.adventure.text.Component;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -29,8 +32,10 @@ import java.util.concurrent.ConcurrentHashMap;
public class InventoryView { public class InventoryView {
public static final int OUTSIDE = -999;
private final Player player; private final Player player;
private final String title; private Component title;
private Inventory topInventory; private Inventory topInventory;
private final Inventory bottomInventory; private final Inventory bottomInventory;
private final Map<Property, Integer> properties; private final Map<Property, Integer> properties;
@ -38,7 +43,7 @@ public class InventoryView {
private final Unsafe unsafe; private final Unsafe unsafe;
public InventoryView(Player player, String title, Inventory topInventory, Inventory bottomInventory) { public InventoryView(Player player, Component title, Inventory topInventory, Inventory bottomInventory) {
this.player = player; this.player = player;
this.title = title; this.title = title;
this.topInventory = topInventory; this.topInventory = topInventory;
@ -58,14 +63,14 @@ public class InventoryView {
} }
public InventoryType getType() { public InventoryType getType() {
return topInventory.getType(); return topInventory == null ? bottomInventory.getType() : topInventory.getType();
} }
public Player getPlayer() { public Player getPlayer() {
return player; return player;
} }
public String getTitle() { public Component getTitle() {
return title; return title;
} }
@ -81,29 +86,237 @@ public class InventoryView {
return Collections.unmodifiableMap(properties); return Collections.unmodifiableMap(properties);
} }
/**
* Gets the inventory corresponding to the given raw slot ID.
*
* If the slot ID is {@link #OUTSIDE} null will be returned, otherwise
* behaviour for illegal and negative slot IDs is undefined.
*
* May be used with {@link #convertSlot(int)} to directly index an
* underlying inventory.
*
* @param rawSlot The raw slot ID.
* @return corresponding inventory, or null
*/
public Inventory getInventory(int rawSlot) {
// Slot may be -1 if not properly detected due to client bug
// e.g. dropping an item into part of the enchantment list section of an enchanting table
if (rawSlot == OUTSIDE || rawSlot == -1) {
return null;
}
if (rawSlot < 0) {
throw new IllegalArgumentException("Negative, non outside slot " + rawSlot);
}
if (rawSlot >= countSlots()) {
throw new IllegalArgumentException("Slot " + rawSlot + " greater than inventory slot count");
}
if (rawSlot < topInventory.getSize()) {
return getTopInventory();
} else {
return getBottomInventory();
}
}
/**
* Converts a raw slot ID into its local slot ID into whichever of the two
* inventories the slot points to.
* <p>
* If the raw slot refers to the upper inventory, it will be returned
* unchanged and thus be suitable for getTopInventory().getItem(); if it
* refers to the lower inventory, the output will differ from the input
* and be suitable for getBottomInventory().getItem().
*
* @param rawSlot The raw slot ID.
* @return The converted slot ID.
*/
public int convertSlot(int rawSlot) {
int numInTop = topInventory == null ? 0 : topInventory.getSize();
// Index from the top inventory as having slots from [0,size]
if (rawSlot < numInTop) {
return rawSlot;
}
// Move down the slot index by the top size
int slot = rawSlot - numInTop;
// Player crafting slots are indexed differently. The matrix is caught by the first return.
// Creative mode is the same, except that you can't see the crafting slots (but the IDs are still used)
if (getType() == InventoryType.CRAFTING || getType() == InventoryType.CREATIVE) {
/*
* Raw Slots:
*
* 5 1 2 0
* 6 3 4
* 7
* 8 45
* 9 10 11 12 13 14 15 16 17
* 18 19 20 21 22 23 24 25 26
* 27 28 29 30 31 32 33 34 35
* 36 37 38 39 40 41 42 43 44
*/
/*
* Converted Slots:
*
* 39 1 2 0
* 38 3 4
* 37
* 36 40
* 9 10 11 12 13 14 15 16 17
* 18 19 20 21 22 23 24 25 26
* 27 28 29 30 31 32 33 34 35
* 0 1 2 3 4 5 6 7 8
*/
if (slot < 4) {
// Send [5,8] to [39,36]
return 39 - slot;
} else if (slot > 39) {
// Slot lives in the extra slot section
return slot;
} else {
// Reset index so 9 -> 0
slot -= 4;
}
}
// 27 = 36 - 9
if (slot >= 27) {
// Put into hotbar section
slot -= 27;
} else {
// Take out of hotbar section
// 9 = 36 - 27
slot += 9;
}
return slot;
}
/**
* Determine the type of the slot by its raw slot ID.
* <p>
* If the type of the slot is unknown, then
* {@link InventoryType.SlotType#CONTAINER} will be returned.
*
* @param slot The raw slot ID
* @return the slot type
*/
public InventoryType.SlotType getSlotType(int slot) {
InventoryType.SlotType type = InventoryType.SlotType.CONTAINER;
if (topInventory != null && slot >= 0 && slot < topInventory.getSize()) {
switch (this.getType()) {
case BLAST_FURNACE:
case FURNACE:
case SMOKER:
if (slot == 2) {
type = InventoryType.SlotType.RESULT;
} else if (slot == 1) {
type = InventoryType.SlotType.FUEL;
} else {
type = InventoryType.SlotType.CRAFTING;
}
break;
case BREWING:
if (slot == 3) {
type = InventoryType.SlotType.FUEL;
} else {
type = InventoryType.SlotType.CRAFTING;
}
break;
case ENCHANTING:
case BEACON:
type = InventoryType.SlotType.CRAFTING;
break;
case WORKBENCH:
case CRAFTING:
if (slot == 0) {
type = InventoryType.SlotType.RESULT;
} else {
type = InventoryType.SlotType.CRAFTING;
}
break;
case ANVIL:
case SMITHING:
case CARTOGRAPHY:
case GRINDSTONE:
case MERCHANT:
if (slot == 2) {
type = InventoryType.SlotType.RESULT;
} else {
type = InventoryType.SlotType.CRAFTING;
}
break;
case STONECUTTER:
if (slot == 1) {
type = InventoryType.SlotType.RESULT;
} else {
type = InventoryType.SlotType.CRAFTING;
}
break;
case LOOM:
if (slot == 3) {
type = InventoryType.SlotType.RESULT;
} else {
type = InventoryType.SlotType.CRAFTING;
}
break;
default:
// Nothing to do, it's a CONTAINER slot
}
} else {
if (slot < 0) {
type = InventoryType.SlotType.OUTSIDE;
} else if (this.getType() == InventoryType.CRAFTING) { // Also includes creative inventory
if (slot < 9) {
type = InventoryType.SlotType.ARMOR;
} else if (slot > 35) {
type = InventoryType.SlotType.QUICKBAR;
}
} else if (slot >= (this.countSlots() - (9 + 4 + 1))) { // Quickbar, Armor, Offhand
type = InventoryType.SlotType.QUICKBAR;
}
}
return type;
}
/**
* Check the total number of slots in this view, combining the upper and
* lower inventories.
* <p>
* Note though that it's possible for this to be greater than the sum of
* the two inventories if for example some slots are not being used.
*
* @return The total size
*/
public int countSlots() { public int countSlots() {
return topInventory.getSize() + bottomInventory.getSize(); return (topInventory == null ? 0 : topInventory.getSize()) + bottomInventory.getSize();
}
public void close() {
player.closeInventory();
}
public boolean isSlot(int index) {
if (topInventory != null) {
if (index < topInventory.getSize()) {
return true;
}
index -= topInventory.getSize();
}
if (bottomInventory instanceof PlayerInventory) {
return index < 36;
}
return index < bottomInventory.getSize();
} }
public ItemStack getItem(int index) { public ItemStack getItem(int index) {
if (topInventory != null) { return getInventory(index).getItem(convertSlot(index));
if (index < topInventory.getSize()) {
return topInventory.getItem(topInventory.getUnsafe().b().applyAsInt(index));
}
index -= topInventory.getSize();
}
return bottomInventory.getItem(bottomInventory.getUnsafe().b().applyAsInt(index));
} }
public void setItem(int index, ItemStack itemStack) { public void setItem(int index, ItemStack itemStack) {
if (topInventory != null) { getInventory(index).setItem(convertSlot(index), itemStack);
if (index < topInventory.getSize()) {
topInventory.setItem(topInventory.getUnsafe().b().applyAsInt(index), itemStack);
return;
}
index -= topInventory.getSize();
}
bottomInventory.setItem(bottomInventory.getUnsafe().b().applyAsInt(index), itemStack);
} }
public void setProperty(InventoryView.Property prop, int value) { public void setProperty(InventoryView.Property prop, int value) {
@ -121,6 +334,18 @@ public class InventoryView {
} }
} }
public void updateView() {
if (topInventory != null) {
topInventory.updateInventory(player);
}
bottomInventory.updateInventory(player);
try {
player.clientConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, 0, carriedItem));
} catch (IOException e) {
e.printStackTrace();
}
}
@SuppressWarnings("DeprecatedIsStillUsed") @SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated @Deprecated
public Unsafe getUnsafe() { public Unsafe getUnsafe() {
@ -139,10 +364,20 @@ public class InventoryView {
} }
@Deprecated @Deprecated
public void a(Inventory topInventory) { public void a(Inventory topInventory, Component title) {
inventoryView.topInventory = topInventory; inventoryView.topInventory = topInventory;
inventoryView.title = title;
inventoryView.properties.clear(); inventoryView.properties.clear();
} }
@Deprecated
public int a() {
if (inventoryView.topInventory != null) {
return inventoryView.topInventory.getUnsafe().c().getOrDefault(inventoryView.player, -1);
}
return inventoryView.bottomInventory.getUnsafe().c().getOrDefault(inventoryView.player, -1);
}
} }
/** /**

View File

@ -20,8 +20,12 @@
package com.loohp.limbo.inventory; package com.loohp.limbo.inventory;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.querz.nbt.io.SNBTUtil;
import net.querz.nbt.tag.CompoundTag; import net.querz.nbt.tag.CompoundTag;
import java.io.IOException;
import java.util.Objects; import java.util.Objects;
public class ItemStack implements Cloneable { public class ItemStack implements Cloneable {
@ -86,6 +90,43 @@ public class ItemStack implements Cloneable {
return new ItemStack(material, amount, nbt == null ? null : nbt.clone()); return new ItemStack(material, amount, nbt == null ? null : nbt.clone());
} }
public Component displayName() {
if (type().equals(AIR.type()) || nbt == null) {
return null;
}
CompoundTag displayTag = nbt.getCompoundTag("display");
if (displayTag == null) {
return null;
}
String json = displayTag.getString("Name");
if (json == null) {
return null;
}
try {
return GsonComponentSerializer.gson().deserialize(json);
} catch (Exception e) {
return null;
}
}
public ItemStack displayName(Component component) {
if (type().equals(AIR.type())) {
return this;
}
try {
String json = GsonComponentSerializer.gson().serialize(component);
CompoundTag nbt = this.nbt.clone();
CompoundTag displayTag = nbt.getCompoundTag("display");
if (displayTag == null) {
nbt.put("display", displayTag = new CompoundTag());
}
displayTag.putString("Name", json);
return nbt(nbt);
} catch (Exception ignore) {
}
return this;
}
public int getMaxStackSize() { public int getMaxStackSize() {
return 64; return 64;
} }
@ -112,11 +153,21 @@ public class ItemStack implements Cloneable {
} }
public boolean isSimilar(ItemStack stack) { public boolean isSimilar(ItemStack stack) {
return material.equals(stack.material) && Objects.equals(nbt, stack.nbt); return stack != null && material.equals(stack.material) && Objects.equals(nbt, stack.nbt);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(material, amount, nbt); return Objects.hash(material, amount, nbt);
} }
@Override
public String toString() {
try {
return SNBTUtil.toSNBT(getFullTag());
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
} }

View File

@ -20,6 +20,8 @@
package com.loohp.limbo.network; package com.loohp.limbo.network;
import com.loohp.limbo.Limbo; import com.loohp.limbo.Limbo;
import com.loohp.limbo.entity.EntityEquipment;
import com.loohp.limbo.events.inventory.AnvilRenameInputEvent;
import com.loohp.limbo.events.inventory.InventoryCloseEvent; import com.loohp.limbo.events.inventory.InventoryCloseEvent;
import com.loohp.limbo.events.inventory.InventoryCreativeEvent; import com.loohp.limbo.events.inventory.InventoryCreativeEvent;
import com.loohp.limbo.events.player.PlayerInteractEvent; import com.loohp.limbo.events.player.PlayerInteractEvent;
@ -30,10 +32,13 @@ import com.loohp.limbo.events.player.PlayerQuitEvent;
import com.loohp.limbo.events.player.PlayerResourcePackStatusEvent; import com.loohp.limbo.events.player.PlayerResourcePackStatusEvent;
import com.loohp.limbo.events.player.PlayerSelectedSlotChangeEvent; import com.loohp.limbo.events.player.PlayerSelectedSlotChangeEvent;
import com.loohp.limbo.events.player.PlayerSpawnEvent; import com.loohp.limbo.events.player.PlayerSpawnEvent;
import com.loohp.limbo.events.player.PlayerSwapHandItemsEvent;
import com.loohp.limbo.events.player.PluginMessageEvent; import com.loohp.limbo.events.player.PluginMessageEvent;
import com.loohp.limbo.events.status.StatusPingEvent; import com.loohp.limbo.events.status.StatusPingEvent;
import com.loohp.limbo.file.ServerProperties; import com.loohp.limbo.file.ServerProperties;
import com.loohp.limbo.inventory.AnvilInventory;
import com.loohp.limbo.inventory.Inventory; import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.location.Location; import com.loohp.limbo.location.Location;
import com.loohp.limbo.network.protocol.packets.Packet; import com.loohp.limbo.network.protocol.packets.Packet;
import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn; import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn;
@ -44,11 +49,14 @@ import com.loohp.limbo.network.protocol.packets.PacketLoginOutDisconnect;
import com.loohp.limbo.network.protocol.packets.PacketLoginOutLoginSuccess; import com.loohp.limbo.network.protocol.packets.PacketLoginOutLoginSuccess;
import com.loohp.limbo.network.protocol.packets.PacketLoginOutPluginMessaging; import com.loohp.limbo.network.protocol.packets.PacketLoginOutPluginMessaging;
import com.loohp.limbo.network.protocol.packets.PacketOut; import com.loohp.limbo.network.protocol.packets.PacketOut;
import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockDig;
import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockPlace; import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockPlace;
import com.loohp.limbo.network.protocol.packets.PacketPlayInChat; import com.loohp.limbo.network.protocol.packets.PacketPlayInChat;
import com.loohp.limbo.network.protocol.packets.PacketPlayInCloseWindow; import com.loohp.limbo.network.protocol.packets.PacketPlayInCloseWindow;
import com.loohp.limbo.network.protocol.packets.PacketPlayInHeldItemChange; import com.loohp.limbo.network.protocol.packets.PacketPlayInHeldItemChange;
import com.loohp.limbo.network.protocol.packets.PacketPlayInItemName;
import com.loohp.limbo.network.protocol.packets.PacketPlayInKeepAlive; import com.loohp.limbo.network.protocol.packets.PacketPlayInKeepAlive;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPickItem;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPluginMessaging; import com.loohp.limbo.network.protocol.packets.PacketPlayInPluginMessaging;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPosition; import com.loohp.limbo.network.protocol.packets.PacketPlayInPosition;
import com.loohp.limbo.network.protocol.packets.PacketPlayInPositionAndLook; import com.loohp.limbo.network.protocol.packets.PacketPlayInPositionAndLook;
@ -85,6 +93,7 @@ import com.loohp.limbo.network.protocol.packets.PacketStatusOutResponse;
import com.loohp.limbo.network.protocol.packets.ServerboundChatCommandPacket; import com.loohp.limbo.network.protocol.packets.ServerboundChatCommandPacket;
import com.loohp.limbo.player.Player; import com.loohp.limbo.player.Player;
import com.loohp.limbo.player.PlayerInteractManager; import com.loohp.limbo.player.PlayerInteractManager;
import com.loohp.limbo.player.PlayerInventory;
import com.loohp.limbo.utils.BungeecordAdventureConversionUtils; import com.loohp.limbo.utils.BungeecordAdventureConversionUtils;
import com.loohp.limbo.utils.CheckedBiConsumer; import com.loohp.limbo.utils.CheckedBiConsumer;
import com.loohp.limbo.utils.CustomStringUtils; import com.loohp.limbo.utils.CustomStringUtils;
@ -92,6 +101,7 @@ import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.DeclareCommands; import com.loohp.limbo.utils.DeclareCommands;
import com.loohp.limbo.utils.ForwardingUtils; import com.loohp.limbo.utils.ForwardingUtils;
import com.loohp.limbo.utils.GameMode; import com.loohp.limbo.utils.GameMode;
import com.loohp.limbo.utils.InventoryClickUtils;
import com.loohp.limbo.utils.MojangAPIUtils; import com.loohp.limbo.utils.MojangAPIUtils;
import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse; import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse;
import com.loohp.limbo.world.BlockPosition; import com.loohp.limbo.world.BlockPosition;
@ -99,6 +109,7 @@ import com.loohp.limbo.world.BlockState;
import com.loohp.limbo.world.World; import com.loohp.limbo.world.World;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
@ -124,7 +135,6 @@ import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
@ -668,9 +678,9 @@ public class ClientConnection extends Thread {
sendPacket(response); sendPacket(response);
} }
}; };
System.out.println("Waiting");
PacketIn packetIn = channel.readPacket(); PacketIn packetIn = channel.readPacket();
System.out.println("Received " + packetIn.getClass());
if (packetIn instanceof PacketPlayInPositionAndLook) { if (packetIn instanceof PacketPlayInPositionAndLook) {
PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn; PacketPlayInPositionAndLook pos = (PacketPlayInPositionAndLook) packetIn;
Location from = player.getLocation(); Location from = player.getLocation();
@ -757,33 +767,80 @@ public class ClientConnection extends Thread {
Limbo.getInstance().getEventsManager().callEvent(new PlayerInteractEvent(player, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, player.getEquipment().getItem(packet.getHand()), block, packet.getBlockHit().getDirection(), packet.getHand())); Limbo.getInstance().getEventsManager().callEvent(new PlayerInteractEvent(player, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, player.getEquipment().getItem(packet.getHand()), block, packet.getBlockHit().getDirection(), packet.getHand()));
} else if (packetIn instanceof PacketPlayInSetCreativeSlot) { } else if (packetIn instanceof PacketPlayInSetCreativeSlot) {
PacketPlayInSetCreativeSlot packet = (PacketPlayInSetCreativeSlot) packetIn; PacketPlayInSetCreativeSlot packet = (PacketPlayInSetCreativeSlot) packetIn;
InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player, player.getInventoryView(), player.getInventory(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack())); InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player.getInventoryView(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack()));
if (event.isCancelled()) { if (event.isCancelled()) {
player.updateInventory(); player.updateInventory();
} else { } else {
player.getInventory().getUnsafe().b(packet.getSlotNumber(), event.getNewItem()); player.getInventory().setItem(event.getSlot(), event.getNewItem());
} }
} else if (packetIn instanceof PacketPlayInWindowClick) { } else if (packetIn instanceof PacketPlayInWindowClick) {
PacketPlayInWindowClick packet = (PacketPlayInWindowClick) packetIn; PacketPlayInWindowClick packet = (PacketPlayInWindowClick) packetIn;
/* try {
InventoryCreativeEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryCreativeEvent(player, player.getInventoryView(), player.getInventory(), player.getInventory().getUnsafe().b().applyAsInt(packet.getSlotNumber()), packet.getItemStack())); InventoryClickUtils.handle(player, packet);
if (event.isCancelled()) { } catch (Throwable e) {
player.updateInventory(); e.printStackTrace();
} else {
player.getInventory().getUnsafe().b(packet.getSlotNumber(), event.getNewItem());
} }
*/
} else if (packetIn instanceof PacketPlayInCloseWindow) { } else if (packetIn instanceof PacketPlayInCloseWindow) {
PacketPlayInCloseWindow packet = (PacketPlayInCloseWindow) packetIn; PacketPlayInCloseWindow packet = (PacketPlayInCloseWindow) packetIn;
Inventory inventory = player.getInventoryView().getTopInventory(); Inventory inventory = player.getInventoryView().getTopInventory();
if (inventory != null) { if (inventory != null) {
Integer id = inventory.getUnsafe().c().get(player); Integer id = inventory.getUnsafe().c().get(player);
if (id != null) { if (id != null) {
Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(player, player.getInventoryView())); Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(player.getInventoryView()));
player.getInventoryView().getUnsafe().a(null); player.getInventoryView().getUnsafe().a(null, null);
inventory.getUnsafe().c().remove(player); inventory.getUnsafe().c().remove(player);
} }
} }
} else if (packetIn instanceof PacketPlayInBlockDig) {
PacketPlayInBlockDig packet = (PacketPlayInBlockDig) packetIn;
//noinspection SwitchStatementWithTooFewBranches
switch (packet.getAction()) {
case SWAP_ITEM_WITH_OFFHAND: {
EntityEquipment equipment = player.getEquipment();
PlayerSwapHandItemsEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerSwapHandItemsEvent(player, equipment.getItemInOffHand(), equipment.getItemInMainHand()));
if (!event.isCancelled()) {
equipment.setItemInMainHand(event.getMainHandItem());
equipment.setItemInOffHand(event.getOffHandItem());
}
break;
}
}
} else if (packetIn instanceof PacketPlayInPickItem) {
PacketPlayInPickItem packet = (PacketPlayInPickItem) packetIn;
PlayerInventory inventory = player.getInventory();
int slot = inventory.getUnsafe().b().applyAsInt(packet.getSlot());
int i = player.getSelectedSlot();
byte selectedSlot = -1;
boolean firstRun = true;
while (selectedSlot < 0 || (!firstRun && i == player.getSelectedSlot())) {
ItemStack itemStack = inventory.getItem(i);
if (itemStack == null) {
selectedSlot = (byte) i;
break;
}
if (++i >= 9) {
i = 0;
}
}
if (selectedSlot < 0) {
selectedSlot = player.getSelectedSlot();
}
ItemStack leavingHotbar = inventory.getItem(selectedSlot);
inventory.setItem(selectedSlot, inventory.getItem(slot));
inventory.setItem(slot, leavingHotbar);
player.setSelectedSlot(selectedSlot);
} else if (packetIn instanceof PacketPlayInItemName) {
PacketPlayInItemName packet = (PacketPlayInItemName) packetIn;
if (player.getInventoryView().getTopInventory() instanceof AnvilInventory) {
AnvilRenameInputEvent event = Limbo.getInstance().getEventsManager().callEvent(new AnvilRenameInputEvent(player.getInventoryView(), packet.getName()));
if (!event.isCancelled()) {
AnvilInventory anvilInventory = (AnvilInventory) player.getInventoryView().getTopInventory();
ItemStack result = anvilInventory.getItem(2);
if (result != null) {
result.displayName(LegacyComponentSerializer.legacySection().deserialize(event.getInput()));
}
}
}
} }
} catch (Exception e) { } catch (Exception e) {
break; break;

View File

@ -0,0 +1,74 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.location.BlockFace;
import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.world.BlockPosition;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInBlockDig extends PacketIn {
public enum PlayerDigType {
START_DESTROY_BLOCK,
ABORT_DESTROY_BLOCK,
STOP_DESTROY_BLOCK,
DROP_ALL_ITEMS,
DROP_ITEM,
RELEASE_USE_ITEM,
SWAP_ITEM_WITH_OFFHAND;
}
private PlayerDigType action;
private BlockPosition pos;
private BlockFace direction;
private int sequence;
public PacketPlayInBlockDig(PlayerDigType action, BlockPosition pos, BlockFace direction, int sequence) {
this.action = action;
this.pos = pos;
this.direction = direction;
this.sequence = sequence;
}
public PacketPlayInBlockDig(DataInputStream in) throws IOException {
this(PlayerDigType.values()[DataTypeIO.readVarInt(in)], DataTypeIO.readBlockPosition(in), BlockFace.values()[in.readByte()], DataTypeIO.readVarInt(in));
}
public BlockPosition getPos() {
return pos;
}
public BlockFace getDirection() {
return direction;
}
public PlayerDigType getAction() {
return action;
}
public int getSequence() {
return sequence;
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayInItemName extends PacketIn {
private String name;
public PacketPlayInItemName(String name) {
this.name = name;
}
public PacketPlayInItemName(DataInputStream in) throws IOException {
this(DataTypeIO.readString(in, StandardCharsets.UTF_8));
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInPickItem extends PacketIn {
private int slot;
public PacketPlayInPickItem(int slot) {
this.slot = slot;
}
public PacketPlayInPickItem(DataInputStream in) throws IOException {
this(DataTypeIO.readVarInt(in));
}
public int getSlot() {
return slot;
}
}

View File

@ -31,7 +31,7 @@ public class PacketPlayOutPlayerAbilities extends PacketOut {
ALLOW_FLYING(0x04), ALLOW_FLYING(0x04),
CREATIVE(0x08); CREATIVE(0x08);
int bitvalue; private final int bitvalue;
PlayerAbilityFlags(int bitvalue) { PlayerAbilityFlags(int bitvalue) {
this.bitvalue = bitvalue; this.bitvalue = bitvalue;

View File

@ -38,7 +38,7 @@ public class PacketPlayOutSetSlot extends PacketOut {
this.containerId = containerId; this.containerId = containerId;
this.stateId = stateId; this.stateId = stateId;
this.slot = slot; this.slot = slot;
this.itemStack = itemStack; this.itemStack = itemStack == null ? ItemStack.AIR : itemStack;
} }
public int getContainerId() { public int getContainerId() {

View File

@ -132,8 +132,9 @@ public class Player extends LivingEntity implements CommandSender, InventoryHold
} }
public void setSelectedSlot(byte slot) { public void setSelectedSlot(byte slot) {
if(slot == selectedSlot) if (slot == selectedSlot) {
return; return;
}
try { try {
PacketPlayOutHeldItemChange state = new PacketPlayOutHeldItemChange(slot); PacketPlayOutHeldItemChange state = new PacketPlayOutHeldItemChange(slot);
clientConnection.sendPacket(state); clientConnection.sendPacket(state);
@ -603,22 +604,22 @@ public class Player extends LivingEntity implements CommandSender, InventoryHold
} }
public void openInventory(Inventory inventory) { public void openInventory(Inventory inventory) {
inventoryView.getUnsafe().a(inventory); Component title = inventory instanceof TitledInventory ? ((TitledInventory) inventory).getTitle() : Component.translatable("container.chest");
inventoryView.getUnsafe().a(inventory, title);
int id = nextContainerId(); int id = nextContainerId();
inventory.getUnsafe().c().put(this, id); inventory.getUnsafe().c().put(this, id);
InventoryOpenEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryOpenEvent(this, inventoryView)); InventoryOpenEvent event = Limbo.getInstance().getEventsManager().callEvent(new InventoryOpenEvent(inventoryView));
if (event.isCancelled()) { if (event.isCancelled()) {
inventoryView.getUnsafe().a(null); inventoryView.getUnsafe().a(null, null);
inventory.getUnsafe().c().remove(this); inventory.getUnsafe().c().remove(this);
} else { } else {
Component title = inventory instanceof TitledInventory ? ((TitledInventory) inventory).getTitle() : Component.translatable("container.chest");
PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, inventory.getType().getRawType(inventory.getSize()), title); PacketPlayOutOpenWindow packet = new PacketPlayOutOpenWindow(id, inventory.getType().getRawType(inventory.getSize()), title);
try { try {
clientConnection.sendPacket(packet); clientConnection.sendPacket(packet);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
inventory.updateInventory(this); inventoryView.updateView();
} }
} }
@ -627,8 +628,8 @@ public class Player extends LivingEntity implements CommandSender, InventoryHold
if (inventory != null) { if (inventory != null) {
Integer id = inventory.getUnsafe().c().get(this); Integer id = inventory.getUnsafe().c().get(this);
if (id != null) { if (id != null) {
Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(this, inventoryView)); Limbo.getInstance().getEventsManager().callEvent(new InventoryCloseEvent(inventoryView));
inventoryView.getUnsafe().a(null); inventoryView.getUnsafe().a(null, null);
inventory.getUnsafe().c().remove(this); inventory.getUnsafe().c().remove(this);
PacketPlayOutCloseWindow packet = new PacketPlayOutCloseWindow(id); PacketPlayOutCloseWindow packet = new PacketPlayOutCloseWindow(id);
try { try {

View File

@ -27,7 +27,6 @@ import com.loohp.limbo.inventory.AbstractInventory;
import com.loohp.limbo.inventory.EquipmentSlot; import com.loohp.limbo.inventory.EquipmentSlot;
import com.loohp.limbo.inventory.InventoryType; import com.loohp.limbo.inventory.InventoryType;
import com.loohp.limbo.inventory.ItemStack; import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.location.Location;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -165,9 +164,4 @@ public class PlayerInventory extends AbstractInventory implements EntityEquipmen
} }
} }
@Override
public Location getLocation() {
return player.getLocation();
}
} }

View File

@ -0,0 +1,531 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.utils;
import com.loohp.limbo.Limbo;
import com.loohp.limbo.events.inventory.InventoryClickEvent;
import com.loohp.limbo.events.inventory.InventoryDragEvent;
import com.loohp.limbo.inventory.ClickType;
import com.loohp.limbo.inventory.Inventory;
import com.loohp.limbo.inventory.InventoryAction;
import com.loohp.limbo.inventory.InventoryClickType;
import com.loohp.limbo.inventory.InventoryType;
import com.loohp.limbo.inventory.InventoryView;
import com.loohp.limbo.inventory.ItemStack;
import com.loohp.limbo.network.protocol.packets.PacketPlayInWindowClick;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutSetSlot;
import com.loohp.limbo.player.Player;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
public class InventoryClickUtils {
private static final Map<Player, QuickCraftInfo> QUICK_CRAFT_INFO = Collections.synchronizedMap(new WeakHashMap<>());
public static synchronized void handle(Player player, PacketPlayInWindowClick packetplayinwindowclick) {
InventoryClickEvent event;
InventoryView inventory = player.getInventoryView();
InventoryType.SlotType type = inventory.getSlotType(packetplayinwindowclick.getSlotNum());
int rawSlot = packetplayinwindowclick.getSlotNum();
boolean cancelled = player.getGamemode().equals(GameMode.SPECTATOR);
ClickType click = ClickType.UNKNOWN;
InventoryAction action = InventoryAction.UNKNOWN;
ItemStack itemstack = null;
switch (packetplayinwindowclick.getClickType()) {
case PICKUP:
if (packetplayinwindowclick.getButtonNum() == 0) {
click = ClickType.LEFT;
} else if (packetplayinwindowclick.getButtonNum() == 1) {
click = ClickType.RIGHT;
}
if (packetplayinwindowclick.getButtonNum() == 0 || packetplayinwindowclick.getButtonNum() == 1) {
action = InventoryAction.NOTHING; // Don't want to repeat ourselves
if (packetplayinwindowclick.getSlotNum() == -999) {
if (inventory.getCarriedItem() != null) {
action = packetplayinwindowclick.getButtonNum() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR;
}
} else if (packetplayinwindowclick.getSlotNum() < 0) {
action = InventoryAction.NOTHING;
} else {
ItemStack clickedItem = inventory.getItem(rawSlot);
if (inventory.isSlot(rawSlot)) {
ItemStack cursor = inventory.getCarriedItem();
if (clickedItem == null) {
if (cursor != null) {
action = packetplayinwindowclick.getButtonNum() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE;
}
} else {
if (cursor == null) {
action = packetplayinwindowclick.getButtonNum() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF;
} else {
if (clickedItem.isSimilar(cursor)) {
int toPlace = packetplayinwindowclick.getButtonNum() == 0 ? cursor.amount() : 1;
toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.amount());
toPlace = Math.min(toPlace, cursor.getMaxStackSize() - clickedItem.amount());
if (toPlace == 1) {
action = InventoryAction.PLACE_ONE;
} else if (toPlace == cursor.amount()) {
action = InventoryAction.PLACE_ALL;
} else if (toPlace < 0) {
action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks
} else if (toPlace != 0) {
action = InventoryAction.PLACE_SOME;
}
} else if (cursor.amount() <= cursor.getMaxStackSize()) {
action = InventoryAction.SWAP_WITH_CURSOR;
}
}
}
}
}
}
break;
case QUICK_MOVE:
if (packetplayinwindowclick.getButtonNum() == 0) {
click = ClickType.SHIFT_LEFT;
} else if (packetplayinwindowclick.getButtonNum() == 1) {
click = ClickType.SHIFT_RIGHT;
}
if (packetplayinwindowclick.getButtonNum() == 0 || packetplayinwindowclick.getButtonNum() == 1) {
if (packetplayinwindowclick.getSlotNum() < 0) {
action = InventoryAction.NOTHING;
} else {
ItemStack slot = inventory.getItem(rawSlot);
if (inventory.isSlot(rawSlot) && slot != null) {
action = InventoryAction.MOVE_TO_OTHER_INVENTORY;
} else {
action = InventoryAction.NOTHING;
}
}
}
break;
case SWAP:
if ((packetplayinwindowclick.getButtonNum() >= 0 && packetplayinwindowclick.getButtonNum() < 9) || packetplayinwindowclick.getButtonNum() == 40) {
click = (packetplayinwindowclick.getButtonNum() == 40) ? ClickType.SWAP_OFFHAND : ClickType.NUMBER_KEY;
ItemStack clickedSlot = inventory.getItem(rawSlot);
ItemStack hotbar = inventory.getPlayer().getInventory().getItem(packetplayinwindowclick.getButtonNum());
boolean canCleanSwap = hotbar == null || inventory.getInventory(rawSlot).equals(inventory.getPlayer().getInventory()); // the slot will accept the hotbar item
if (clickedSlot != null) {
if (canCleanSwap) {
action = InventoryAction.HOTBAR_SWAP;
} else {
action = InventoryAction.HOTBAR_MOVE_AND_READD;
}
} else if (clickedSlot == null && hotbar != null) {
action = InventoryAction.HOTBAR_SWAP;
} else {
action = InventoryAction.NOTHING;
}
}
break;
case CLONE:
if (packetplayinwindowclick.getButtonNum() == 2) {
click = ClickType.MIDDLE;
if (packetplayinwindowclick.getSlotNum() < 0) {
action = InventoryAction.NOTHING;
} else {
ItemStack slot = inventory.getItem(rawSlot);
if (inventory.isSlot(rawSlot) && slot != null && player.getGamemode().equals(GameMode.CREATIVE) && inventory.getCarriedItem() == null) {
action = InventoryAction.CLONE_STACK;
} else {
action = InventoryAction.NOTHING;
}
}
} else {
click = ClickType.UNKNOWN;
action = InventoryAction.UNKNOWN;
}
break;
case THROW:
if (packetplayinwindowclick.getSlotNum() >= 0) {
if (packetplayinwindowclick.getButtonNum() == 0) {
click = ClickType.DROP;
ItemStack slot = inventory.getItem(rawSlot);
if (inventory.isSlot(rawSlot) && slot != null && !slot.type().equals(ItemStack.AIR.type())) {
action = InventoryAction.DROP_ONE_SLOT;
} else {
action = InventoryAction.NOTHING;
}
} else if (packetplayinwindowclick.getButtonNum() == 1) {
click = ClickType.CONTROL_DROP;
ItemStack slot = inventory.getItem(rawSlot);
if (inventory.isSlot(rawSlot) && slot != null && !slot.type().equals(ItemStack.AIR.type())) {
action = InventoryAction.DROP_ALL_SLOT;
} else {
action = InventoryAction.NOTHING;
}
}
} else {
// Sane default (because this happens when they are holding nothing. Don't ask why.)
click = ClickType.LEFT;
if (packetplayinwindowclick.getButtonNum() == 1) {
click = ClickType.RIGHT;
}
action = InventoryAction.NOTHING;
}
break;
case PICKUP_ALL:
click = ClickType.DOUBLE_CLICK;
action = InventoryAction.NOTHING;
if (packetplayinwindowclick.getSlotNum() >= 0 && inventory.getCarriedItem() != null) {
ItemStack cursor = inventory.getCarriedItem();
int amount = cursor == null ? 0 : cursor.amount();
action = InventoryAction.NOTHING;
// Quick check for if we have any of the item
if ((inventory.getTopInventory() != null && inventory.getTopInventory().containsAtLeast(cursor, 1)) || inventory.getBottomInventory().containsAtLeast(cursor, 1)) {
action = InventoryAction.COLLECT_TO_CURSOR;
}
}
break;
case QUICK_CRAFT: {
QuickCraftInfo quickCraft;
synchronized (QUICK_CRAFT_INFO) {
quickCraft = QUICK_CRAFT_INFO.get(player);
if (quickCraft == null) {
QUICK_CRAFT_INFO.put(player, quickCraft = new QuickCraftInfo());
}
}
int slotNum = packetplayinwindowclick.getSlotNum();
int buttonNum = packetplayinwindowclick.getButtonNum();
int quickcraftStatus = quickCraft.quickcraftStatus;
ItemStack itemstack1;
int l;
quickCraft.quickcraftStatus = getQuickcraftHeader(buttonNum);
if ((quickcraftStatus != 1 || quickCraft.quickcraftStatus != 2) && quickcraftStatus != quickCraft.quickcraftStatus) {
quickCraft.resetQuickCraft();
} else if (inventory.getCarriedItem() == null) {
quickCraft.resetQuickCraft();
} else if (quickCraft.quickcraftStatus == 0) {
quickCraft.quickcraftType = getQuickcraftType(buttonNum);
if (isValidQuickcraftType(quickCraft.quickcraftType, player)) {
quickCraft.quickcraftStatus = 1;
quickCraft.quickcraftSlots.clear();
} else {
quickCraft.resetQuickCraft();
}
} else if (quickCraft.quickcraftStatus == 1) {
itemstack = inventory.getCarriedItem();
if (canItemQuickReplace(inventory, slotNum, itemstack, true) && (quickCraft.quickcraftType == 2 || itemstack.amount() > quickCraft.quickcraftSlots.size())) {
quickCraft.quickcraftSlots.add(slotNum);
}
} else if (quickCraft.quickcraftStatus == 2) {
if (!quickCraft.quickcraftSlots.isEmpty()) {
itemstack1 = inventory.getCarriedItem();
l = inventory.getCarriedItem().amount();
Iterator<Integer> iterator = quickCraft.quickcraftSlots.iterator();
Map<Integer, ItemStack> draggedSlots = new HashMap<>(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack)
while (iterator.hasNext()) {
int slot1 = iterator.next();
ItemStack slotItem = inventory.getItem(slot1);
ItemStack itemstack2 = inventory.getCarriedItem();
if (inventory.isSlot(slot1) && canItemQuickReplace(inventory, slot1, slotItem, true) && (quickCraft.quickcraftType == 2 || itemstack2.amount() >= quickCraft.quickcraftSlots.size())) {
ItemStack itemstack3 = itemstack1;
int j1 = slotItem != null ? slotItem.amount() : 0;
itemstack3 = getQuickCraftSlotCount(quickCraft.quickcraftSlots, quickCraft.quickcraftType, itemstack3, j1);
int k1 = Math.min(itemstack3.getMaxStackSize(), slotItem == null ? 64 : slotItem.getMaxStackSize());
if (itemstack3.amount() > k1) {
itemstack3 = itemstack3.amount(k1);
}
l -= itemstack3.amount() - j1;
// slot1.set(itemstack3);
draggedSlots.put(slot1, itemstack3); // CraftBukkit - Put in map instead of setting
}
}
// CraftBukkit start - InventoryDragEvent
ItemStack newcursor = itemstack1.amount(l);
// It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory.
ItemStack oldCursor = inventory.getCarriedItem();
inventory.setCarriedItem(newcursor);
InventoryDragEvent dragEvent = new InventoryDragEvent(inventory, (newcursor.type() != ItemStack.AIR.type() ? newcursor : null), oldCursor, quickCraft.quickcraftType == 1, draggedSlots);
Limbo.getInstance().getEventsManager().callEvent(dragEvent);
if (!dragEvent.isCancelled()) {
for (Map.Entry<Integer, ItemStack> dslot : draggedSlots.entrySet()) {
inventory.setItem(dslot.getKey(), dslot.getValue());
}
// The only time the carried item will be set to null is if the inventory is closed by the server.
// If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early.
if (inventory.getCarriedItem() != null) {
inventory.setCarriedItem(dragEvent.getCarriedItem());
}
} else {
inventory.setCarriedItem(oldCursor);
}
inventory.updateView();
}
quickCraft.resetQuickCraft();
} else {
quickCraft.resetQuickCraft();
}
break;
}
default:
break;
}
if (packetplayinwindowclick.getClickType() != InventoryClickType.QUICK_CRAFT) {
if (click == ClickType.NUMBER_KEY) {
event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.getSlotNum(), click, action, packetplayinwindowclick.getButtonNum());
} else {
event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.getSlotNum(), click, action);
}
event.setCancelled(cancelled);
Inventory oldTopInventory = player.getInventoryView().getTopInventory();
Limbo.getInstance().getEventsManager().callEvent(event);
if (player.getInventoryView().getTopInventory() != oldTopInventory) {
return;
}
if (event.isCancelled()) {
try {
switch (action) {
// Modified other slots
case PICKUP_ALL:
case MOVE_TO_OTHER_INVENTORY:
case HOTBAR_MOVE_AND_READD:
case HOTBAR_SWAP:
case COLLECT_TO_CURSOR:
case UNKNOWN:
player.getInventoryView().updateView();
break;
// Modified cursor and clicked
case PICKUP_SOME:
case PICKUP_HALF:
case PICKUP_ONE:
case PLACE_ALL:
case PLACE_SOME:
case PLACE_ONE:
case SWAP_WITH_CURSOR:
player.clientConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, 0, inventory.getCarriedItem()));
player.clientConnection.sendPacket(new PacketPlayOutSetSlot(inventory.getUnsafe().a(), 0, packetplayinwindowclick.getSlotNum(), inventory.getItem(packetplayinwindowclick.getSlotNum())));
break;
// Modified clicked only
case DROP_ALL_SLOT:
case DROP_ONE_SLOT:
player.clientConnection.sendPacket(new PacketPlayOutSetSlot(inventory.getUnsafe().a(), 0, packetplayinwindowclick.getSlotNum(), inventory.getItem(packetplayinwindowclick.getSlotNum())));
break;
// Modified cursor only
case DROP_ALL_CURSOR:
case DROP_ONE_CURSOR:
case CLONE_STACK:
player.clientConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, 0, inventory.getCarriedItem()));
break;
// Nothing
case NOTHING:
break;
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
switch (event.getAction()) {
case PICKUP_ALL: {
inventory.setCarriedItem(event.getCurrentItem());
inventory.setItem(event.getRawSlot(), null);
break;
}
case PICKUP_SOME: {
int amountTaken = Math.min(event.getCurrentItem().getMaxStackSize(), event.getCurrentItem().amount());
inventory.setCarriedItem(event.getCurrentItem().amount(amountTaken));
ItemStack oversize = event.getCurrentItem();
inventory.setItem(event.getRawSlot(), oversize.amount(oversize.amount() - amountTaken));
break;
}
case PICKUP_HALF: {
int amountTaken = (int) Math.ceil((double) event.getCurrentItem().amount() / 2.0);
inventory.setCarriedItem(event.getCurrentItem().amount(amountTaken));
ItemStack left = event.getCurrentItem();
inventory.setItem(event.getRawSlot(), left.amount(left.amount() - amountTaken));
break;
}
case PICKUP_ONE: {
inventory.setCarriedItem(event.getCurrentItem().amount(1));
ItemStack left = event.getCurrentItem();
inventory.setItem(event.getRawSlot(), left.amount(left.amount() - 1));
break;
}
case PLACE_ALL: {
ItemStack stack = event.getCarriedItem();
inventory.setCarriedItem(null);
ItemStack item = event.getCurrentItem();
inventory.setItem(event.getRawSlot(), stack.amount((item == null ? 0 : item.amount()) + stack.amount()));
break;
}
case PLACE_SOME: {
ItemStack stack = event.getCarriedItem();
ItemStack item = event.getCurrentItem();
int amountPlaced = item.getMaxStackSize() - item.amount();
inventory.setItem(event.getRawSlot(), item.amount(item.getMaxStackSize()));
inventory.setCarriedItem(event.getCarriedItem().amount(event.getCarriedItem().amount() - amountPlaced));
break;
}
case PLACE_ONE: {
ItemStack stack = event.getCarriedItem();
ItemStack item = event.getCurrentItem();
inventory.setItem(event.getRawSlot(), item == null ? stack.amount(1) : item.amount(item.amount() + 1));
inventory.setCarriedItem(event.getCarriedItem().amount(event.getCarriedItem().amount() - 1));
break;
}
case SWAP_WITH_CURSOR: {
ItemStack stack = event.getCarriedItem();
inventory.setCarriedItem(event.getCurrentItem());
inventory.setItem(event.getRawSlot(), stack);
break;
}
case DROP_ALL_CURSOR: {
inventory.setCarriedItem(null);
break;
}
case DROP_ONE_CURSOR: {
inventory.setCarriedItem(event.getCarriedItem().amount(event.getCarriedItem().amount() - 1));
break;
}
case DROP_ALL_SLOT: {
inventory.setItem(event.getRawSlot(), null);
break;
}
case DROP_ONE_SLOT: {
ItemStack item = event.getCurrentItem();
inventory.setItem(event.getRawSlot(), item.amount(item.amount() - 1));
break;
}
case MOVE_TO_OTHER_INVENTORY: {
ItemStack item = event.getCurrentItem();
Inventory inv;
if (event.getClickedInventory() == inventory.getTopInventory()) {
inv = inventory.getBottomInventory();
} else {
inv = inventory.getTopInventory();
}
HashMap<Integer, ItemStack> leftOver = inv.addItem(item);
if (leftOver.isEmpty()) {
inventory.setItem(event.getRawSlot(), null);
} else {
inventory.setItem(event.getRawSlot(), leftOver.values().iterator().next());
}
break;
}
case HOTBAR_MOVE_AND_READD: {
ItemStack item = inventory.getPlayer().getInventory().getItem(event.getHotbarKey());
inventory.getPlayer().getInventory().setItem(event.getHotbarKey(), event.getCurrentItem());
inventory.setItem(event.getRawSlot(), null);
inventory.getPlayer().getInventory().addItem(item);
break;
}
case HOTBAR_SWAP: {
int hotbarNum = event.getClick().equals(ClickType.SWAP_OFFHAND) ? 40 : event.getHotbarKey();
ItemStack item = inventory.getPlayer().getInventory().getItem(hotbarNum);
inventory.getPlayer().getInventory().setItem(hotbarNum, event.getCurrentItem());
inventory.setItem(event.getRawSlot(), item);
break;
}
case CLONE_STACK: {
ItemStack item = event.getCurrentItem();
inventory.setCarriedItem(item.amount(item.getMaxStackSize()));
break;
}
case COLLECT_TO_CURSOR: {
ItemStack item = event.getCarriedItem();
ItemStack toSearch = item.amount(item.getMaxStackSize() - item.amount());
HashMap<Integer, ItemStack> grabbed = event.getClickedInventory().removeItem(toSearch);
int newAmount = item.amount() + toSearch.amount();
if (!grabbed.isEmpty()) {
newAmount -= grabbed.values().iterator().next().amount();
}
inventory.setCarriedItem(item.amount(newAmount));
break;
}
}
}
inventory.updateView();
}
}
public static int getQuickcraftType(int i) {
return i >> 2 & 3;
}
public static int getQuickcraftHeader(int i) {
return i & 3;
}
public static int getQuickcraftMask(int i, int j) {
return i & 3 | (j & 3) << 2;
}
public static boolean isValidQuickcraftType(int i, Player player) {
return i == 0 || (i == 1 || i == 2 && player.getGamemode().equals(GameMode.CREATIVE));
}
public static boolean canItemQuickReplace(InventoryView view, int slot, ItemStack itemstack, boolean flag) {
boolean flag1 = !view.isSlot(slot) || view.getItem(slot) == null;
ItemStack slotItem = view.getItem(slot);
return !flag1 && slotItem.isSimilar(itemstack) ? slotItem.amount() + (flag ? 0 : itemstack.amount()) <= itemstack.getMaxStackSize() : flag1;
}
public static ItemStack getQuickCraftSlotCount(Set<Integer> set, int i, ItemStack itemstack, int j) {
switch (i) {
case 0:
itemstack = itemstack.amount((int) Math.floor((float) itemstack.amount() / (float) set.size()));
break;
case 1:
itemstack = itemstack.amount(1);
break;
case 2:
itemstack = itemstack.amount(itemstack.getMaxStackSize());
}
return itemstack.amount(itemstack.amount() + j);
}
public static class QuickCraftInfo {
public int quickcraftType;
public int quickcraftStatus;
public final Set<Integer> quickcraftSlots = ConcurrentHashMap.newKeySet();
public void resetQuickCraft() {
quickcraftStatus = 0;
quickcraftSlots.clear();
}
}
}

View File

@ -26,7 +26,10 @@
"0x31": "PacketPlayInUseItem", "0x31": "PacketPlayInUseItem",
"0x2B": "PacketPlayInSetCreativeSlot", "0x2B": "PacketPlayInSetCreativeSlot",
"0x0A": "PacketPlayInWindowClick", "0x0A": "PacketPlayInWindowClick",
"0x0B": "PacketPlayInCloseWindow" "0x0B": "PacketPlayInCloseWindow",
"0x19": "PacketPlayInPickItem",
"0x1C": "PacketPlayInBlockDig",
"0x23": "PacketPlayInItemName"
}, },
"PlayOut": { "PlayOut": {
"PacketPlayOutLogin": "0x24", "PacketPlayOutLogin": "0x24",