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);