diff --git a/README.md b/README.md index 0d04227bf36..28afde7df2f 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,10 @@ Links Build JAR file ------------- -- `git clone https://github.com/NukkitX/Nukkit` +- `git clone https://github.com/CloudburstMC/Nukkit` - `cd Nukkit` - `git submodule update --init` +- `chmod +x mvnw` - `./mvnw clean package` The compiled JAR can be found in the `target/` directory. diff --git a/pom.xml b/pom.xml index 71072613e91..5d23cf68d09 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ 1.8 5.0.0-M4 1.0.0-M4 - 2.11.1 + 2.13.3 UTF-8 3.9.0 diff --git a/src/main/java/cn/nukkit/Nukkit.java b/src/main/java/cn/nukkit/Nukkit.java index afd0a2feb84..783fb0b45be 100644 --- a/src/main/java/cn/nukkit/Nukkit.java +++ b/src/main/java/cn/nukkit/Nukkit.java @@ -42,7 +42,7 @@ public class Nukkit { public final static Properties GIT_INFO = getGitInfo(); public final static String VERSION = getVersion(); - public final static String API_VERSION = "1.0.10"; + public final static String API_VERSION = "1.0.11"; public final static String CODENAME = ""; @Deprecated public final static String MINECRAFT_VERSION = ProtocolInfo.MINECRAFT_VERSION; diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index ceb47d7e8eb..f359258daae 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -33,6 +33,7 @@ import cn.nukkit.form.window.FormWindowCustom; import cn.nukkit.inventory.*; import cn.nukkit.inventory.transaction.CraftingTransaction; +import cn.nukkit.inventory.transaction.EnchantTransaction; import cn.nukkit.inventory.transaction.InventoryTransaction; import cn.nukkit.inventory.transaction.action.InventoryAction; import cn.nukkit.inventory.transaction.data.ReleaseItemData; @@ -137,6 +138,7 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde protected final BiMap windowIndex = windows.inverse(); protected final Set permanentWindows = new IntOpenHashSet(); + private boolean inventoryOpen; protected int messageCounter = 2; @@ -151,6 +153,7 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde protected PlayerUIInventory playerUIInventory; protected CraftingGrid craftingGrid; protected CraftingTransaction craftingTransaction; + protected EnchantTransaction enchantTransaction; public long creationTime = 0; @@ -1053,7 +1056,7 @@ public boolean dataPacket(DataPacket packet) { return false; } - if (log.isTraceEnabled() && !(packet instanceof BatchPacket)) { + if (log.isTraceEnabled() && !server.isIgnoredPacket(packet.getClass())) { log.trace("Outbound {}: {}", this.getName(), packet); } @@ -2152,7 +2155,7 @@ public void handleDataPacket(DataPacket packet) { (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == ' ' - ) { + ) { continue; } @@ -2269,7 +2272,7 @@ public void onCompletion(Server server) { PlayerSkinPacket skinPacket = (PlayerSkinPacket) packet; Skin skin = skinPacket.skin; - if(!skin.isValid()) { + if (!skin.isValid()) { break; } @@ -2635,7 +2638,10 @@ public void onCompletion(Server server) { break; case InteractPacket.ACTION_OPEN_INVENTORY: if (targetEntity.getId() != this.getId()) break; - this.inventory.open(this); + if (!this.inventoryOpen) { + this.inventory.open(this); + this.inventoryOpen = true; + } break; } break; @@ -2668,7 +2674,7 @@ public void onCompletion(Server server) { int itemSlot = -1; for (int slot = 0; slot < this.inventory.getSize(); slot++) { if (this.inventory.getItem(slot).equals(pickEvent.getItem())) { - if(slot < this.inventory.getHotbarSize()) { + if (slot < this.inventory.getHotbarSize()) { this.inventory.setHeldItemSlot(slot); } else { itemSlot = slot; @@ -2678,13 +2684,13 @@ public void onCompletion(Server server) { } } - for(int slot = 0; slot < this.inventory.getHotbarSize(); slot++) { - if(this.inventory.getItem(slot).isNull()) { - if(!itemExists && this.isCreative()) { + for (int slot = 0; slot < this.inventory.getHotbarSize(); slot++) { + if (this.inventory.getItem(slot).isNull()) { + if (!itemExists && this.isCreative()) { this.inventory.setHeldItemSlot(slot); this.inventory.setItemInHand(pickEvent.getItem()); break packetswitch; - } else if(itemSlot > -1) { + } else if (itemSlot > -1) { this.inventory.setHeldItemSlot(slot); this.inventory.setItemInHand(this.inventory.getItem(itemSlot)); this.inventory.clear(itemSlot, true); @@ -2693,18 +2699,18 @@ public void onCompletion(Server server) { } } - if(!itemExists && this.isCreative()) { + if (!itemExists && this.isCreative()) { Item itemInHand = this.inventory.getItemInHand(); this.inventory.setItemInHand(pickEvent.getItem()); - if(!this.inventory.isFull()) { - for(int slot = 0; slot < this.inventory.getSize(); slot++) { - if(this.inventory.getItem(slot).isNull()) { + if (!this.inventory.isFull()) { + for (int slot = 0; slot < this.inventory.getSize(); slot++) { + if (this.inventory.getItem(slot).isNull()) { this.inventory.setItem(slot, itemInHand); break; } } } - } else if(itemSlot > -1) { + } else if (itemSlot > -1) { Item itemInHand = this.inventory.getItemInHand(); this.inventory.setItemInHand(this.inventory.getItem(itemSlot)); this.inventory.setItem(itemSlot, itemInHand); @@ -2764,14 +2770,6 @@ public void onCompletion(Server server) { this.dataPacket(entityEventPacket); Server.broadcastPacket(this.getViewers().values(), entityEventPacket); break; - case EntityEventPacket.ENCHANT: - if (entityEventPacket.eid != this.id) { - break; - } - - int levels = entityEventPacket.data; // Sent as negative number of levels lost - if (levels < 0) this.setExperience(this.exp, this.expLevel + levels); - break; } break; case ProtocolInfo.COMMAND_REQUEST_PACKET: @@ -2809,15 +2807,14 @@ public void onCompletion(Server server) { break; case ProtocolInfo.CONTAINER_CLOSE_PACKET: ContainerClosePacket containerClosePacket = (ContainerClosePacket) packet; - if (!this.spawned) { + if (!this.spawned || containerClosePacket.windowId == ContainerIds.INVENTORY && !inventoryOpen) { break; } if (this.windowIndex.containsKey(containerClosePacket.windowId)) { this.server.getPluginManager().callEvent(new InventoryCloseEvent(this.windowIndex.get(containerClosePacket.windowId), this)); + if (containerClosePacket.windowId == ContainerIds.INVENTORY) this.inventoryOpen = false; this.removeWindow(this.windowIndex.get(containerClosePacket.windowId)); - } else { - this.windowIndex.remove(containerClosePacket.windowId); } if (containerClosePacket.windowId == -1) { this.craftingType = CRAFTING_SMALL; @@ -2972,24 +2969,51 @@ public void onCompletion(Server server) { } } - if (this.craftingTransaction.getPrimaryOutput() != null) { + if (this.craftingTransaction.getPrimaryOutput() != null && this.craftingTransaction.canExecute()) { //we get the actions for this in several packets, so we can't execute it until we get the result this.craftingTransaction.execute(); this.craftingTransaction = null; } + return; + } else if (transactionPacket.isEnchantingPart) { + if (this.enchantTransaction == null) { + this.enchantTransaction = new EnchantTransaction(this, actions); + } else { + for (InventoryAction action : actions) { + this.enchantTransaction.addAction(action); + } + } + if (this.enchantTransaction.canExecute()) { + this.enchantTransaction.execute(); + this.enchantTransaction = null; + } return; } else if (this.craftingTransaction != null) { - if(craftingTransaction.checkForCraftingPart(actions)){ + if (craftingTransaction.checkForCraftingPart(actions)) { for (InventoryAction action : actions) { craftingTransaction.addAction(action); } return; } else { this.server.getLogger().debug("Got unexpected normal inventory action with incomplete crafting transaction from " + this.getName() + ", refusing to execute crafting"); + this.removeAllWindows(false); + this.sendAllInventories(); this.craftingTransaction = null; } + } else if (this.enchantTransaction != null) { + if (enchantTransaction.checkForEnchantPart(actions)) { + for (InventoryAction action : actions) { + enchantTransaction.addAction(action); + } + return; + } else { + this.server.getLogger().debug("Got unexpected normal inventory action with incomplete enchanting transaction from " + this.getName() + ", refusing to execute enchant " + transactionPacket.toString()); + this.removeAllWindows(false); + this.sendAllInventories(); + this.enchantTransaction = null; + } } switch (transactionPacket.transactionType) { @@ -4532,6 +4556,10 @@ public int addWindow(Inventory inventory, Integer forceId) { } public int addWindow(Inventory inventory, Integer forceId, boolean isPermanent) { + return addWindow(inventory, forceId, isPermanent, false); + } + + public int addWindow(Inventory inventory, Integer forceId, boolean isPermanent, boolean alwaysOpen) { if (this.windows.containsKey(inventory)) { return this.windows.get(inventory); } @@ -4547,13 +4575,17 @@ public int addWindow(Inventory inventory, Integer forceId, boolean isPermanent) this.permanentWindows.add(cnt); } - if (inventory.open(this)) { + if (this.spawned && inventory.open(this)) { return cnt; - } else { + } else if (!alwaysOpen) { this.removeWindow(inventory); return -1; + } else { + inventory.getViewers().add(this); } + + return cnt; } public Optional getTopWindow() { @@ -4582,11 +4614,11 @@ public void sendAllInventories() { } protected void addDefaultWindows() { - this.addWindow(this.getInventory(), ContainerIds.INVENTORY, true); - this.getInventory().close(this); + this.addWindow(this.getInventory(), ContainerIds.INVENTORY, true, true); + this.playerUIInventory = new PlayerUIInventory(this); this.addWindow(this.playerUIInventory, ContainerIds.UI, true); - this.addWindow(this.offhandInventory, ContainerIds.OFFHAND, true); + this.addWindow(this.offhandInventory, ContainerIds.OFFHAND, true, true); this.craftingGrid = this.playerUIInventory.getCraftingGrid(); this.addWindow(this.craftingGrid, ContainerIds.NONE); @@ -4727,7 +4759,7 @@ public static BatchPacket getChunkCacheFromData(int chunkX, int chunkZ, int subC batchPayload[1] = buf; byte[] data = Binary.appendBytes(batchPayload); try { - batch.payload = Network.deflate_raw(data, Server.getInstance().networkCompressionLevel); + batch.payload = Network.deflateRaw(data, Server.getInstance().networkCompressionLevel); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index 597c03d3979..b73f8660207 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -713,7 +713,7 @@ public void batchPackets(Player[] players, DataPacket[] packets, boolean forceSy } else { try { byte[] data = Binary.appendBytes(payload); - this.broadcastPacketsCallback(Network.deflate_raw(data, this.networkCompressionLevel), targets); + this.broadcastPacketsCallback(Network.deflateRaw(data, this.networkCompressionLevel), targets); } catch (Exception e) { throw new RuntimeException(e); } @@ -2287,9 +2287,11 @@ private void registerEntities() { Entity.registerEntity("Evoker", EntityEvoker.class); Entity.registerEntity("Ghast", EntityGhast.class); Entity.registerEntity("Guardian", EntityGuardian.class); + Entity.registerEntity("Hoglin", EntityHoglin.class); Entity.registerEntity("Husk", EntityHusk.class); Entity.registerEntity("MagmaCube", EntityMagmaCube.class); Entity.registerEntity("Phantom", EntityPhantom.class); + Entity.registerEntity("Piglin", EntityPiglin.class); Entity.registerEntity("Pillager", EntityPillager.class); Entity.registerEntity("Ravager", EntityRavager.class); Entity.registerEntity("Shulker", EntityShulker.class); @@ -2304,6 +2306,7 @@ private void registerEntities() { Entity.registerEntity("Wither", EntityWither.class); Entity.registerEntity("WitherSkeleton", EntityWitherSkeleton.class); Entity.registerEntity("Zombie", EntityZombie.class); + Entity.registerEntity("Zoglin", EntityZoglin.class); Entity.registerEntity("ZombiePigman", EntityZombiePigman.class); Entity.registerEntity("ZombieVillager", EntityZombieVillager.class); Entity.registerEntity("ZombieVillagerV1", EntityZombieVillagerV1.class); @@ -2330,6 +2333,7 @@ private void registerEntities() { Entity.registerEntity("Sheep", EntitySheep.class); Entity.registerEntity("SkeletonHorse", EntitySkeletonHorse.class); Entity.registerEntity("Squid", EntitySquid.class); + Entity.registerEntity("Strider", EntityStrider.class); Entity.registerEntity("TropicalFish", EntityTropicalFish.class); Entity.registerEntity("Turtle", EntityTurtle.class); Entity.registerEntity("Villager", EntityVillager.class); diff --git a/src/main/java/cn/nukkit/entity/mob/EntityHoglin.java b/src/main/java/cn/nukkit/entity/mob/EntityHoglin.java new file mode 100644 index 00000000000..f5f3f8bc136 --- /dev/null +++ b/src/main/java/cn/nukkit/entity/mob/EntityHoglin.java @@ -0,0 +1,42 @@ +package cn.nukkit.entity.mob; + +import cn.nukkit.level.format.FullChunk; +import cn.nukkit.nbt.tag.CompoundTag; + +/** + * @author Erik Miller | EinBexiii + */ +public class EntityHoglin extends EntityMob { + + public final static int NETWORK_ID = 124; + + @Override + public int getNetworkId() { + return NETWORK_ID; + } + + public EntityHoglin(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + } + + @Override + protected void initEntity() { + super.initEntity(); + this.setMaxHealth(40); + } + + @Override + public float getWidth() { + return 0.9f; + } + + @Override + public float getHeight() { + return 0.9f; + } + + @Override + public String getName() { + return "Hoglin"; + } +} diff --git a/src/main/java/cn/nukkit/entity/mob/EntityPiglin.java b/src/main/java/cn/nukkit/entity/mob/EntityPiglin.java new file mode 100644 index 00000000000..2501de7ad8f --- /dev/null +++ b/src/main/java/cn/nukkit/entity/mob/EntityPiglin.java @@ -0,0 +1,42 @@ +package cn.nukkit.entity.mob; + +import cn.nukkit.level.format.FullChunk; +import cn.nukkit.nbt.tag.CompoundTag; + +/** + * @author Erik Miller | EinBexiii + */ +public class EntityPiglin extends EntityMob { + + public final static int NETWORK_ID = 123; + + @Override + public int getNetworkId() { + return NETWORK_ID; + } + + public EntityPiglin(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + } + + @Override + protected void initEntity() { + super.initEntity(); + this.setMaxHealth(16); + } + + @Override + public float getWidth() { + return 0.6f; + } + + @Override + public float getHeight() { + return 1.95f; + } + + @Override + public String getName() { + return "Piglin"; + } +} diff --git a/src/main/java/cn/nukkit/entity/mob/EntityZoglin.java b/src/main/java/cn/nukkit/entity/mob/EntityZoglin.java new file mode 100644 index 00000000000..1be82ee3714 --- /dev/null +++ b/src/main/java/cn/nukkit/entity/mob/EntityZoglin.java @@ -0,0 +1,42 @@ +package cn.nukkit.entity.mob; + +import cn.nukkit.level.format.FullChunk; +import cn.nukkit.nbt.tag.CompoundTag; + +/** + * @author Erik Miller | EinBexiii + */ +public class EntityZoglin extends EntityMob { + + public final static int NETWORK_ID = 126; + + @Override + public int getNetworkId() { + return NETWORK_ID; + } + + public EntityZoglin(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + } + + @Override + protected void initEntity() { + super.initEntity(); + this.setMaxHealth(40); + } + + @Override + public float getWidth() { + return 0.9f; + } + + @Override + public float getHeight() { + return 0.9f; + } + + @Override + public String getName() { + return "Zoglin"; + } +} diff --git a/src/main/java/cn/nukkit/entity/passive/EntityStrider.java b/src/main/java/cn/nukkit/entity/passive/EntityStrider.java new file mode 100644 index 00000000000..64558d8bb30 --- /dev/null +++ b/src/main/java/cn/nukkit/entity/passive/EntityStrider.java @@ -0,0 +1,42 @@ +package cn.nukkit.entity.passive; + +import cn.nukkit.level.format.FullChunk; +import cn.nukkit.nbt.tag.CompoundTag; + +/** + * @author Erik Miller | EinBexiii + */ +public class EntityStrider extends EntityAnimal { + + public final static int NETWORK_ID = 125; + + @Override + public int getNetworkId() { + return NETWORK_ID; + } + + public EntityStrider(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + } + + @Override + protected void initEntity() { + super.initEntity(); + this.setMaxHealth(15); + } + + @Override + public float getWidth() { + return 0.9f; + } + + @Override + public float getHeight() { + return 1.7f; + } + + @Override + public String getName() { + return "Strider"; + } +} diff --git a/src/main/java/cn/nukkit/event/inventory/EnchantItemEvent.java b/src/main/java/cn/nukkit/event/inventory/EnchantItemEvent.java new file mode 100644 index 00000000000..f67d3abbcc2 --- /dev/null +++ b/src/main/java/cn/nukkit/event/inventory/EnchantItemEvent.java @@ -0,0 +1,32 @@ +package cn.nukkit.event.inventory; + +import cn.nukkit.Player; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.inventory.EnchantInventory; +import cn.nukkit.item.Item; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class EnchantItemEvent extends InventoryEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private Item oldItem; + private Item newItem; + private int xpCost; + private Player enchanter; + + public EnchantItemEvent(EnchantInventory inventory, Item oldItem, Item newItem, int cost, Player p) { + super(inventory); + this.oldItem = oldItem; + this.newItem = newItem; + this.xpCost = cost; + this.enchanter = p; + } +} diff --git a/src/main/java/cn/nukkit/inventory/EnchantInventory.java b/src/main/java/cn/nukkit/inventory/EnchantInventory.java index 8752947d0e2..f31918ec0b6 100644 --- a/src/main/java/cn/nukkit/inventory/EnchantInventory.java +++ b/src/main/java/cn/nukkit/inventory/EnchantInventory.java @@ -1,6 +1,7 @@ package cn.nukkit.inventory; import cn.nukkit.Player; +import cn.nukkit.item.Item; import cn.nukkit.level.Position; @@ -10,6 +11,9 @@ */ public class EnchantInventory extends FakeBlockUIComponent { + public static final int ENCHANT_INPUT_ITEM_UI_SLOT = 14; + public static final int ENCHANT_REAGENT_UI_SLOT = 15; + public EnchantInventory(PlayerUIInventory playerUI, Position position) { super(playerUI, InventoryType.ENCHANT_TABLE, 14, position); } @@ -30,4 +34,16 @@ public void onClose(Player who) { } } } + + public Item getInputSlot() { + return this.getItem(0); + } + + public Item getOutputSlot() { + return this.getItem(0); + } + + public Item getReagentSlot() { + return this.getItem(1); + } } diff --git a/src/main/java/cn/nukkit/inventory/PlayerInventory.java b/src/main/java/cn/nukkit/inventory/PlayerInventory.java index 1529744a6ba..73fcc1da0aa 100644 --- a/src/main/java/cn/nukkit/inventory/PlayerInventory.java +++ b/src/main/java/cn/nukkit/inventory/PlayerInventory.java @@ -24,7 +24,6 @@ public class PlayerInventory extends BaseInventory { protected int itemInHandIndex = 0; private int[] hotbar; - private boolean openedByPlayer; public PlayerInventory(EntityHumanType player) { super(player, InventoryType.PLAYER); @@ -498,17 +497,9 @@ public EntityHuman getHolder() { return (EntityHuman) super.getHolder(); } - @Override - public boolean open(Player who) { - if (who.equals(this.getHolder()) && this.openedByPlayer) - return false; - return super.open(who); - } - @Override public void onOpen(Player who) { super.onOpen(who); - if (who.equals(this.getHolder())) this.openedByPlayer = true; ContainerOpenPacket pk = new ContainerOpenPacket(); pk.windowId = who.getWindowId(this); pk.type = this.getType().getNetworkType(); @@ -524,11 +515,9 @@ public void onClose(Player who) { ContainerClosePacket pk = new ContainerClosePacket(); pk.windowId = who.getWindowId(this); who.dataPacket(pk); - - if (who.equals(this.getHolder())) { - this.openedByPlayer = false; - return; + // player can never stop viewing their own inventory + if (who != holder) { + super.onClose(who); } - super.onClose(who); } } diff --git a/src/main/java/cn/nukkit/inventory/PlayerUIInventory.java b/src/main/java/cn/nukkit/inventory/PlayerUIInventory.java index 8434727761a..cf5753e35de 100644 --- a/src/main/java/cn/nukkit/inventory/PlayerUIInventory.java +++ b/src/main/java/cn/nukkit/inventory/PlayerUIInventory.java @@ -1,8 +1,6 @@ package cn.nukkit.inventory; import cn.nukkit.Player; -import cn.nukkit.item.Item; -import cn.nukkit.network.protocol.InventoryContentPacket; import cn.nukkit.network.protocol.InventorySlotPacket; import cn.nukkit.network.protocol.types.ContainerIds; @@ -50,7 +48,6 @@ public void sendSlot(int index, Player... target) { for (Player p : target) { if (p == this.getHolder()) { pk.inventoryId = ContainerIds.UI; - p.dataPacket(pk); } else { int id; @@ -59,34 +56,14 @@ public void sendSlot(int index, Player... target) { continue; } pk.inventoryId = id; - p.dataPacket(pk); } + p.dataPacket(pk); } } @Override public void sendContents(Player... target) { - InventoryContentPacket pk = new InventoryContentPacket(); - pk.slots = new Item[this.getSize()]; - for (int i = 0; i < this.getSize(); ++i) { - pk.slots[i] = this.getItem(i); - } - - for (Player p : target) { - if (p == this.getHolder()) { - pk.inventoryId = ContainerIds.UI; - p.dataPacket(pk); - } else { - int id; - - if ((id = p.getWindowId(this)) == ContainerIds.NONE) { - this.close(p); - continue; - } - pk.inventoryId = id; - p.dataPacket(pk); - } - } + //doesn't work here } @Override diff --git a/src/main/java/cn/nukkit/inventory/transaction/EnchantTransaction.java b/src/main/java/cn/nukkit/inventory/transaction/EnchantTransaction.java new file mode 100644 index 00000000000..e99855d0a31 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/EnchantTransaction.java @@ -0,0 +1,106 @@ +package cn.nukkit.inventory.transaction; + +import cn.nukkit.Player; +import cn.nukkit.event.inventory.EnchantItemEvent; +import cn.nukkit.inventory.EnchantInventory; +import cn.nukkit.inventory.Inventory; +import cn.nukkit.inventory.transaction.action.EnchantingAction; +import cn.nukkit.inventory.transaction.action.InventoryAction; +import cn.nukkit.item.Item; +import cn.nukkit.network.protocol.types.NetworkInventoryAction; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class EnchantTransaction extends InventoryTransaction { + private Item inputItem; + private Item outputItem; + private int cost = -1; + + public EnchantTransaction(Player source, List actions) { + super(source, actions); + } + + @Override + public boolean canExecute() { + Inventory inv = getSource().getWindowById(Player.ENCHANT_WINDOW_ID); + if (inv == null) return false; + EnchantInventory eInv = (EnchantInventory) inv; + if (!getSource().isCreative()) { + if (cost == -1 || !eInv.getReagentSlot().equals(Item.get(Item.DYE, 4)) || eInv.getReagentSlot().count < cost) + return false; + } + return (inputItem != null && outputItem != null && inputItem.equals(eInv.getInputSlot(), true, true)); + } + + @Override + public boolean execute() { + // This will validate the enchant conditions + if (this.hasExecuted || !this.canExecute()) { + source.removeAllWindows(false); + source.sendAllInventories(); + return false; + } + EnchantInventory inv = (EnchantInventory) getSource().getWindowById(Player.ENCHANT_WINDOW_ID); + EnchantItemEvent ev = new EnchantItemEvent(inv, inputItem, outputItem, cost, source); + source.getServer().getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + source.removeAllWindows(); + source.sendAllInventories(); + // Cancelled by plugin, means handled OK + return true; + } + // This will process all the slot changes + for (InventoryAction a : this.actions) { + if (a.execute(source)) { + a.onExecuteSuccess(source); + } else { + a.onExecuteFail(source); + } + } + + if (!ev.getNewItem().equals(this.outputItem, true, true)) { + // Plugin changed item, so the previous slot change is going to be invalid + // Send the replaced item to the enchant inventory manually + inv.setItem(0, ev.getNewItem(), true); + } + + if (!source.isCreative()) { + source.setExperience(source.getExperience(), source.getExperienceLevel() - ev.getXpCost()); + } + return true; + } + + @Override + public void addAction(InventoryAction action) { + super.addAction(action); + if (action instanceof EnchantingAction) { + switch (((EnchantingAction) action).getType()) { + case NetworkInventoryAction.SOURCE_TYPE_ENCHANT_INPUT: + this.inputItem = action.getTargetItem(); // Input sent as newItem + break; + case NetworkInventoryAction.SOURCE_TYPE_ENCHANT_OUTPUT: + this.outputItem = action.getSourceItem(); // Output sent as oldItem + break; + case NetworkInventoryAction.SOURCE_TYPE_ENCHANT_MATERIAL: + if (action.getTargetItem().equals(Item.get(Item.AIR), false, false)) { + this.cost = action.getSourceItem().count; + } else { + this.cost = action.getSourceItem().count - action.getTargetItem().count; + } + break; + } + + } + } + + public boolean checkForEnchantPart(List actions) { + for (InventoryAction action : actions) { + if (action instanceof EnchantingAction) return true; + } + return false; + } +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/InventoryTransaction.java b/src/main/java/cn/nukkit/inventory/transaction/InventoryTransaction.java index ea14736f949..c23b9407fa8 100644 --- a/src/main/java/cn/nukkit/inventory/transaction/InventoryTransaction.java +++ b/src/main/java/cn/nukkit/inventory/transaction/InventoryTransaction.java @@ -4,14 +4,11 @@ import cn.nukkit.event.inventory.InventoryClickEvent; import cn.nukkit.event.inventory.InventoryTransactionEvent; import cn.nukkit.inventory.Inventory; -import cn.nukkit.inventory.PlayerInventory; import cn.nukkit.inventory.transaction.action.InventoryAction; import cn.nukkit.inventory.transaction.action.SlotChangeAction; import cn.nukkit.item.Item; -import cn.nukkit.utils.MainLogger; import java.util.*; -import java.util.Map.Entry; /** * @author CreeperFace @@ -67,19 +64,19 @@ public Set getActions() { } public void addAction(InventoryAction action) { - if(action instanceof SlotChangeAction){ - SlotChangeAction slotChangeAction = (SlotChangeAction)action; + if (action instanceof SlotChangeAction) { + SlotChangeAction slotChangeAction = (SlotChangeAction) action; ListIterator iterator = this.actions.listIterator(); - while(iterator.hasNext()){ + while (iterator.hasNext()) { InventoryAction existingAction = iterator.next(); - if(existingAction instanceof SlotChangeAction){ - SlotChangeAction existingSlotChangeAction = (SlotChangeAction)existingAction; - if(!existingSlotChangeAction.getInventory().equals(slotChangeAction.getInventory())) + if (existingAction instanceof SlotChangeAction) { + SlotChangeAction existingSlotChangeAction = (SlotChangeAction) existingAction; + if (!existingSlotChangeAction.getInventory().equals(slotChangeAction.getInventory())) continue; Item existingTarget = existingSlotChangeAction.getTargetItem(); - if(existingSlotChangeAction.getSlot() == slotChangeAction.getSlot() && slotChangeAction.getSourceItem().equals(existingTarget, existingTarget.hasMeta(), existingTarget.hasCompoundTag())){ + if (existingSlotChangeAction.getSlot() == slotChangeAction.getSlot() && slotChangeAction.getSourceItem().equals(existingTarget, existingTarget.hasMeta(), existingTarget.hasCompoundTag())) { iterator.set(new SlotChangeAction(existingSlotChangeAction.getInventory(), existingSlotChangeAction.getSlot(), existingSlotChangeAction.getSourceItem(), slotChangeAction.getTargetItem())); action.onAddToTransaction(this); return; @@ -137,10 +134,11 @@ protected boolean matchItems(List needItems, List haveItems) { } protected void sendInventories() { - for (Inventory inventory : this.inventories) { - inventory.sendContents(this.source); - if (inventory instanceof PlayerInventory) { - ((PlayerInventory) inventory).sendArmorContents(this.source); + for (InventoryAction action : this.actions) { + if (action instanceof SlotChangeAction) { + SlotChangeAction sca = (SlotChangeAction) action; + + sca.getInventory().sendSlot(sca.getSlot(), this.source); } } } @@ -165,7 +163,7 @@ protected boolean callExecuteEvent() { } SlotChangeAction slotChange = (SlotChangeAction) action; - if (slotChange.getInventory() instanceof PlayerInventory) { + if (slotChange.getInventory().getHolder() instanceof Player) { who = (Player) slotChange.getInventory().getHolder(); } diff --git a/src/main/java/cn/nukkit/inventory/transaction/action/EnchantingAction.java b/src/main/java/cn/nukkit/inventory/transaction/action/EnchantingAction.java new file mode 100644 index 00000000000..c035bad751c --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/action/EnchantingAction.java @@ -0,0 +1,34 @@ +package cn.nukkit.inventory.transaction.action; + +import cn.nukkit.Player; +import cn.nukkit.item.Item; +import lombok.Getter; + +public class EnchantingAction extends InventoryAction { + @Getter + private int type; + + public EnchantingAction(Item source, Item target, int type) { + super(source, target); + this.type = type; + } + + @Override + public boolean isValid(Player source) { + return source.getWindowById(Player.ENCHANT_WINDOW_ID) != null; + } + + @Override + public boolean execute(Player source) { + return true; + } + + @Override + public void onExecuteSuccess(Player source) { + } + + @Override + public void onExecuteFail(Player source) { + + } +} diff --git a/src/main/java/cn/nukkit/network/CompressBatchedPacket.java b/src/main/java/cn/nukkit/network/CompressBatchedPacket.java index f2d42fde71a..bcf4f135e8f 100644 --- a/src/main/java/cn/nukkit/network/CompressBatchedPacket.java +++ b/src/main/java/cn/nukkit/network/CompressBatchedPacket.java @@ -36,7 +36,7 @@ public CompressBatchedPacket(byte[] data, List targets, int l @Override public void onRun() { try { - this.finalData = Network.deflate_raw(data, level); + this.finalData = Network.deflateRaw(data, level); this.data = null; } catch (Exception e) { //ignore diff --git a/src/main/java/cn/nukkit/network/CompressBatchedTask.java b/src/main/java/cn/nukkit/network/CompressBatchedTask.java index e2433806bf1..bd64ccd0579 100644 --- a/src/main/java/cn/nukkit/network/CompressBatchedTask.java +++ b/src/main/java/cn/nukkit/network/CompressBatchedTask.java @@ -36,7 +36,7 @@ public CompressBatchedTask(byte[][] data, List targets, int l @Override public void onRun() { try { - this.finalData = Network.deflate_raw(this.data, this.level); + this.finalData = Network.deflateRaw(this.data, this.level); this.data = null; } catch (Exception e) { //ignore diff --git a/src/main/java/cn/nukkit/network/Network.java b/src/main/java/cn/nukkit/network/Network.java index 5f0c4395571..c896af28341 100644 --- a/src/main/java/cn/nukkit/network/Network.java +++ b/src/main/java/cn/nukkit/network/Network.java @@ -70,7 +70,7 @@ public Network(Server server) { this.server = server; } - public static byte[] inflate_raw(byte[] data) throws IOException, DataFormatException { + public static byte[] inflateRaw(byte[] data) throws IOException, DataFormatException { Inflater inflater = INFLATER_RAW.get(); inflater.reset(); inflater.setInput(data); @@ -86,7 +86,7 @@ public static byte[] inflate_raw(byte[] data) throws IOException, DataFormatExce return bos.toByteArray(); } - public static byte[] deflate_raw(byte[] data, int level) throws IOException { + public static byte[] deflateRaw(byte[] data, int level) throws IOException { Deflater deflater = DEFLATER_RAW.get(); deflater.reset(); deflater.setLevel(level); @@ -103,7 +103,7 @@ public static byte[] deflate_raw(byte[] data, int level) throws IOException { return bos.toByteArray(); } - public static byte[] deflate_raw(byte[][] datas, int level) throws IOException { + public static byte[] deflateRaw(byte[][] datas, int level) throws IOException { Deflater deflater = DEFLATER_RAW.get(); deflater.reset(); deflater.setLevel(level); @@ -215,7 +215,7 @@ public Server getServer() { public void processBatch(BatchPacket packet, Player player) { byte[] data; try { - data = Network.inflate_raw(packet.payload); + data = Network.inflateRaw(packet.payload); //data = Zlib.inflate(packet.payload, 2 * 1024 * 1024); // Max 2MB } catch (Exception e) { log.debug("Exception while inflating batch packet", e); diff --git a/src/main/java/cn/nukkit/network/RakNetInterface.java b/src/main/java/cn/nukkit/network/RakNetInterface.java index 1bda4d466f8..9e345e60a6e 100644 --- a/src/main/java/cn/nukkit/network/RakNetInterface.java +++ b/src/main/java/cn/nukkit/network/RakNetInterface.java @@ -1,5 +1,6 @@ package cn.nukkit.network; +import cn.nukkit.Nukkit; import cn.nukkit.Player; import cn.nukkit.Server; import cn.nukkit.event.player.PlayerCreationEvent; diff --git a/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java b/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java index 3edc25ff4b0..cc08d89e14b 100644 --- a/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java @@ -126,6 +126,10 @@ public class AddEntityPacket extends DataPacket { .put(EntityZombieVillager.NETWORK_ID, "minecraft:zombie_villager_v2") .put(121, "minecraft:fox") .put(122, "minecraft:bee") + .put(EntityPiglin.NETWORK_ID, "minecraft:piglin") + .put(EntityHoglin.NETWORK_ID, "minecraft:hoglin") + .put(EntityStrider.NETWORK_ID, "minecraft:strider") + .put(EntityZoglin.NETWORK_ID, "minecraft:zoglin") .build(); @Override diff --git a/src/main/java/cn/nukkit/network/protocol/DataPacket.java b/src/main/java/cn/nukkit/network/protocol/DataPacket.java index 4bb2c010183..3a24945a4a8 100644 --- a/src/main/java/cn/nukkit/network/protocol/DataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/DataPacket.java @@ -66,7 +66,7 @@ public BatchPacket compress(int level) { batchPayload[1] = buf; byte[] data = Binary.appendBytes(batchPayload); try { - batch.payload = Network.deflate_raw(data, level); + batch.payload = Network.deflateRaw(data, level); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java b/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java index 2aa9fcd6f3c..b9ac3a30bd1 100644 --- a/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java @@ -43,10 +43,11 @@ public class InventoryTransactionPacket extends DataPacket { public int legacyRequestId; /** - * NOTE: THIS FIELD DOES NOT EXIST IN THE PROTOCOL, it's merely used for convenience for PocketMine-MP to easily - * determine whether we're doing a crafting transaction. + * NOTE: THESE FIELDS DO NOT EXIST IN THE PROTOCOL, it's merely used for convenience for us to easily + * determine whether we're doing a crafting or enchanting transaction. */ public boolean isCraftingPart = false; + public boolean isEnchantingPart = false; @Override public byte pid() { diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerEnchantOptionsPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerEnchantOptionsPacket.java index dec27f2f509..a8eecd819c9 100644 --- a/src/main/java/cn/nukkit/network/protocol/PlayerEnchantOptionsPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/PlayerEnchantOptionsPacket.java @@ -22,7 +22,7 @@ public byte pid() { public void decode() { int size = (int) this.getUnsignedVarInt(); for (int i = 0; i < size; i++) { - int cost = this.getVarInt(); + int minLevel = this.getVarInt(); int slot = this.getInt(); int eSize = (int) this.getUnsignedVarInt(); @@ -47,7 +47,7 @@ public void decode() { } String enchantName = this.getString(); int eNetId = (int) this.getUnsignedVarInt(); - this.options.add(new EnchantOptionData(cost, slot, list1, list2, list3, enchantName, eNetId)); + this.options.add(new EnchantOptionData(minLevel, slot, list1, list2, list3, enchantName, eNetId)); } } @@ -57,7 +57,7 @@ public void encode() { this.reset(); this.putUnsignedVarInt(this.options.size()); for (EnchantOptionData option : this.options) { - this.putVarInt(option.getCost()); + this.putVarInt(option.getMinLevel()); this.putInt(option.getPrimarySlot()); this.putUnsignedVarInt(option.getEnchants0().size()); for (EnchantData data : option.getEnchants0()) { @@ -82,7 +82,7 @@ public void encode() { @Value public class EnchantOptionData { - private final int cost; + private final int minLevel; private final int primarySlot; private final List enchants0; private final List enchants1; diff --git a/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java b/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java index 19881b044cc..7efee377b1e 100644 --- a/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java +++ b/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java @@ -7,7 +7,6 @@ import cn.nukkit.inventory.Inventory; import cn.nukkit.inventory.transaction.action.*; import cn.nukkit.item.Item; -import cn.nukkit.item.ItemID; import cn.nukkit.network.protocol.InventoryTransactionPacket; import lombok.ToString; @@ -91,6 +90,11 @@ public NetworkInventoryAction read(InventoryTransactionPacket packet) { case SOURCE_TYPE_CRAFTING_USE_INGREDIENT: packet.isCraftingPart = true; break; + case SOURCE_TYPE_ENCHANT_INPUT: + case SOURCE_TYPE_ENCHANT_OUTPUT: + case SOURCE_TYPE_ENCHANT_MATERIAL: + packet.isEnchantingPart = true; + break; } break; } @@ -140,6 +144,25 @@ public InventoryAction createInventoryAction(Player player) { this.inventorySlot += 36; this.windowId = ContainerIds.INVENTORY; } + // ID 124 with slot 14/15 is enchant inventory + if (this.windowId == ContainerIds.UI) { + if (this.inventorySlot == EnchantInventory.ENCHANT_INPUT_ITEM_UI_SLOT) { + if (player.getWindowById(Player.ENCHANT_WINDOW_ID) == null) { + player.getServer().getLogger().error("Player " + player.getName() + " does not have enchant window open"); + return null; + } + this.windowId = Player.ENCHANT_WINDOW_ID; + this.inventorySlot = 0; + // TODO, check if unenchanted item and send EnchantOptionsPacket + } else if (this.inventorySlot == EnchantInventory.ENCHANT_REAGENT_UI_SLOT) { + if (player.getWindowById(Player.ENCHANT_WINDOW_ID) == null) { + player.getServer().getLogger().error("Player " + player.getName() + " does not have enchant window open"); + return null; + } + this.windowId = Player.ENCHANT_WINDOW_ID; + this.inventorySlot = 1; + } + } Inventory window = player.getWindowById(this.windowId); if (window != null) { @@ -235,52 +258,13 @@ public InventoryAction createInventoryAction(Player player) { } EnchantInventory enchant = (EnchantInventory) inv; - // TODO: This is all a temporary hack. Enchanting needs it's own transaction class. switch (this.windowId) { case SOURCE_TYPE_ENCHANT_INPUT: - if (this.inventorySlot != 0) { - // Input should only be in slot 0. - return null; - } - break; + return new EnchantingAction(this.oldItem, this.newItem, SOURCE_TYPE_ENCHANT_INPUT); case SOURCE_TYPE_ENCHANT_MATERIAL: - if (this.inventorySlot != 1) { - // Material should only be in slot 1. - return null; - } - break; + return new EnchantingAction(this.newItem, this.oldItem, SOURCE_TYPE_ENCHANT_MATERIAL); // Mojang ish backwards? case SOURCE_TYPE_ENCHANT_OUTPUT: - if (this.inventorySlot != 0) { - // Outputs should only be in slot 0. - return null; - } - if (Item.get(ItemID.DYE, 4).equals(this.newItem, true, false)) { - this.inventorySlot = 2; // Fake slot to store used material - if (this.newItem.getCount() < 1 || this.newItem.getCount() > 3) { - // Invalid material - return null; - } - Item material = enchant.getItem(1); - // Material to take away. - int toRemove = this.newItem.getCount(); - if (material.getId() != ItemID.DYE && material.getDamage() != 4 && - material.getCount() < toRemove) { - // Invalid material or not enough - return null; - } - } else { - Item toEnchant = enchant.getItem(0); - Item material = enchant.getItem(1); - if (toEnchant.equals(this.newItem, true, true) && - (material.getId() == ItemID.DYE && material.getDamage() == 4 || player.isCreative())) { - this.inventorySlot = 3; // Fake slot to store the resultant item. - - //TODO: Check (old) item has valid enchantments - enchant.setItem(3, this.oldItem, false); - } else { - return null; - } - } + return new EnchantingAction(this.oldItem, this.newItem, SOURCE_TYPE_ENCHANT_OUTPUT); } return new SlotChangeAction(enchant, this.inventorySlot, this.oldItem, this.newItem);