From fc376ca73e8230bd58af18fe104f4e5285b9c24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Bedn=C3=A1=C5=99?= Date: Sun, 11 Aug 2019 23:13:40 +0200 Subject: [PATCH 01/52] Basic dispenser functionality --- .gitignore | 1 + src/main/java/cn/nukkit/Server.java | 3 + .../java/cn/nukkit/block/BlockDispenser.java | 163 ++++++++++++++++-- src/main/java/cn/nukkit/block/BlockLever.java | 9 +- .../java/cn/nukkit/block/BlockPistonBase.java | 2 +- .../java/cn/nukkit/block/BlockSapling.java | 3 +- .../nukkit/block/BlockUndyedShulkerBox.java | 19 +- .../cn/nukkit/blockentity/BlockEntity.java | 3 +- .../blockentity/BlockEntityDispenser.java | 135 +++++++++++++++ .../blockentity/BlockEntityPistonArm.java | 14 +- .../dispenser/BoatDispenseBehavior.java | 39 +++++ .../dispenser/BucketDispenseBehavior.java | 26 ++- .../dispenser/DefaultDispenseBehavior.java | 32 +++- .../cn/nukkit/dispenser/DispenseBehavior.java | 3 +- .../dispenser/DispenseBehaviorRegister.java | 60 ++++++- .../nukkit/dispenser/DyeDispenseBehavior.java | 28 +++ .../EmptyBucketDispenseBehavior.java | 17 +- .../dispenser/FireChargeDispenseBehavior.java | 14 ++ .../dispenser/FireworksDispenseBehavior.java | 22 +++ .../FlintAndSteelDispenseBehavior.java | 25 +++ .../dispenser/ProjectileDispenseBehavior.java | 41 +++-- .../dispenser/PumpkinDispenseBehavior.java | 17 ++ .../dispenser/ShulkerBoxDispenseBehavior.java | 46 +++++ .../dispenser/SpawnEggDispenseBehavior.java | 32 ++++ .../nukkit/dispenser/TNTDispenseBehavior.java | 23 +++ .../cn/nukkit/entity/EntityHumanType.java | 2 +- .../nukkit/entity/projectile/EntityArrow.java | 3 - .../entity/projectile/EntityProjectile.java | 25 ++- .../nukkit/inventory/DispenserInventory.java | 15 ++ .../cn/nukkit/inventory/InventoryType.java | 2 +- src/main/java/cn/nukkit/item/Item.java | 3 +- src/main/java/cn/nukkit/item/ItemBucket.java | 10 +- src/main/java/cn/nukkit/level/Location.java | 10 ++ 33 files changed, 782 insertions(+), 65 deletions(-) create mode 100644 src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java create mode 100644 src/main/java/cn/nukkit/dispenser/BoatDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/DyeDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/FireChargeDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/FireworksDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/FlintAndSteelDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/PumpkinDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/ShulkerBoxDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/SpawnEggDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/dispenser/TNTDispenseBehavior.java create mode 100644 src/main/java/cn/nukkit/inventory/DispenserInventory.java diff --git a/.gitignore b/.gitignore index 367049fedc6..22344077438 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ recipes.json #Nukkit data dir data/ data/* +/src/main/resources/rebel.xml diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index 0a726b824c5..debb134426c 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -4,6 +4,7 @@ import cn.nukkit.blockentity.*; import cn.nukkit.command.*; import cn.nukkit.console.NukkitConsole; +import cn.nukkit.dispenser.DispenseBehaviorRegister; import cn.nukkit.entity.Attribute; import cn.nukkit.entity.Entity; import cn.nukkit.entity.EntityHuman; @@ -453,6 +454,7 @@ public Level remove(Object key) { Effect.init(); Potion.init(); Attribute.init(); + DispenseBehaviorRegister.init(); GlobalBlockPalette.getOrCreateRuntimeId(0, 0); //Force it to load // Convert legacy data before plugins get the chance to mess with it. @@ -2315,6 +2317,7 @@ private void registerBlockEntities() { BlockEntity.registerBlockEntity(BlockEntity.SHULKER_BOX, BlockEntityShulkerBox.class); BlockEntity.registerBlockEntity(BlockEntity.BANNER, BlockEntityBanner.class); BlockEntity.registerBlockEntity(BlockEntity.MUSIC, BlockEntityMusic.class); + BlockEntity.registerBlockEntity(BlockEntity.DISPENSER, BlockEntityDispenser.class); } public boolean isNetherAllowed() { diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index 3bd7bbd75f5..6914f60ccc3 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -1,11 +1,23 @@ package cn.nukkit.block; +import cn.nukkit.Player; +import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityDispenser; +import cn.nukkit.dispenser.DispenseBehavior; +import cn.nukkit.dispenser.DispenseBehaviorRegister; +import cn.nukkit.inventory.ContainerInventory; +import cn.nukkit.inventory.Inventory; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; +import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; import cn.nukkit.math.Vector3; import cn.nukkit.utils.Faceable; +import java.util.Map.Entry; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + /** * Created by CreeperFace on 15.4.2017. */ @@ -41,17 +53,13 @@ public Item toItem() { @Override public int getComparatorInputOverride() { - /*BlockEntity blockEntity = this.level.getBlockEntity(this); - - if(blockEntity instanceof BlockEntityDispenser) { - //return ContainerInventory.calculateRedstone(((BlockEntityDispenser) blockEntity).getInventory()); TODO: dispenser - }*/ + BlockEntity blockEntity = this.level.getBlockEntity(this); - return super.getComparatorInputOverride(); - } + if (blockEntity instanceof BlockEntityDispenser) { + return ContainerInventory.calculateRedstone(((BlockEntityDispenser) blockEntity).getInventory()); + } - public BlockFace getFacing() { - return BlockFace.fromIndex(this.getDamage() & 7); + return 0; } public boolean isTriggered() { @@ -60,7 +68,7 @@ public boolean isTriggered() { public void setTriggered(boolean value) { int i = 0; - i |= getFacing().getIndex(); + i |= getBlockFace().getIndex(); if (value) { i |= 8; @@ -69,21 +77,144 @@ public void setTriggered(boolean value) { this.setDamage(i); } + @Override + public boolean canBeActivated() { + return true; + } + + @Override + public boolean onActivate(Item item, Player player) { + if (player == null) { + return false; + } + + BlockEntity blockEntity = this.level.getBlockEntity(this); + + if (!(blockEntity instanceof BlockEntityDispenser)) { + return false; + } + + player.addWindow(((BlockEntityDispenser) blockEntity).getInventory()); + return true; + } + + @Override + public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { + if (player != null) { + if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) { + double y = player.y + player.getEyeHeight(); + + if (y - this.y > 2) { + this.setDamage(BlockFace.UP.getIndex()); + } else if (this.y - y > 0) { + this.setDamage(BlockFace.DOWN.getIndex()); + } else { + this.setDamage(player.getHorizontalFacing().getOpposite().getIndex()); + } + } else { + this.setDamage(player.getHorizontalFacing().getOpposite().getIndex()); + } + } + + this.getLevel().setBlock(block, this, true); + + new BlockEntityDispenser(this.level.getChunk(getChunkX(), getChunkZ()), + BlockEntity.getDefaultCompound(this, BlockEntity.DISPENSER)); + return true; + } + + @Override + public int onUpdate(int type) { + if (type == Level.BLOCK_UPDATE_SCHEDULED) { + this.setTriggered(false); + this.level.setBlock(this, this, false, false); + + dispense(); + return type; + } else if (type == Level.BLOCK_UPDATE_REDSTONE) { + Vector3 pos = this.add(0); + + boolean powered = level.isBlockPowered(pos) || level.isBlockPowered(pos.up()); + boolean triggered = isTriggered(); + + if (powered && !triggered) { + this.setTriggered(true); + this.level.setBlock(this, this, false, false); + level.scheduleUpdate(this, this, 4); + } + + return type; + } + + return 0; + } + + public void dispense() { + BlockEntity blockEntity = this.level.getBlockEntity(this); + + if (!(blockEntity instanceof BlockEntityDispenser)) { + return; + } + + Random rand = ThreadLocalRandom.current(); + int r = 1; + int slot = -1; + Item target = null; + + Inventory inv = ((BlockEntityDispenser) blockEntity).getInventory(); + for (Entry entry : inv.getContents().entrySet()) { + Item item = entry.getValue(); + + if (!item.isNull() && rand.nextInt(r++) == 0) { + target = item; + slot = entry.getKey(); + } + } + + if (target == null) { +// this.level.addLevelSoundEvent(this); //TODO: sound + return; + } + Item origin = target; + target = target.clone(); + + DispenseBehavior behavior = DispenseBehaviorRegister.getBehavior(target.getId()); + Item result = behavior.dispense(this, getBlockFace(), target); + + if (result == null) { + target.count--; + inv.setItem(slot, target); + } else { + if (result.getId() != origin.getId() || result.getDamage() != origin.getDamage()) { + Item[] fit = inv.addItem(result); + + if (fit.length > 0) { + for (Item drop : fit) { + this.level.dropItem(this, drop); + } + } + } else { + inv.setItem(slot, result); + } + } + } + @Override public boolean canHarvestWithHand() { return false; } public Vector3 getDispensePosition() { - BlockFace facing = getFacing(); - double x = this.getX() + 0.7 * facing.getXOffset(); - double y = this.getY() + 0.7 * facing.getYOffset(); - double z = this.getZ() + 0.7 * facing.getZOffset(); - return new Vector3(x, y, z); + BlockFace facing = getBlockFace(); + return this.add( + 0.5 + 0.7 * facing.getXOffset(), + 0.5 + 0.7 * facing.getYOffset(), + 0.5 + 0.7 * facing.getZOffset() + ); } @Override public BlockFace getBlockFace() { - return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07); + return BlockFace.fromIndex(this.getDamage() & 0x07); } } diff --git a/src/main/java/cn/nukkit/block/BlockLever.java b/src/main/java/cn/nukkit/block/BlockLever.java index 6cfaa411e63..32a858fadd5 100644 --- a/src/main/java/cn/nukkit/block/BlockLever.java +++ b/src/main/java/cn/nukkit/block/BlockLever.java @@ -66,13 +66,16 @@ public boolean onActivate(Item item, Player player) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, isPowerOn() ? 15 : 0, isPowerOn() ? 0 : 15)); this.setDamage(this.getDamage() ^ 0x08); - this.getLevel().setBlock(this, this, false, true); + this.getLevel().setBlock(this, this, false, false); this.getLevel().addSound(this, Sound.RANDOM_CLICK); //TODO: correct pitch LeverOrientation orientation = LeverOrientation.byMetadata(this.isPowerOn() ? this.getDamage() ^ 0x08 : this.getDamage()); BlockFace face = orientation.getFacing(); - //this.level.updateAroundRedstone(this, null); - this.level.updateAroundRedstone(this.getLocation().getSide(face.getOpposite()), isPowerOn() ? face : null); + + Block target = this.getSide(face.getOpposite()); + target.onUpdate(Level.BLOCK_UPDATE_REDSTONE); + + this.level.updateAroundRedstone(target.getLocation(), isPowerOn() ? face : null); return true; } diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 943b22f7a9d..3fc7ed0616d 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -65,7 +65,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl .putInt("z", (int) this.z) .putBoolean("Sticky", this.sticky); - BlockEntityPistonArm be = new BlockEntityPistonArm(this.level.getChunk((int) this.x >> 4, (int) this.z >> 4), nbt); + BlockEntityPistonArm be = new BlockEntityPistonArm(this.level.getChunk(getChunkX(), getChunkZ()), nbt); //this.checkState(); return true; diff --git a/src/main/java/cn/nukkit/block/BlockSapling.java b/src/main/java/cn/nukkit/block/BlockSapling.java index 1446962665d..d3d785ae257 100644 --- a/src/main/java/cn/nukkit/block/BlockSapling.java +++ b/src/main/java/cn/nukkit/block/BlockSapling.java @@ -10,6 +10,7 @@ import cn.nukkit.math.NukkitRandom; import cn.nukkit.math.Vector3; import cn.nukkit.utils.BlockColor; + import java.util.concurrent.ThreadLocalRandom; /** @@ -74,7 +75,7 @@ public boolean canBeActivated() { public boolean onActivate(Item item, Player player) { if (item.getId() == Item.DYE && item.getDamage() == 0x0F) { //BoneMeal - if ((player.gamemode & 0x01) == 0) { + if (player != null && (player.gamemode & 0x01) == 0) { item.count--; } diff --git a/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java b/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java index 4f9646a1ea8..cd88e611db3 100644 --- a/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java +++ b/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java @@ -8,6 +8,8 @@ import cn.nukkit.Player; import cn.nukkit.blockentity.BlockEntity; import cn.nukkit.blockentity.BlockEntityShulkerBox; +import cn.nukkit.inventory.ContainerInventory; +import cn.nukkit.inventory.InventoryHolder; import cn.nukkit.inventory.ShulkerBoxInventory; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; @@ -17,7 +19,6 @@ import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.utils.BlockColor; -import cn.nukkit.utils.DyeColor; /** * @@ -156,6 +157,22 @@ public boolean onActivate(Item item, Player player) { return true; } + @Override + public boolean hasComparatorInputOverride() { + return true; + } + + @Override + public int getComparatorInputOverride() { + BlockEntity be = this.getLevel().getBlockEntity(this); + + if (!(be instanceof InventoryHolder)) { + return 0; + } + + return ContainerInventory.calculateRedstone(((InventoryHolder) be).getInventory()); + } + @Override public BlockColor getColor() { return BlockColor.PURPLE_BLOCK_COLOR; diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java index cbc97c453e7..41a2587d9e7 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java @@ -42,6 +42,7 @@ public abstract class BlockEntity extends Position { public static final String JUKEBOX = "Jukebox"; public static final String SHULKER_BOX = "ShulkerBox"; public static final String BANNER = "Banner"; + public static final String DISPENSER = "Dispenser"; public static long count = 1; @@ -210,7 +211,7 @@ public boolean isMovable() { } public static CompoundTag getDefaultCompound(Vector3 pos, String id) { - return new CompoundTag("") + return new CompoundTag() .putString("id", id) .putInt("x", pos.getFloorX()) .putInt("y", pos.getFloorY()) diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java b/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java new file mode 100644 index 00000000000..1df276eb105 --- /dev/null +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java @@ -0,0 +1,135 @@ +package cn.nukkit.blockentity; + +import cn.nukkit.block.BlockAir; +import cn.nukkit.block.BlockID; +import cn.nukkit.inventory.DispenserInventory; +import cn.nukkit.inventory.InventoryHolder; +import cn.nukkit.item.Item; +import cn.nukkit.item.ItemBlock; +import cn.nukkit.level.format.FullChunk; +import cn.nukkit.nbt.NBTIO; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.ListTag; + +public class BlockEntityDispenser extends BlockEntitySpawnable implements BlockEntityContainer, BlockEntityNameable, InventoryHolder { + + protected DispenserInventory inventory; + + public BlockEntityDispenser(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + } + + @Override + protected void initBlockEntity() { + this.inventory = new DispenserInventory(this); + + if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) { + this.namedTag.putList(new ListTag("Items")); + } + + for (int i = 0; i < this.getSize(); i++) { + this.inventory.setItem(i, this.getItem(i)); + } + + super.initBlockEntity(); + } + + @Override + public int getSize() { + return 9; + } + + protected int getSlotIndex(int index) { + ListTag list = this.namedTag.getList("Items", CompoundTag.class); + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getByte("Slot") == index) { + return i; + } + } + + return -1; + } + + @Override + public Item getItem(int index) { + int i = this.getSlotIndex(index); + if (i < 0) { + return new ItemBlock(new BlockAir(), 0, 0); + } else { + CompoundTag data = (CompoundTag) this.namedTag.getList("Items").get(i); + return NBTIO.getItemHelper(data); + } + } + + @Override + public void setItem(int index, Item item) { + int i = this.getSlotIndex(index); + + CompoundTag d = NBTIO.putItemHelper(item, index); + + if (item.getId() == Item.AIR || item.getCount() <= 0) { + if (i >= 0) { + this.namedTag.getList("Items").getAll().remove(i); + } + } else if (i < 0) { + (this.namedTag.getList("Items", CompoundTag.class)).add(d); + } else { + (this.namedTag.getList("Items", CompoundTag.class)).add(i, d); + } + } + + @Override + public DispenserInventory getInventory() { + return inventory; + } + + @Override + public CompoundTag getSpawnCompound() { + CompoundTag c = new CompoundTag() + .putString("id", BlockEntity.DISPENSER) + .putInt("x", (int) this.x) + .putInt("y", (int) this.y) + .putInt("z", (int) this.z); + + if (this.hasName()) { + c.put("CustomName", this.namedTag.get("CustomName")); + } + + return c; + } + + @Override + public void saveNBT() { + this.namedTag.putList(new ListTag("Items")); + for (int index = 0; index < this.getSize(); index++) { + this.setItem(index, this.inventory.getItem(index)); + } + + super.saveNBT(); + } + + @Override + public boolean isBlockEntityValid() { + return this.getLevelBlock().getId() == BlockID.DISPENSER; + } + + @Override + public String getName() { + return this.hasName() ? this.namedTag.getString("CustomName") : "Dispenser"; + } + + @Override + public boolean hasName() { + return this.namedTag.contains("CustomName"); + } + + @Override + public void setName(String name) { + if (name == null || name.equals("")) { + this.namedTag.remove("CustomName"); + return; + } + + this.namedTag.putString("CustomName", name); + } +} diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index e8772ed2466..4fb4a2d2658 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -13,14 +13,14 @@ /** * @author CreeperFace */ -public class BlockEntityPistonArm extends BlockEntity { +public class BlockEntityPistonArm extends BlockEntitySpawnable { - public float progress = 1.0F; + public float progress; public float lastProgress = 1.0F; public BlockFace facing; public boolean extending = false; public boolean sticky = false; - public byte state = 1; + public byte state; public byte newState = 1; public Vector3 attachedBlock = null; public boolean isMovable = true; @@ -96,6 +96,12 @@ public void saveNBT() { } public CompoundTag getSpawnCompound() { - return (new CompoundTag()).putString("id", "PistonArm").putInt("x", (int) this.x).putInt("y", (int) this.y).putInt("z", (int) this.z); + return new CompoundTag() + .putString("id", "PistonArm") + .putInt("x", (int) this.x) + .putInt("y", (int) this.y) + .putInt("z", (int) this.z) + .putFloat("Progress", this.progress) + .putByte("State", this.state); } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/dispenser/BoatDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/BoatDispenseBehavior.java new file mode 100644 index 00000000000..b4eb9aeb5fb --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/BoatDispenseBehavior.java @@ -0,0 +1,39 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.block.BlockID; +import cn.nukkit.block.BlockWater; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityBoat; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; +import cn.nukkit.math.Vector3; + +public class BoatDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Vector3 pos = block.getSide(face).multiply(1.125); + + Block target = block.getSide(face); + + if (target instanceof BlockWater) { + pos.y += 1; + } else if (target.getId() != BlockID.AIR || !(target.down() instanceof BlockWater)) { + return super.dispense(block, face, item); + } + + pos = target.getLocation().setYaw(face.getHorizontalAngle()); + + EntityBoat boat = new EntityBoat(block.level.getChunk(target.getChunkX(), target.getChunkZ()), + Entity.getDefaultNBT(pos) + .putByte("woodID", item.getDamage()) + ); + + boat.spawnToAll(); + + return null; + } + +} diff --git a/src/main/java/cn/nukkit/dispenser/BucketDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/BucketDispenseBehavior.java index 73cf483bd85..58a1c214804 100644 --- a/src/main/java/cn/nukkit/dispenser/BucketDispenseBehavior.java +++ b/src/main/java/cn/nukkit/dispenser/BucketDispenseBehavior.java @@ -1,15 +1,37 @@ package cn.nukkit.dispenser; +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockAir; import cn.nukkit.block.BlockDispenser; +import cn.nukkit.block.BlockLiquid; import cn.nukkit.item.Item; +import cn.nukkit.item.ItemBucket; +import cn.nukkit.item.ItemID; +import cn.nukkit.math.BlockFace; /** * @author CreeperFace */ -public class BucketDispenseBehavior implements DispenseBehavior { +public class BucketDispenseBehavior extends DefaultDispenseBehavior { @Override - public void dispense(BlockDispenser block, Item stack) { + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Block target = block.getSide(face); + if (item.getDamage() > 0) { + if (target.canBeFlowedInto()) { + Block replace = Block.get(ItemBucket.getDamageByTarget(item.getDamage())); + + if (replace instanceof BlockLiquid) { + block.level.setBlock(target, replace); + return Item.get(ItemID.BUCKET); + } + } + } else if (target instanceof BlockLiquid && target.getDamage() == 0) { + target.level.setBlock(target, new BlockAir()); + return new ItemBucket(ItemBucket.getDamageByTarget(target.getId())); + } + + return super.dispense(block, face, item); } } diff --git a/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java index c13002af48e..f88956d80e1 100644 --- a/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java +++ b/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java @@ -3,15 +3,45 @@ import cn.nukkit.block.BlockDispenser; import cn.nukkit.item.Item; import cn.nukkit.math.BlockFace; +import cn.nukkit.math.BlockFace.Axis; +import cn.nukkit.math.Vector3; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; /** * @author CreeperFace */ public class DefaultDispenseBehavior implements DispenseBehavior { + public boolean success = true; + @Override - public void dispense(BlockDispenser block, Item stack) { + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Vector3 dispensePos = block.getDispensePosition(); + + if (face.getAxis() == Axis.Y) { + dispensePos.y -= 0.125; + } else { + dispensePos.y -= 0.15625; + } + + Random rand = ThreadLocalRandom.current(); + Vector3 motion = new Vector3(); + + double offset = rand.nextDouble() * 0.1 + 0.2; + + motion.x = face.getXOffset() * offset; + motion.y = 0.20000000298023224; + motion.z = face.getZOffset() * offset; + + motion.x += rand.nextGaussian() * 0.007499999832361937 * 6; + motion.y += rand.nextGaussian() * 0.007499999832361937 * 6; + motion.z += rand.nextGaussian() * 0.007499999832361937 * 6; + + block.level.dropItem(dispensePos, item.clone().setCount(1), motion); + return null; } private int getParticleMetadataForFace(BlockFace face) { diff --git a/src/main/java/cn/nukkit/dispenser/DispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/DispenseBehavior.java index 9019b20ff16..ebb94f2c36a 100644 --- a/src/main/java/cn/nukkit/dispenser/DispenseBehavior.java +++ b/src/main/java/cn/nukkit/dispenser/DispenseBehavior.java @@ -2,12 +2,13 @@ import cn.nukkit.block.BlockDispenser; import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; /** * @author CreeperFace */ public interface DispenseBehavior { - void dispense(BlockDispenser block, Item item); + Item dispense(BlockDispenser block, BlockFace face, Item item); } diff --git a/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java b/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java index 9cf02b8558b..049264c3013 100644 --- a/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java +++ b/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java @@ -1,12 +1,15 @@ package cn.nukkit.dispenser; +import cn.nukkit.block.BlockID; +import cn.nukkit.item.ItemID; + import java.util.HashMap; import java.util.Map; /** * @author CreeperFace */ -public class DispenseBehaviorRegister { +public final class DispenseBehaviorRegister { private static final Map behaviors = new HashMap<>(); private static DispenseBehavior defaultBehavior = new DefaultDispenseBehavior(); @@ -22,4 +25,59 @@ public static DispenseBehavior getBehavior(int id) { public static void removeDispenseBehavior(int id) { behaviors.remove(id); } + + public static void init() { + registerBehavior(ItemID.BOAT, new BoatDispenseBehavior()); + registerBehavior(ItemID.BUCKET, new BucketDispenseBehavior()); + registerBehavior(ItemID.DYE, new DyeDispenseBehavior()); + registerBehavior(ItemID.FIREWORKS, new FireworksDispenseBehavior()); + registerBehavior(ItemID.FLINT_AND_STEEL, new FlintAndSteelDispenseBehavior()); + registerBehavior(BlockID.SHULKER_BOX, new ShulkerBoxDispenseBehavior()); + registerBehavior(ItemID.SPAWN_EGG, new SpawnEggDispenseBehavior()); + registerBehavior(BlockID.TNT, new TNTDispenseBehavior()); + registerBehavior(ItemID.ARROW, new ProjectileDispenseBehavior("Arrow") { + @Override + protected double getMotion() { + return super.getMotion() * 2; + } + }); + //TODO: tipped arrow + //TODO: spectral arrow + registerBehavior(ItemID.EGG, new ProjectileDispenseBehavior("Egg")); + registerBehavior(ItemID.SNOWBALL, new ProjectileDispenseBehavior("Snowball")); + registerBehavior(ItemID.EXPERIENCE_BOTTLE, new ProjectileDispenseBehavior("ThrownExpBottle") { + @Override + protected float getAccuracy() { + return super.getAccuracy() * 0.5f; + } + + @Override + protected double getMotion() { + return super.getMotion() * 1.25; + } + }); + registerBehavior(ItemID.SPLASH_POTION, new ProjectileDispenseBehavior("ThrownPotion") { + @Override + protected float getAccuracy() { + return super.getAccuracy() * 0.5f; + } + + @Override + protected double getMotion() { + return super.getMotion() * 1.25; + } + }); +// registerBehavior(ItemID.LINGERING_POTION, new ProjectileDispenseBehavior("LingeringPotion")); //TODO + registerBehavior(ItemID.TRIDENT, new ProjectileDispenseBehavior("ThrownTrident") { + @Override + protected float getAccuracy() { + return super.getAccuracy() * 0.5f; + } + + @Override + protected double getMotion() { + return super.getMotion() * 1.25; + } + }); + } } diff --git a/src/main/java/cn/nukkit/dispenser/DyeDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/DyeDispenseBehavior.java new file mode 100644 index 00000000000..c065af3b646 --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/DyeDispenseBehavior.java @@ -0,0 +1,28 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.*; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; +import cn.nukkit.utils.DyeColor; + +public class DyeDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Block target = block.getSide(face); + + if (DyeColor.getByDyeData(item.getDamage()) == DyeColor.WHITE) { + if (target instanceof BlockCrops || target instanceof BlockSapling || target instanceof BlockTallGrass + || target instanceof BlockDoublePlant || target instanceof BlockMushroom) { + target.onActivate(item); + + } else { + this.success = false; + } + + return null; + } + + return super.dispense(block, face, item); + } +} diff --git a/src/main/java/cn/nukkit/dispenser/EmptyBucketDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/EmptyBucketDispenseBehavior.java index f01f4e1619f..750213c35ae 100644 --- a/src/main/java/cn/nukkit/dispenser/EmptyBucketDispenseBehavior.java +++ b/src/main/java/cn/nukkit/dispenser/EmptyBucketDispenseBehavior.java @@ -1,15 +1,28 @@ package cn.nukkit.dispenser; +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockAir; import cn.nukkit.block.BlockDispenser; +import cn.nukkit.block.BlockLiquid; import cn.nukkit.item.Item; +import cn.nukkit.item.ItemBucket; +import cn.nukkit.math.BlockFace; /** * @author CreeperFace */ -public class EmptyBucketDispenseBehavior implements DispenseBehavior { +public class EmptyBucketDispenseBehavior extends DefaultDispenseBehavior { @Override - public void dispense(BlockDispenser block, Item item) { + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Block target = block.getSide(face); + if (target instanceof BlockLiquid && target.getDamage() == 0) { + target.level.setBlock(target, new BlockAir()); + return new ItemBucket(ItemBucket.getDamageByTarget(target.getId())); + } + + super.dispense(block, face, item); + return null; } } diff --git a/src/main/java/cn/nukkit/dispenser/FireChargeDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/FireChargeDispenseBehavior.java new file mode 100644 index 00000000000..6edfaa09655 --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/FireChargeDispenseBehavior.java @@ -0,0 +1,14 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; + +public class FireChargeDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + //TODO: firecharge + return null; + } +} diff --git a/src/main/java/cn/nukkit/dispenser/FireworksDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/FireworksDispenseBehavior.java new file mode 100644 index 00000000000..d1133ba15cb --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/FireworksDispenseBehavior.java @@ -0,0 +1,22 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityFirework; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; +import cn.nukkit.math.Vector3; + +public class FireworksDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Vector3 pos = block.getSide(face).add(0, 0.2); + + EntityFirework firework = new EntityFirework(block.level.getChunk(pos.getChunkX(), pos.getChunkZ()), + Entity.getDefaultNBT(pos)); + firework.spawnToAll(); + + return null; + } +} diff --git a/src/main/java/cn/nukkit/dispenser/FlintAndSteelDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/FlintAndSteelDispenseBehavior.java new file mode 100644 index 00000000000..3b098800d5d --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/FlintAndSteelDispenseBehavior.java @@ -0,0 +1,25 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.block.BlockID; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; + +public class FlintAndSteelDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Block target = block.getSide(face); + + if (target.getId() == BlockID.AIR) { + block.level.setBlock(target, Block.get(BlockID.FIRE)); + } else if (target.getId() == BlockID.TNT) { + target.onActivate(item); + } else { + this.success = false; + } + + return null; + } +} diff --git a/src/main/java/cn/nukkit/dispenser/ProjectileDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/ProjectileDispenseBehavior.java index 52c2006408c..cac498f5793 100644 --- a/src/main/java/cn/nukkit/dispenser/ProjectileDispenseBehavior.java +++ b/src/main/java/cn/nukkit/dispenser/ProjectileDispenseBehavior.java @@ -2,8 +2,8 @@ import cn.nukkit.block.BlockDispenser; import cn.nukkit.entity.Entity; +import cn.nukkit.entity.projectile.EntityProjectile; import cn.nukkit.item.Item; -import cn.nukkit.level.Position; import cn.nukkit.math.BlockFace; import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; @@ -11,33 +11,46 @@ /** * @author CreeperFace */ -public class ProjectileDispenseBehavior implements DispenseBehavior { +public class ProjectileDispenseBehavior extends DefaultDispenseBehavior { - private String entityType; - - public ProjectileDispenseBehavior() { - - } + private final String entityType; public ProjectileDispenseBehavior(String entity) { this.entityType = entity; } @Override - public void dispense(BlockDispenser source, Item item) { - Position dispensePos = Position.fromObject(source.getDispensePosition(), source.getLevel()); + public Item dispense(BlockDispenser source, BlockFace face, Item item) { + Vector3 dispensePos = source.getDispensePosition(); + CompoundTag nbt = Entity.getDefaultNBT(dispensePos); this.correctNBT(nbt); - BlockFace face = source.getFacing(); + Entity projectile = Entity.createEntity(getEntityType(), source.level.getChunk(dispensePos.getChunkX(), dispensePos.getChunkZ()), nbt); - Entity projectile = Entity.createEntity(getEntityType(), dispensePos.getLevel().getChunk(dispensePos.getFloorX(), dispensePos.getFloorZ()), nbt); - if (projectile == null) { - return; + if (!(projectile instanceof EntityProjectile)) { + return super.dispense(source, face, item); } - projectile.setMotion(new Vector3(face.getXOffset(), face.getYOffset() + 0.1f, face.getZOffset()).multiply(6)); + Vector3 motion = new Vector3(face.getXOffset(), face.getYOffset() + 0.1f, face.getZOffset()) + .normalize(); + + projectile.setMotion(motion); + ((EntityProjectile) projectile).inaccurate(getAccuracy()); + projectile.setMotion(projectile.getMotion().multiply(getMotion())); + + ((EntityProjectile) projectile).updateRotation(); + projectile.spawnToAll(); + return null; + } + + protected double getMotion() { + return 1.1; + } + + protected float getAccuracy() { + return 6; } protected String getEntityType() { diff --git a/src/main/java/cn/nukkit/dispenser/PumpkinDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/PumpkinDispenseBehavior.java new file mode 100644 index 00000000000..0803372fa92 --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/PumpkinDispenseBehavior.java @@ -0,0 +1,17 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; + +public class PumpkinDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Block target = block.getSide(face); + + //TODO: snowman / golem + return null; + } +} diff --git a/src/main/java/cn/nukkit/dispenser/ShulkerBoxDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/ShulkerBoxDispenseBehavior.java new file mode 100644 index 00000000000..550bda8d5ca --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/ShulkerBoxDispenseBehavior.java @@ -0,0 +1,46 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.block.BlockID; +import cn.nukkit.block.BlockShulkerBox; +import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityShulkerBox; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; +import cn.nukkit.nbt.tag.CompoundTag; + +public class ShulkerBoxDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Block shulkerBox = new BlockShulkerBox(); + Block target = block.getSide(face); + + this.success = block.level.getCollidingEntities(shulkerBox.getBoundingBox()).length == 0; + + if (this.success) { + BlockFace shulkerBoxFace = target.down().getId() == BlockID.AIR ? face : BlockFace.UP; + + CompoundTag nbt = BlockEntity.getDefaultCompound(target, BlockEntity.SHULKER_BOX); + nbt.putByte("facing", shulkerBoxFace.getIndex()); + + if (item.hasCustomName()) { + nbt.putString("CustomName", item.getCustomName()); + } + + CompoundTag tag = item.getNamedTag(); + + if (tag != null) { + if (tag.contains("Items")) { + nbt.putList(tag.getList("Items")); + } + } + + new BlockEntityShulkerBox(block.level.getChunk(target.getChunkX(), target.getChunkZ()), nbt); + block.level.updateComparatorOutputLevel(target); + } + + return null; + } +} diff --git a/src/main/java/cn/nukkit/dispenser/SpawnEggDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/SpawnEggDispenseBehavior.java new file mode 100644 index 00000000000..04a36857f72 --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/SpawnEggDispenseBehavior.java @@ -0,0 +1,32 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.EntityLiving; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; +import cn.nukkit.math.Vector3; + +public class SpawnEggDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Vector3 pos = block.getSide(face).add(0.5, 0.7, 0.5); + + Entity entity = Entity.createEntity(item.getDamage(), block.level.getChunk(pos.getChunkX(), pos.getChunkZ()), + Entity.getDefaultNBT(pos)); + + this.success = entity != null; + + if (this.success) { + if (item.hasCustomName() && entity instanceof EntityLiving) { + entity.setNameTag(item.getCustomName()); + } + + entity.spawnToAll(); + return null; + } + + return super.dispense(block, face, item); + } +} diff --git a/src/main/java/cn/nukkit/dispenser/TNTDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/TNTDispenseBehavior.java new file mode 100644 index 00000000000..bbdb9aaf266 --- /dev/null +++ b/src/main/java/cn/nukkit/dispenser/TNTDispenseBehavior.java @@ -0,0 +1,23 @@ +package cn.nukkit.dispenser; + +import cn.nukkit.block.BlockDispenser; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityPrimedTNT; +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; +import cn.nukkit.math.Vector3; + +public class TNTDispenseBehavior extends DefaultDispenseBehavior { + + @Override + public Item dispense(BlockDispenser block, BlockFace face, Item item) { + Vector3 pos = block.getSide(face).add(0.5, 0, 0.5); + + EntityPrimedTNT tnt = new EntityPrimedTNT(block.level.getChunk(pos.getChunkX(), pos.getChunkZ()), + Entity.getDefaultNBT(pos)); + tnt.spawnToAll(); + + return null; + } + +} diff --git a/src/main/java/cn/nukkit/entity/EntityHumanType.java b/src/main/java/cn/nukkit/entity/EntityHumanType.java index 687b80ba16b..a5351bae22a 100644 --- a/src/main/java/cn/nukkit/entity/EntityHumanType.java +++ b/src/main/java/cn/nukkit/entity/EntityHumanType.java @@ -123,7 +123,7 @@ public Item[] getDrops() { @Override public boolean attack(EntityDamageEvent source) { - if (!this.isAlive()) { + if (!this.isAlive() || closed) { return false; } diff --git a/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java b/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java index 8660ef827a6..890b11d3c2a 100644 --- a/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java +++ b/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java @@ -51,9 +51,6 @@ public float getDrag() { return 0.01f; } - protected float gravity = 0.05f; - protected float drag = 0.01f; - public EntityArrow(FullChunk chunk, CompoundTag nbt) { this(chunk, nbt, null); } diff --git a/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java b/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java index 5826e800819..daf47fcfa4c 100644 --- a/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java +++ b/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java @@ -13,6 +13,9 @@ import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + /** * author: MagicDroidX * Nukkit Project @@ -124,6 +127,8 @@ public boolean onUpdate(int currentTick) { if (!this.isCollided) { this.motionY -= this.getGravity(); + this.motionX *= 1 - this.getDrag(); + this.motionZ *= 1 - this.getDrag(); } Vector3 moveVector = new Vector3(this.x + this.motionX, this.y + this.motionY, this.z + this.motionZ); @@ -136,7 +141,7 @@ public boolean onUpdate(int currentTick) { for (Entity entity : list) { if (/*!entity.canCollideWith(this) or */ (entity == this.shootingEntity && this.ticksLived < 5) - ) { + ) { continue; } @@ -182,9 +187,7 @@ public boolean onUpdate(int currentTick) { } if (!this.hadCollision || Math.abs(this.motionX) > 0.00001 || Math.abs(this.motionY) > 0.00001 || Math.abs(this.motionZ) > 0.00001) { - double f = Math.sqrt((this.motionX * this.motionX) + (this.motionZ * this.motionZ)); - this.yaw = Math.atan2(this.motionX, this.motionZ) * 180 / Math.PI; - this.pitch = Math.atan2(this.motionY, f) * 180 / Math.PI; + updateRotation(); hasUpdate = true; } @@ -194,4 +197,18 @@ public boolean onUpdate(int currentTick) { return hasUpdate; } + + public void updateRotation() { + double f = Math.sqrt((this.motionX * this.motionX) + (this.motionZ * this.motionZ)); + this.yaw = Math.atan2(this.motionX, this.motionZ) * 180 / Math.PI; + this.pitch = Math.atan2(this.motionY, f) * 180 / Math.PI; + } + + public void inaccurate(float modifier) { + Random rand = ThreadLocalRandom.current(); + + this.motionX += rand.nextGaussian() * 0.007499999832361937 * modifier; + this.motionY += rand.nextGaussian() * 0.007499999832361937 * modifier; + this.motionZ += rand.nextGaussian() * 0.007499999832361937 * modifier; + } } diff --git a/src/main/java/cn/nukkit/inventory/DispenserInventory.java b/src/main/java/cn/nukkit/inventory/DispenserInventory.java new file mode 100644 index 00000000000..55f651447fd --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/DispenserInventory.java @@ -0,0 +1,15 @@ +package cn.nukkit.inventory; + +import cn.nukkit.blockentity.BlockEntityDispenser; + +public class DispenserInventory extends ContainerInventory { + + public DispenserInventory(BlockEntityDispenser blockEntity) { + super(blockEntity, InventoryType.DISPENSER); + } + + @Override + public BlockEntityDispenser getHolder() { + return (BlockEntityDispenser) super.getHolder(); + } +} diff --git a/src/main/java/cn/nukkit/inventory/InventoryType.java b/src/main/java/cn/nukkit/inventory/InventoryType.java index bf79919c02b..4e843faf28d 100644 --- a/src/main/java/cn/nukkit/inventory/InventoryType.java +++ b/src/main/java/cn/nukkit/inventory/InventoryType.java @@ -15,7 +15,7 @@ public enum InventoryType { BREWING_STAND(5, "Brewing", 4), //1 INPUT, 3 POTION, 1 fuel ANVIL(3, "Anvil", 5), //2 INPUT, 1 OUTPUT ENCHANT_TABLE(2, "Enchant", 3), //1 INPUT/OUTPUT, 1 LAPIS - DISPENSER(0, "Dispenser", 6), //9 CONTAINER + DISPENSER(9, "Dispenser", 6), //9 CONTAINER DROPPER(9, "Dropper", 7), //9 CONTAINER HOPPER(5, "Hopper", 8), //5 CONTAINER CURSOR(1, "Cursor", -1), diff --git a/src/main/java/cn/nukkit/item/Item.java b/src/main/java/cn/nukkit/item/Item.java index 55170777d21..2d8669e8760 100644 --- a/src/main/java/cn/nukkit/item/Item.java +++ b/src/main/java/cn/nukkit/item/Item.java @@ -811,8 +811,9 @@ public int getCount() { return count; } - public void setCount(int count) { + public Item setCount(int count) { this.count = count; + return this; } public boolean isNull() { diff --git a/src/main/java/cn/nukkit/item/ItemBucket.java b/src/main/java/cn/nukkit/item/ItemBucket.java index 2cbe2a2d9c7..3462e8a1648 100644 --- a/src/main/java/cn/nukkit/item/ItemBucket.java +++ b/src/main/java/cn/nukkit/item/ItemBucket.java @@ -1,11 +1,7 @@ package cn.nukkit.item; import cn.nukkit.Player; -import cn.nukkit.block.Block; -import cn.nukkit.block.BlockAir; -import cn.nukkit.block.BlockLava; -import cn.nukkit.block.BlockLiquid; -import cn.nukkit.block.BlockWater; +import cn.nukkit.block.*; import cn.nukkit.event.player.PlayerBucketEmptyEvent; import cn.nukkit.event.player.PlayerBucketFillEvent; import cn.nukkit.level.Level; @@ -52,7 +48,7 @@ protected static String getName(int meta) { } } - protected int getDamageByTarget(int target) { + public static int getDamageByTarget(int target) { switch (target) { case 2: case 3: @@ -85,7 +81,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, if (targetBlock instanceof BlockAir) { if (target instanceof BlockLiquid && target.getDamage() == 0) { - Item result = Item.get(BUCKET, this.getDamageByTarget(target.getId()), 1); + Item result = Item.get(BUCKET, getDamageByTarget(target.getId()), 1); PlayerBucketFillEvent ev; player.getServer().getPluginManager().callEvent(ev = new PlayerBucketFillEvent(player, block, face, this, result)); if (!ev.isCancelled()) { diff --git a/src/main/java/cn/nukkit/level/Location.java b/src/main/java/cn/nukkit/level/Location.java index d6f0fc863ad..44a62d9bc9b 100644 --- a/src/main/java/cn/nukkit/level/Location.java +++ b/src/main/java/cn/nukkit/level/Location.java @@ -73,6 +73,16 @@ public double getPitch() { return this.pitch; } + public Location setYaw(double yaw) { + this.yaw = yaw; + return this; + } + + public Location setPitch(double pitch) { + this.pitch = pitch; + return this; + } + @Override public String toString() { return "Location (level=" + (this.isValid() ? this.getLevel().getName() : "null") + ", x=" + this.x + ", y=" + this.y + ", z=" + this.z + ", yaw=" + this.yaw + ", pitch=" + this.pitch + ")"; From 538c819c7651c7670815fdd487de0e4487548f48 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Fri, 16 Aug 2019 11:52:17 +0200 Subject: [PATCH 02/52] Broken pistons implementation --- src/main/java/cn/nukkit/Server.java | 1 + src/main/java/cn/nukkit/block/Block.java | 7 +- src/main/java/cn/nukkit/block/BlockID.java | 2 +- src/main/java/cn/nukkit/block/BlockLever.java | 1 + .../java/cn/nukkit/block/BlockMoving.java | 34 +++ .../java/cn/nukkit/block/BlockPistonBase.java | 197 +++++++++++------- .../java/cn/nukkit/block/BlockPistonHead.java | 14 +- .../cn/nukkit/block/BlockRedstoneDiode.java | 7 + .../cn/nukkit/block/BlockRedstoneWire.java | 13 +- .../cn/nukkit/blockentity/BlockEntity.java | 7 +- .../blockentity/BlockEntityMovingBlock.java | 44 ++-- .../blockentity/BlockEntityPistonArm.java | 170 ++++++++++++--- .../blockentity/BlockEntitySpawnable.java | 22 +- .../cn/nukkit/level/GlobalBlockPalette.java | 16 +- src/main/java/cn/nukkit/level/Level.java | 1 + .../java/cn/nukkit/nbt/tag/CompoundTag.java | 13 +- src/main/java/cn/nukkit/nbt/tag/Tag.java | 4 +- src/main/java/cn/nukkit/network/Network.java | 5 +- 18 files changed, 404 insertions(+), 154 deletions(-) create mode 100644 src/main/java/cn/nukkit/block/BlockMoving.java diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index 631d6e1954b..b5db3136613 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -2329,6 +2329,7 @@ private void registerBlockEntities() { BlockEntity.registerBlockEntity(BlockEntity.BANNER, BlockEntityBanner.class); BlockEntity.registerBlockEntity(BlockEntity.MUSIC, BlockEntityMusic.class); BlockEntity.registerBlockEntity(BlockEntity.DISPENSER, BlockEntityDispenser.class); + BlockEntity.registerBlockEntity(BlockEntity.MOVING_BLOCK, BlockEntityMovingBlock.class); } public boolean isNetherAllowed() { diff --git a/src/main/java/cn/nukkit/block/Block.java b/src/main/java/cn/nukkit/block/Block.java index f0e38669b50..8246d3eba8a 100644 --- a/src/main/java/cn/nukkit/block/Block.java +++ b/src/main/java/cn/nukkit/block/Block.java @@ -296,8 +296,7 @@ public static void init() { list[GLOWING_OBSIDIAN] = BlockObsidianGlowing.class; //246 //list[NETHER_REACTOR] = BlockNetherReactor.class; //247 Should not be removed - //TODO: list[PISTON_EXTENSION] = BlockPistonExtension.class; //250 - + list[MOVING_BLOCK] = BlockMoving.class; //250 list[OBSERVER] = BlockObserver.class; //251 for (int id = 0; id < 256; id++) { @@ -691,6 +690,10 @@ public Block getSide(BlockFace face) { } public Block getSide(BlockFace face, int step) { + if (step == 0) { + return this; + } + if (this.isValid()) { if (step == 1) { return this.getLevel().getBlock((int) x + face.getXOffset(), (int) y + face.getYOffset(), (int) z + face.getZOffset()); diff --git a/src/main/java/cn/nukkit/block/BlockID.java b/src/main/java/cn/nukkit/block/BlockID.java index 9673b523c7d..2df1d9b9a04 100644 --- a/src/main/java/cn/nukkit/block/BlockID.java +++ b/src/main/java/cn/nukkit/block/BlockID.java @@ -302,7 +302,7 @@ public interface BlockID { int GLOWING_OBSIDIAN = 246; int NETHER_REACTOR = 247; //Should not be removed - int PISTON_EXTENSION = 250; + int MOVING_BLOCK = 250; int OBSERVER = 251; } diff --git a/src/main/java/cn/nukkit/block/BlockLever.java b/src/main/java/cn/nukkit/block/BlockLever.java index 32a858fadd5..9556ca13545 100644 --- a/src/main/java/cn/nukkit/block/BlockLever.java +++ b/src/main/java/cn/nukkit/block/BlockLever.java @@ -75,6 +75,7 @@ public boolean onActivate(Item item, Player player) { Block target = this.getSide(face.getOpposite()); target.onUpdate(Level.BLOCK_UPDATE_REDSTONE); + this.level.updateAroundRedstone(this.getLocation(), isPowerOn() ? face.getOpposite() : null); this.level.updateAroundRedstone(target.getLocation(), isPowerOn() ? face : null); return true; } diff --git a/src/main/java/cn/nukkit/block/BlockMoving.java b/src/main/java/cn/nukkit/block/BlockMoving.java new file mode 100644 index 00000000000..c693449632b --- /dev/null +++ b/src/main/java/cn/nukkit/block/BlockMoving.java @@ -0,0 +1,34 @@ +package cn.nukkit.block; + +import cn.nukkit.item.Item; + +public class BlockMoving extends Block { + + public BlockMoving() { + this(0); + } + + public BlockMoving(int meta) { + super(); + } + + @Override + public String getName() { + return "MovingBlock"; + } + + @Override + public int getId() { + return BlockID.MOVING_BLOCK; + } + + @Override + public boolean canBePushed() { + return false; + } + + @Override + public boolean isBreakable(Item item) { + return false; + } +} diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 3fc7ed0616d..64be0c5a547 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -2,10 +2,12 @@ import cn.nukkit.Player; import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityMovingBlock; import cn.nukkit.blockentity.BlockEntityPistonArm; import cn.nukkit.event.block.BlockPistonChangeEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; +import cn.nukkit.level.GlobalBlockPalette; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; import cn.nukkit.math.Vector3; @@ -14,14 +16,16 @@ import cn.nukkit.utils.Faceable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * @author CreeperFace */ public abstract class BlockPistonBase extends BlockSolidMeta implements Faceable { - public boolean sticky; + public boolean sticky = false; public BlockPistonBase() { this(0); @@ -56,18 +60,19 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl } else { this.setDamage(player.getHorizontalFacing().getIndex()); } - this.level.setBlock(block, this, true, false); + this.level.setBlock(block, this, true, true); CompoundTag nbt = new CompoundTag("") .putString("id", BlockEntity.PISTON_ARM) .putInt("x", (int) this.x) .putInt("y", (int) this.y) .putInt("z", (int) this.z) + .putInt("facing", this.getBlockFace().getIndex()) .putBoolean("Sticky", this.sticky); - BlockEntityPistonArm be = new BlockEntityPistonArm(this.level.getChunk(getChunkX(), getChunkZ()), nbt); + new BlockEntityPistonArm(this.level.getChunk(getChunkX(), getChunkZ()), nbt); - //this.checkState(); + this.checkState(null); return true; } @@ -75,34 +80,40 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl public boolean onBreak(Item item) { this.level.setBlock(this, new BlockAir(), true, true); - Block block = this.getSide(getFacing()); + Block block = this.getSide(getBlockFace()); - if (block instanceof BlockPistonHead && ((BlockPistonHead) block).getFacing() == this.getFacing()) { + if (block instanceof BlockPistonHead && ((BlockPistonHead) block).getBlockFace() == this.getBlockFace()) { block.onBreak(item); } return true; } public boolean isExtended() { - BlockFace face = getFacing(); + BlockFace face = getBlockFace(); Block block = getSide(face); - return block instanceof BlockPistonHead && ((BlockPistonHead) block).getFacing() == face; + + return block instanceof BlockPistonHead && ((BlockPistonHead) block).getBlockFace() == face; } @Override public int onUpdate(int type) { - if (type != 6 && type != 1) { + if (type != Level.BLOCK_UPDATE_NORMAL && type != Level.BLOCK_UPDATE_REDSTONE && type != Level.BLOCK_UPDATE_SCHEDULED) { return 0; } else { BlockEntity blockEntity = this.level.getBlockEntity(this); if (blockEntity instanceof BlockEntityPistonArm) { BlockEntityPistonArm arm = (BlockEntityPistonArm) blockEntity; boolean powered = this.isPowered(); + if (arm.powered != powered) { this.level.getServer().getPluginManager().callEvent(new BlockPistonChangeEvent(this, powered ? 0 : 15, powered ? 15 : 0)); - arm.powered = !arm.powered; - if (arm.chunk != null) { - arm.chunk.setChanged(); + + if (checkState(powered)) { + arm.powered = powered; + + if (arm.chunk != null) { + arm.chunk.setChanged(); + } } } } @@ -111,46 +122,46 @@ public int onUpdate(int type) { } } - private void checkState() { - BlockFace facing = getFacing(); - boolean isPowered = this.isPowered(); + private boolean checkState(Boolean isPowered) { + BlockFace facing = getBlockFace(); + if (isPowered == null) { + isPowered = this.isPowered(); + } if (isPowered && !isExtended()) { - if ((new BlocksCalculator(this.level, this, facing, true)).canMove()) { + if ((new BlocksCalculator(true)).canMove()) { if (!this.doMove(true)) { - return; + return false; } this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_PISTON_OUT); - } else { + return true; } } else if (!isPowered && isExtended()) { - //this.level.setBlock() TODO: set piston extension? - if (this.sticky) { - Vector3 pos = this.add(facing.getXOffset() * 2, facing.getYOffset() * 2, facing.getZOffset() * 2); + Vector3 pos = this.add(0).getSide(facing, 2); Block block = this.level.getBlock(pos); - if (block.getId() == AIR) { - this.level.setBlock(this.getLocation().getSide(facing), new BlockAir(), true, true); - } if (canPush(block, facing.getOpposite(), false) && (!(block instanceof BlockFlowable) || block.getId() == PISTON || block.getId() == STICKY_PISTON)) { - this.doMove(false); + if (!this.doMove(false)) { + return false; + } + } else { + return false; } - } else { - this.level.setBlock(getLocation().getSide(facing), new BlockAir(), true, false); + } else if (!this.doMove(false)) { + return false; } this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_PISTON_IN); + return true; } - } - public BlockFace getFacing() { - return BlockFace.fromIndex(this.getDamage()).getOpposite(); + return false; } private boolean isPowered() { - BlockFace face = getFacing(); + BlockFace face = getBlockFace(); for (BlockFace side : BlockFace.values()) { if (side != face && this.level.isSidePowered(this.getLocation().getSide(side), side)) { @@ -174,47 +185,69 @@ private boolean isPowered() { } private boolean doMove(boolean extending) { - Vector3 pos = this.getLocation(); - BlockFace direction = getFacing(); - - if (!extending) { - this.level.setBlock(pos.getSide(direction), new BlockAir(), true, false); - } - - BlocksCalculator calculator = new BlocksCalculator(this.level, this, direction, extending); + BlockFace direction = getBlockFace(); + BlocksCalculator calculator = new BlocksCalculator(extending); if (!calculator.canMove()) { return false; } else { - List blocks = calculator.getBlocksToMove(); + List attached = Collections.emptyList(); - List newBlocks = new ArrayList<>(blocks); + if (this.sticky || extending) { + List destroyBlocks = calculator.getBlocksToDestroy(); + for (int i = destroyBlocks.size() - 1; i >= 0; --i) { + Block block = destroyBlocks.get(i); + this.level.useBreakOn(block, null, null, false); + } - List destroyBlocks = calculator.getBlocksToDestroy(); - BlockFace side = extending ? direction : direction.getOpposite(); + List newBlocks = calculator.getBlocksToMove(); + attached = newBlocks.stream().map(b -> b.add(0)).collect(Collectors.toList()); - for (int i = destroyBlocks.size() - 1; i >= 0; --i) { - Block block = destroyBlocks.get(i); - this.level.useBreakOn(block); - } + BlockFace side = extending ? direction : direction.getOpposite(); - for (int i = blocks.size() - 1; i >= 0; --i) { - Block block = blocks.get(i); - this.level.setBlock(block, new BlockAir()); - Vector3 newPos = block.getLocation().getSide(side); + for (Block newBlock : newBlocks) { + Vector3 oldPos = newBlock.add(0); + newBlock.position(newBlock.add(0).getSide(side)); - //TODO: change this to block entity - this.level.setBlock(newPos, newBlocks.get(i)); - } + BlockEntity blockEntity = this.level.getBlockEntity(oldPos); - Vector3 pistonHead = pos.getSide(direction); + this.level.setBlock(newBlock, Block.get(BlockID.MOVING_BLOCK)); - if (extending) { - //extension block entity + CompoundTag nbt = BlockEntity.getDefaultCompound(newBlock, BlockEntity.MOVING_BLOCK) + .putInt("pistonPosX", this.getFloorX()) + .putInt("pistonPosY", this.getFloorY()) + .putInt("pistonPosZ", this.getFloorZ()) + .putCompound("movingBlock", new CompoundTag() + .putInt("id", newBlock.getId()) //only for nukkit purpose + .putInt("meta", newBlock.getDamage()) //only for nukkit purpose + .putShort("val", newBlock.getDamage()) + .putString("name", GlobalBlockPalette.getName(newBlock.getId(), newBlock.getDamage())) + ); + + if (blockEntity != null) { + blockEntity.saveNBT(); + + CompoundTag t = new CompoundTag(blockEntity.namedTag.getTags()); + t.print(System.out); + + nbt.putCompound("movingEntity", t); + blockEntity.close(); + } + + new BlockEntityMovingBlock(this.level.getChunk(newBlock.getChunkX(), newBlock.getChunkZ()), nbt); - this.level.setBlock(pistonHead, new BlockPistonHead(this.getDamage())); + if (this.level.getBlockIdAt(oldPos.getFloorX(), oldPos.getFloorY(), oldPos.getFloorZ()) != BlockID.MOVING_BLOCK) { + this.level.setBlock(oldPos, Block.get(BlockID.AIR)); + } + } } + if (extending) { + this.level.setBlock(this.getSide(direction), new BlockPistonHead(this.getDamage())); + } + + BlockEntityPistonArm blockEntity = (BlockEntityPistonArm) this.level.getBlockEntity(this); + blockEntity.move(extending, attached); return true; } } @@ -222,38 +255,42 @@ private boolean doMove(boolean extending) { public static boolean canPush(Block block, BlockFace face, boolean destroyBlocks) { if (block.canBePushed() && block.getY() >= 0 && (face != BlockFace.DOWN || block.getY() != 0) && block.getY() <= 255 && (face != BlockFace.UP || block.getY() != 255)) { - if (!(block instanceof BlockPistonBase)) { - if (block instanceof BlockFlowable) { - return destroyBlocks; - } - } else return !((BlockPistonBase) block).isExtended(); - return true; + if (block instanceof BlockFlowable) { + return destroyBlocks; + } + + BlockEntity be = block.level.getBlockEntity(block); + return be == null || be.isMovable(); } - return false; + return false; } public class BlocksCalculator { - private final Level level; private final Vector3 pistonPos; + private Vector3 armPos; private final Block blockToMove; private final BlockFace moveDirection; private final List toMove = new ArrayList<>(); private final List toDestroy = new ArrayList<>(); - public BlocksCalculator(Level level, Block pos, BlockFace facing, boolean extending) { - this.level = level; - this.pistonPos = pos.getLocation(); + public BlocksCalculator(boolean extending) { + this.pistonPos = getLocation(); + + BlockFace face = getBlockFace(); + if (!extending) { + this.armPos = pistonPos.getSide(face); + } if (extending) { - this.moveDirection = facing; - this.blockToMove = pos.getSide(facing); + this.moveDirection = face; + this.blockToMove = getSide(face); } else { - this.moveDirection = facing.getOpposite(); - this.blockToMove = pos.getSide(facing, 2); + this.moveDirection = face.getOpposite(); + this.blockToMove = getSide(face, 2); } } @@ -272,7 +309,7 @@ public boolean canMove() { } else if (!this.addBlockLine(this.blockToMove)) { return false; } else { - for (Block b : this.toMove) { + for (Block b : new ArrayList<>(this.toMove)) { if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) { return false; } @@ -315,15 +352,15 @@ private boolean addBlockLine(Block origin) { int blockCount = 0; - for (int step = count - 1; step >= 0; --step) { - this.toMove.add(block.getSide(this.moveDirection.getOpposite(), step)); + for (int step = count - 1; step >= 0; step--) { + this.toMove.add(origin.getSide(this.moveDirection.getOpposite(), step)); ++blockCount; } int steps = 1; while (true) { - Block nextBlock = block.getSide(this.moveDirection, steps); + Block nextBlock = origin.getSide(this.moveDirection, steps); int index = this.toMove.indexOf(nextBlock); if (index > -1) { @@ -340,7 +377,7 @@ private boolean addBlockLine(Block origin) { return true; } - if (nextBlock.getId() == AIR) { + if (nextBlock.getId() == AIR || nextBlock.equals(armPos)) { return true; } @@ -401,6 +438,8 @@ public Item toItem() { @Override public BlockFace getBlockFace() { - return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07); + BlockFace face = BlockFace.fromIndex(this.getDamage()); + + return face.getHorizontalIndex() >= 0 ? face.getOpposite() : face; } } diff --git a/src/main/java/cn/nukkit/block/BlockPistonHead.java b/src/main/java/cn/nukkit/block/BlockPistonHead.java index 444ade01612..064d9d3cfdd 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonHead.java +++ b/src/main/java/cn/nukkit/block/BlockPistonHead.java @@ -3,11 +3,12 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; import cn.nukkit.math.BlockFace; +import cn.nukkit.utils.Faceable; /** * @author CreeperFace */ -public class BlockPistonHead extends BlockTransparentMeta { +public class BlockPistonHead extends BlockTransparentMeta implements Faceable { public BlockPistonHead() { this(0); @@ -45,16 +46,19 @@ public Item[] getDrops(Item item) { @Override public boolean onBreak(Item item) { this.level.setBlock(this, new BlockAir(), true, true); - Block piston = getSide(getFacing().getOpposite()); + Block piston = getSide(getBlockFace().getOpposite()); - if (piston instanceof BlockPistonBase && ((BlockPistonBase) piston).getFacing() == this.getFacing()) { + if (piston instanceof BlockPistonBase && ((BlockPistonBase) piston).getBlockFace() == this.getBlockFace()) { piston.onBreak(item); } return true; } - public BlockFace getFacing() { - return BlockFace.fromIndex(this.getDamage()).getOpposite(); + @Override + public BlockFace getBlockFace() { + BlockFace face = BlockFace.fromIndex(this.getDamage()); + + return face.getHorizontalIndex() >= 0 ? face.getOpposite() : face; } @Override diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java index c0ea26af643..795d5752ca6 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java @@ -4,7 +4,9 @@ import cn.nukkit.event.redstone.RedstoneUpdateEvent; import cn.nukkit.item.Item; import cn.nukkit.level.Level; +import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.BlockFace; +import cn.nukkit.math.SimpleAxisAlignedBB; import cn.nukkit.math.Vector3; import cn.nukkit.utils.Faceable; @@ -199,6 +201,11 @@ public boolean isFacingTowardsRepeater() { return block instanceof BlockRedstoneDiode && ((BlockRedstoneDiode) block).getFacing() != side; } + @Override + protected AxisAlignedBB recalculateBoundingBox() { + return new SimpleAxisAlignedBB(this.x, this.y, this.z, this.x + 1, this.y + 0.125, this.z + 1); + } + @Override public BlockFace getBlockFace() { return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07); diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java index d407381f4ba..b3f10b312f2 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java @@ -44,7 +44,12 @@ public int getId() { @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { - if (face != BlockFace.UP || !canBePlacedOn(target)) { + if (target.canBeReplaced()) { + block = target; + target = target.down(); + } + + if (!canBePlacedOn(target)) { return false; } @@ -207,7 +212,7 @@ public int onUpdate(int type) { return 0; } - if (type == Level.BLOCK_UPDATE_NORMAL && !this.canBePlacedOn(this.getLocation().down())) { + if (type == Level.BLOCK_UPDATE_NORMAL && !this.canBePlacedOn(this.down())) { this.getLevel().useBreakOn(this); return Level.BLOCK_UPDATE_NORMAL; } @@ -217,9 +222,7 @@ public int onUpdate(int type) { return Level.BLOCK_UPDATE_NORMAL; } - public boolean canBePlacedOn(Vector3 v) { - Block b = this.level.getBlock(v); - + public boolean canBePlacedOn(Block b) { return b.isSolid() && !b.isTransparent() && b.getId() != Block.GLOWSTONE; } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java index 41a2587d9e7..b86775a48fe 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java @@ -77,7 +77,12 @@ public BlockEntity(FullChunk chunk, CompoundTag nbt) { this.x = this.namedTag.getInt("x"); this.y = this.namedTag.getInt("y"); this.z = this.namedTag.getInt("z"); - this.movable = this.namedTag.getBoolean("isMovable"); + + if (namedTag.contains("isMovable")) { + this.movable = this.namedTag.getBoolean("isMovable"); + } else { + this.movable = true; + } this.initBlockEntity(); diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java index 564a9649fb4..eec12f3412f 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java @@ -1,6 +1,7 @@ package cn.nukkit.blockentity; import cn.nukkit.block.Block; +import cn.nukkit.block.BlockID; import cn.nukkit.level.format.FullChunk; import cn.nukkit.math.BlockVector3; import cn.nukkit.nbt.tag.CompoundTag; @@ -10,10 +11,10 @@ */ public class BlockEntityMovingBlock extends BlockEntitySpawnable { - public Block block; + protected String blockString; + protected Block block; - public BlockVector3 piston; - public int progress; + protected BlockVector3 piston; public BlockEntityMovingBlock(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); @@ -21,8 +22,11 @@ public BlockEntityMovingBlock(FullChunk chunk, CompoundTag nbt) { @Override protected void initBlockEntity() { - if (namedTag.contains("movingBlockData") && namedTag.contains("movingBlockId")) { - this.block = Block.get(namedTag.getInt("movingBlockId"), namedTag.getInt("movingBlockData")); + if (namedTag.contains("movingBlock")) { + CompoundTag blockData = namedTag.getCompound("movingBlock"); + + this.blockString = blockData.getString("name"); + this.block = Block.get(blockData.getInt("id"), blockData.getInt("meta")); } else { this.close(); } @@ -30,28 +34,34 @@ protected void initBlockEntity() { if (namedTag.contains("pistonPosX") && namedTag.contains("pistonPosY") && namedTag.contains("pistonPosZ")) { this.piston = new BlockVector3(namedTag.getInt("pistonPosX"), namedTag.getInt("pistonPosY"), namedTag.getInt("pistonPosZ")); } else { - this.close(); + this.piston = new BlockVector3(0, -1, 0); } super.initBlockEntity(); } - public Block getBlock() { + public CompoundTag getBlockEntity() { + if (this.namedTag.contains("movingEntity")) { + return this.namedTag.getCompound("movingEntity"); + } + + return null; + } + + public Block getMovingBlock() { return this.block; } - @Override - public boolean isBlockEntityValid() { - return true; + public String getMovingBlockString() { + return this.blockString; + } + + public void moveCollidedEntities(BlockEntityPistonArm piston) { + } @Override - public CompoundTag getSpawnCompound() { - return getDefaultCompound(this, MOVING_BLOCK) - .putFloat("movingBlockId", this.block.getId()) - .putFloat("movingBlockData", this.block.getDamage()) - .putInt("pistonPosX", this.piston.x) - .putInt("pistonPosY", this.piston.y) - .putInt("pistonPosZ", this.piston.z); + public boolean isBlockEntityValid() { + return this.level.getBlockIdAt(getFloorX(), getFloorY(), getFloorZ()) == BlockID.MOVING_BLOCK; } } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index 4fb4a2d2658..24cafd3c1a2 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -1,30 +1,35 @@ package cn.nukkit.blockentity; -import cn.nukkit.entity.Entity; +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockAir; +import cn.nukkit.block.BlockID; import cn.nukkit.level.format.FullChunk; -import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.BlockFace; -import cn.nukkit.math.SimpleAxisAlignedBB; import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.IntTag; import cn.nukkit.nbt.tag.ListTag; +import cn.nukkit.utils.Faceable; + +import java.util.ArrayList; +import java.util.List; /** * @author CreeperFace */ public class BlockEntityPistonArm extends BlockEntitySpawnable { + public static final float MOVE_STEP = Float.valueOf(0.5f); + public float progress; public float lastProgress = 1.0F; public BlockFace facing; - public boolean extending = false; - public boolean sticky = false; - public byte state; - public byte newState = 1; - public Vector3 attachedBlock = null; - public boolean isMovable = true; - public boolean powered = false; + public boolean extending; + public boolean sticky; + public int state; + public int newState = 1; + public List attachedBlocks; + public boolean powered; public BlockEntityPistonArm(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); @@ -40,45 +45,128 @@ protected void initBlockEntity() { this.lastProgress = (float) namedTag.getInt("LastProgress"); } - if (namedTag.contains("Sticky")) { - this.sticky = namedTag.getBoolean("Sticky"); - } + this.sticky = namedTag.getBoolean("Sticky"); + this.extending = namedTag.getBoolean("Extending"); + this.powered = namedTag.getBoolean("powered"); - if (namedTag.contains("Extending")) { - this.extending = namedTag.getBoolean("Extending"); - } - if (namedTag.contains("powered")) { - this.powered = namedTag.getBoolean("powered"); + if (namedTag.contains("facing")) { + this.facing = BlockFace.fromIndex(namedTag.getInt("facing")); + } else { + Block b = this.getLevelBlock(); + + if (b instanceof Faceable) { + this.facing = ((Faceable) b).getBlockFace(); + } } + attachedBlocks = new ArrayList<>(); + if (namedTag.contains("AttachedBlocks")) { ListTag blocks = namedTag.getList("AttachedBlocks", IntTag.class); if (blocks != null && blocks.size() > 0) { - this.attachedBlock = new Vector3((double) ((IntTag) blocks.get(0)).getData(), (double) ((IntTag) blocks.get(1)).getData(), (double) ((IntTag) blocks.get(2)).getData()); + for (int i = 0; i < blocks.size(); i += 3) { + this.attachedBlocks.add(new Vector3( + ((IntTag) blocks.get(i)).data, + ((IntTag) blocks.get(i + 1)).data, + ((IntTag) blocks.get(i + 1)).data + )); + } } } else { - namedTag.putList(new ListTag("AttachedBlocks")); + namedTag.putList(new ListTag<>("AttachedBlocks")); } super.initBlockEntity(); } - private void pushEntities() { - float lastProgress = this.getExtendedProgress(this.lastProgress); - double x = (double) (lastProgress * (float) this.facing.getXOffset()); - double y = (double) (lastProgress * (float) this.facing.getYOffset()); - double z = (double) (lastProgress * (float) this.facing.getZOffset()); - AxisAlignedBB bb = new SimpleAxisAlignedBB(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D); - Entity[] entities = this.level.getCollidingEntities(bb); - if (entities.length != 0) { + private void moveCollidedEntities() { +// float lastProgress = this.getExtendedProgress(this.lastProgress); +// double x = (double) (lastProgress * (float) this.facing.getXOffset()); +// double y = (double) (lastProgress * (float) this.facing.getYOffset()); +// double z = (double) (lastProgress * (float) this.facing.getZOffset()); +// AxisAlignedBB bb = new SimpleAxisAlignedBB(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D); +// Entity[] entities = this.level.getCollidingEntities(bb); +// if (entities.length != 0) { +// +// } + + BlockFace pushDir = this.extending ? facing : facing.getOpposite(); + for (Vector3 pos : this.attachedBlocks) { + BlockEntity blockEntity = this.level.getBlockEntity(pos.getSide(pushDir)); + + if (blockEntity instanceof BlockEntityMovingBlock) { + ((BlockEntityMovingBlock) blockEntity).moveCollidedEntities(this); + } + } + } + + public void move(boolean extending, List attachedBlocks) { + this.extending = extending; + this.lastProgress = this.progress = extending ? 0 : 1; + this.state = this.newState = extending ? 1 : 3; + this.attachedBlocks = attachedBlocks; + + this.level.addChunkPacket(getChunkX(), getChunkZ(), getSpawnPacket()); + this.lastProgress = extending ? -MOVE_STEP : 1 + MOVE_STEP; + this.moveCollidedEntities(); + this.scheduleUpdate(); + } + + @Override + public boolean onUpdate() { + boolean hasUpdate = true; + + if (this.extending) { + this.progress = Math.min(1, this.progress + MOVE_STEP); + this.lastProgress = Math.min(1, this.lastProgress + MOVE_STEP); + } else { + this.progress = Math.max(0, this.progress - MOVE_STEP); + this.lastProgress = Math.max(0, this.lastProgress - MOVE_STEP); + } + + this.level.addChunkPacket(getChunkX(), getChunkZ(), getSpawnPacket()); + this.moveCollidedEntities(); + + if (this.progress == this.lastProgress) { + this.state = this.newState = extending ? 2 : 0; + + BlockFace pushDir = this.extending ? facing : facing.getOpposite(); + + for (Vector3 pos : this.attachedBlocks) { + BlockEntity movingBlock = this.level.getBlockEntity(pos.getSide(pushDir)); + + if (movingBlock instanceof BlockEntityMovingBlock) { + movingBlock.close(); + Block moved = ((BlockEntityMovingBlock) movingBlock).getMovingBlock(); + + this.level.setBlock(movingBlock, moved); + + CompoundTag blockEntity = ((BlockEntityMovingBlock) movingBlock).getBlockEntity(); + if (blockEntity != null) { + blockEntity.putInt("x", movingBlock.getFloorX()); + blockEntity.putInt("y", movingBlock.getFloorY()); + blockEntity.putInt("z", movingBlock.getFloorZ()); + BlockEntity.createBlockEntity(blockEntity.getString("id"), this.level.getChunk(movingBlock.getChunkX(), movingBlock.getChunkZ()), blockEntity); + } + } + } + + if (!extending && this.level.getBlock(getSide(facing)).getId() == BlockID.PISTON_HEAD) { + this.level.setBlock(getSide(facing), new BlockAir()); + } + + this.level.scheduleUpdate(this.getLevelBlock(), 1); + this.attachedBlocks.clear(); + hasUpdate = false; } + return super.onUpdate() || hasUpdate; } private float getExtendedProgress(float progress) { - return this.extending ? progress - 1.0F : 1.0F - progress; + return this.extending ? progress - 1 : 1 - progress; } public boolean isBlockEntityValid() { @@ -87,21 +175,39 @@ public boolean isBlockEntityValid() { public void saveNBT() { super.saveNBT(); - this.namedTag.putBoolean("isMovable", this.isMovable); this.namedTag.putByte("State", this.state); this.namedTag.putByte("NewState", this.newState); this.namedTag.putFloat("Progress", this.progress); this.namedTag.putFloat("LastProgress", this.lastProgress); this.namedTag.putBoolean("powered", this.powered); + this.namedTag.putList(getAttachedBlocks()); + this.namedTag.putInt("facing", this.facing.getIndex()); } public CompoundTag getSpawnCompound() { return new CompoundTag() - .putString("id", "PistonArm") + .putString("id", BlockEntity.PISTON_ARM) .putInt("x", (int) this.x) .putInt("y", (int) this.y) .putInt("z", (int) this.z) .putFloat("Progress", this.progress) - .putByte("State", this.state); + .putFloat("LastProgress", this.lastProgress) + .putBoolean("isMovable", this.movable) + .putList(getAttachedBlocks()) + .putList(new ListTag<>("BreakBlocks")) + .putBoolean("Sticky", this.sticky) + .putByte("State", this.state) + .putByte("NewState", this.newState); + } + + private ListTag getAttachedBlocks() { + ListTag attachedBlocks = new ListTag<>("AttachedBlocks"); + for (Vector3 block : this.attachedBlocks) { + attachedBlocks.add(new IntTag("", block.getFloorX())); + attachedBlocks.add(new IntTag("", block.getFloorY())); + attachedBlocks.add(new IntTag("", block.getFloorZ())); + } + + return attachedBlocks; } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java index e0d8651346d..7ac3146ec35 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java @@ -26,24 +26,38 @@ protected void initBlockEntity() { this.spawnToAll(); } - public abstract CompoundTag getSpawnCompound(); + public CompoundTag getSpawnCompound() { + return this.namedTag; + } public void spawnTo(Player player) { if (this.closed) { return; } - CompoundTag tag = this.getSpawnCompound(); + player.dataPacket(getSpawnPacket()); + } + + public BlockEntityDataPacket getSpawnPacket() { + return getSpawnPacket(null); + } + + public BlockEntityDataPacket getSpawnPacket(CompoundTag nbt) { + if (nbt == null) { + nbt = this.getSpawnCompound(); + } + BlockEntityDataPacket pk = new BlockEntityDataPacket(); pk.x = (int) this.x; pk.y = (int) this.y; pk.z = (int) this.z; try { - pk.namedTag = NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN, true); + pk.namedTag = NBTIO.write(nbt, ByteOrder.LITTLE_ENDIAN, true); } catch (IOException e) { throw new RuntimeException(e); } - player.dataPacket(pk); + + return pk; } public void spawnToAll() { diff --git a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java index 4d96f182d8f..69e36467073 100644 --- a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java +++ b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java @@ -5,6 +5,8 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.io.InputStream; import java.io.InputStreamReader; @@ -12,12 +14,16 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicInteger; public class GlobalBlockPalette { private static final Int2IntArrayMap legacyToRuntimeId = new Int2IntArrayMap(); private static final Int2IntArrayMap runtimeIdToLegacy = new Int2IntArrayMap(); + private static final Int2ObjectMap legacyIdToString = new Int2ObjectOpenHashMap<>(); + private static final Map stringToLegacyId = new HashMap<>(); private static final AtomicInteger runtimeIdAllocator = new AtomicInteger(0); private static final byte[] compiledPalette; @@ -40,7 +46,7 @@ public class GlobalBlockPalette { table.putUnsignedVarInt(entries.size()); for (TableEntry entry : entries) { - registerMapping((entry.id << 4) | entry.data); + registerMapping((entry.id << 4) | entry.data, entry.name); table.putString(entry.name); table.putLShort(entry.data); table.putLShort(entry.id); @@ -62,9 +68,15 @@ public static int getOrCreateRuntimeId(int legacyId) throws NoSuchElementExcepti return runtimeId; } - private static int registerMapping(int legacyId) { + public static String getName(int id, int meta) { + return legacyIdToString.get((id << 4) | meta); + } + + private static int registerMapping(int legacyId, String name) { int runtimeId = runtimeIdAllocator.getAndIncrement(); runtimeIdToLegacy.put(runtimeId, legacyId); + legacyIdToString.put(legacyId, name); + stringToLegacyId.put(name, legacyId); legacyToRuntimeId.put(legacyId, runtimeId); return runtimeId; } diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index 6d075cd20e9..b3ba0aa4187 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -2105,6 +2105,7 @@ public Item useItemOn(Vector3 vector, Item item, BlockFace face, float fx, float return null; } + MainLogger.getLogger().info("place: " + block + " loc: " + block.getLocationHash()); if (player != null) { if (!player.isCreative()) { item.setCount(item.getCount() - 1); diff --git a/src/main/java/cn/nukkit/nbt/tag/CompoundTag.java b/src/main/java/cn/nukkit/nbt/tag/CompoundTag.java index 52d187f9fdb..9abe4883ee6 100644 --- a/src/main/java/cn/nukkit/nbt/tag/CompoundTag.java +++ b/src/main/java/cn/nukkit/nbt/tag/CompoundTag.java @@ -12,14 +12,23 @@ import java.util.StringJoiner; public class CompoundTag extends Tag implements Cloneable { - private final Map tags = new HashMap<>(); + private final Map tags; public CompoundTag() { - super(""); + this(""); } public CompoundTag(String name) { + this(name, new HashMap<>()); + } + + public CompoundTag(Map tags) { + this("", tags); + } + + public CompoundTag(String name, Map tags) { super(name); + this.tags = tags; } @Override diff --git a/src/main/java/cn/nukkit/nbt/tag/Tag.java b/src/main/java/cn/nukkit/nbt/tag/Tag.java index 3b66e063b2b..2f33f67ed71 100644 --- a/src/main/java/cn/nukkit/nbt/tag/Tag.java +++ b/src/main/java/cn/nukkit/nbt/tag/Tag.java @@ -60,7 +60,9 @@ public void print(String prefix, PrintStream out) { out.print("(\"" + name + "\")"); } out.print(": "); - out.println(toString()); + if (getId() != TAG_Compound && getId() != TAG_List) { + out.println(parseValue()); + } } public Tag setName(String name) { diff --git a/src/main/java/cn/nukkit/network/Network.java b/src/main/java/cn/nukkit/network/Network.java index fe632730d55..0c5d13c2377 100644 --- a/src/main/java/cn/nukkit/network/Network.java +++ b/src/main/java/cn/nukkit/network/Network.java @@ -5,6 +5,7 @@ import cn.nukkit.Server; import cn.nukkit.network.protocol.*; import cn.nukkit.utils.BinaryStream; +import cn.nukkit.utils.MainLogger; import cn.nukkit.utils.Utils; import cn.nukkit.utils.Zlib; import io.netty.buffer.ByteBufUtil; @@ -179,9 +180,7 @@ public void processBatch(BatchPacket packet, Player player) { processPackets(player, packets); } catch (Exception e) { - if (log.isDebugEnabled()) { - log.debug("Error whilst decoding batch packet", e); - } + MainLogger.getLogger().error("Error whilst decoding batch packet", e); } } From 69ea148fc0124c5ead282653c1e28cd73ce63359 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 18 Aug 2019 16:50:17 +0200 Subject: [PATCH 03/52] fixed piston input power, plugin unknown dependency message --- src/main/java/cn/nukkit/Server.java | 5 +++- .../java/cn/nukkit/block/BlockPistonBase.java | 20 ++++++++++++---- .../cn/nukkit/block/BlockRedstoneWire.java | 11 +++++---- .../blockentity/BlockEntityPistonArm.java | 23 +++++++++++-------- .../nukkit/entity/item/EntityMinecartTNT.java | 4 ---- src/main/java/cn/nukkit/level/Level.java | 19 +++++++++++---- .../cn/nukkit/level/format/anvil/Chunk.java | 4 ++-- .../java/cn/nukkit/plugin/PluginManager.java | 2 +- 8 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index b5db3136613..5eeb3d291d0 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -744,8 +744,11 @@ public boolean dispatchCommand(CommandSender sender, String commandLine) throws if (!this.isPrimaryThread()) { getLogger().warning("Command Dispatched Async: " + commandLine); getLogger().warning("Please notify author of plugin causing this execution to fix this bug!", new Throwable()); - // TODO: We should sync the command to the main thread too! + + this.scheduler.scheduleTask(null, () -> dispatchCommand(sender, commandLine)); + return true; } + if (sender == null) { throw new ServerException("CommandSender is not valid"); } diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 64be0c5a547..fa54d022976 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -10,6 +10,7 @@ import cn.nukkit.level.GlobalBlockPalette; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; +import cn.nukkit.math.BlockVector3; import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.LevelSoundEventPacket; @@ -129,7 +130,7 @@ private boolean checkState(Boolean isPowered) { } if (isPowered && !isExtended()) { - if ((new BlocksCalculator(true)).canMove()) { + if (new BlocksCalculator(true).canMove()) { if (!this.doMove(true)) { return false; } @@ -164,7 +165,17 @@ private boolean isPowered() { BlockFace face = getBlockFace(); for (BlockFace side : BlockFace.values()) { - if (side != face && this.level.isSidePowered(this.getLocation().getSide(side), side)) { + if (side == face) { + continue; + } + + Block b = this.getSide(side); + + if (b.getId() == Block.REDSTONE_WIRE && b.getDamage() > 0) { + return true; + } + + if (this.level.isSidePowered(b, side)) { return true; } } @@ -191,7 +202,7 @@ private boolean doMove(boolean extending) { if (!calculator.canMove()) { return false; } else { - List attached = Collections.emptyList(); + List attached = Collections.emptyList(); if (this.sticky || extending) { List destroyBlocks = calculator.getBlocksToDestroy(); @@ -201,7 +212,7 @@ private boolean doMove(boolean extending) { } List newBlocks = calculator.getBlocksToMove(); - attached = newBlocks.stream().map(b -> b.add(0)).collect(Collectors.toList()); + attached = newBlocks.stream().map(Vector3::asBlockVector3).collect(Collectors.toList()); BlockFace side = extending ? direction : direction.getOpposite(); @@ -228,7 +239,6 @@ private boolean doMove(boolean extending) { blockEntity.saveNBT(); CompoundTag t = new CompoundTag(blockEntity.namedTag.getTags()); - t.print(System.out); nbt.putCompound("movingEntity", t); blockEntity.close(); diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java index b3f10b312f2..2d058a000aa 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java @@ -241,17 +241,17 @@ public int getWeakPower(BlockFace side) { } else if (side == BlockFace.UP) { return power; } else { - EnumSet enumset = EnumSet.noneOf(BlockFace.class); + EnumSet faces = EnumSet.noneOf(BlockFace.class); for (BlockFace face : Plane.HORIZONTAL) { if (this.isPowerSourceAt(face)) { - enumset.add(face); + faces.add(face); } } - if (side.getAxis().isHorizontal() && enumset.isEmpty()) { + if (side.getAxis().isHorizontal() && faces.isEmpty()) { return power; - } else if (enumset.contains(side) && !enumset.contains(side.rotateYCCW()) && !enumset.contains(side.rotateY())) { + } else if (faces.contains(side) && !faces.contains(side.rotateYCCW()) && !faces.contains(side.rotateY())) { return power; } else { return 0; @@ -283,6 +283,9 @@ protected static boolean canConnectTo(Block block, BlockFace side) { } else if (BlockRedstoneDiode.isDiode(block)) { BlockFace face = ((BlockRedstoneDiode) block).getFacing(); return face == side || face.getOpposite() == side; + } else if (block instanceof BlockPistonBase) { +// return ((BlockPistonBase) block).getBlockFace() != side.getOpposite(); + return true; } else { return block.isPowerSource() && side != null; } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index 24cafd3c1a2..79d23fd4f8e 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -3,9 +3,10 @@ import cn.nukkit.block.Block; import cn.nukkit.block.BlockAir; import cn.nukkit.block.BlockID; +import cn.nukkit.level.Level; import cn.nukkit.level.format.FullChunk; import cn.nukkit.math.BlockFace; -import cn.nukkit.math.Vector3; +import cn.nukkit.math.BlockVector3; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.IntTag; import cn.nukkit.nbt.tag.ListTag; @@ -28,7 +29,7 @@ public class BlockEntityPistonArm extends BlockEntitySpawnable { public boolean sticky; public int state; public int newState = 1; - public List attachedBlocks; + public List attachedBlocks; public boolean powered; public BlockEntityPistonArm(FullChunk chunk, CompoundTag nbt) { @@ -66,7 +67,7 @@ protected void initBlockEntity() { ListTag blocks = namedTag.getList("AttachedBlocks", IntTag.class); if (blocks != null && blocks.size() > 0) { for (int i = 0; i < blocks.size(); i += 3) { - this.attachedBlocks.add(new Vector3( + this.attachedBlocks.add(new BlockVector3( ((IntTag) blocks.get(i)).data, ((IntTag) blocks.get(i + 1)).data, ((IntTag) blocks.get(i + 1)).data @@ -92,7 +93,7 @@ private void moveCollidedEntities() { // } BlockFace pushDir = this.extending ? facing : facing.getOpposite(); - for (Vector3 pos : this.attachedBlocks) { + for (BlockVector3 pos : this.attachedBlocks) { BlockEntity blockEntity = this.level.getBlockEntity(pos.getSide(pushDir)); if (blockEntity instanceof BlockEntityMovingBlock) { @@ -101,7 +102,7 @@ private void moveCollidedEntities() { } } - public void move(boolean extending, List attachedBlocks) { + public void move(boolean extending, List attachedBlocks) { this.extending = extending; this.lastProgress = this.progress = extending ? 0 : 1; this.state = this.newState = extending ? 1 : 3; @@ -133,7 +134,7 @@ public boolean onUpdate() { BlockFace pushDir = this.extending ? facing : facing.getOpposite(); - for (Vector3 pos : this.attachedBlocks) { + for (BlockVector3 pos : this.attachedBlocks) { BlockEntity movingBlock = this.level.getBlockEntity(pos.getSide(pushDir)); if (movingBlock instanceof BlockEntityMovingBlock) { @@ -150,6 +151,8 @@ public boolean onUpdate() { blockEntity.putInt("z", movingBlock.getFloorZ()); BlockEntity.createBlockEntity(blockEntity.getString("id"), this.level.getChunk(movingBlock.getChunkX(), movingBlock.getChunkZ()), blockEntity); } + + moved.onUpdate(Level.BLOCK_UPDATE_NORMAL); } } @@ -202,10 +205,10 @@ public CompoundTag getSpawnCompound() { private ListTag getAttachedBlocks() { ListTag attachedBlocks = new ListTag<>("AttachedBlocks"); - for (Vector3 block : this.attachedBlocks) { - attachedBlocks.add(new IntTag("", block.getFloorX())); - attachedBlocks.add(new IntTag("", block.getFloorY())); - attachedBlocks.add(new IntTag("", block.getFloorZ())); + for (BlockVector3 block : this.attachedBlocks) { + attachedBlocks.add(new IntTag("", block.x)); + attachedBlocks.add(new IntTag("", block.y)); + attachedBlocks.add(new IntTag("", block.z)); } return attachedBlocks; diff --git a/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java b/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java index 8cad3fb0c05..8b55c069964 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java +++ b/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java @@ -1,6 +1,5 @@ package cn.nukkit.entity.item; -import cn.nukkit.Server; import cn.nukkit.block.BlockTNT; import cn.nukkit.entity.EntityExplosive; import cn.nukkit.entity.data.IntEntityData; @@ -53,7 +52,6 @@ public void initEntity() { public boolean onUpdate(int currentTick) { this.timing.startTiming(); - // Todo: Check why the TNT doesn't want to tick if (activated || fuse < 80) { int tickDiff = currentTick - lastUpdate; @@ -71,8 +69,6 @@ public boolean onUpdate(int currentTick) { } kill(); } - - Server.getInstance().getLogger().info("Debug:" + fuse); } this.timing.stopTiming(); diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index b3ba0aa4187..73169167621 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -2105,7 +2105,6 @@ public Item useItemOn(Vector3 vector, Item item, BlockFace face, float fx, float return null; } - MainLogger.getLogger().info("place: " + block + " loc: " + block.getLocationHash()); if (player != null) { if (!player.isCreative()) { item.setCount(item.getCount() - 1); @@ -2227,10 +2226,14 @@ public Map getLoaders() { } public BlockEntity getBlockEntity(Vector3 pos) { - FullChunk chunk = this.getChunk((int) pos.x >> 4, (int) pos.z >> 4, false); + return getBlockEntity(pos.asBlockVector3()); + } + + public BlockEntity getBlockEntity(BlockVector3 pos) { + FullChunk chunk = this.getChunk(pos.x >> 4, pos.z >> 4, false); if (chunk != null) { - return chunk.getTile((int) pos.x & 0x0f, (int) pos.y & 0xff, (int) pos.z & 0x0f); + return chunk.getTile(pos.x & 0x0f, pos.y & 0xff, pos.z & 0x0f); } return null; @@ -3345,7 +3348,15 @@ public boolean isSidePowered(Vector3 pos, BlockFace face) { } public int getRedstonePower(Vector3 pos, BlockFace face) { - Block block = this.getBlock(pos); + Block block; + + if (pos instanceof Block) { + block = (Block) pos; + pos = pos.add(0); + } else { + block = this.getBlock(pos); + } + return block.isNormalBlock() ? this.getStrongPower(pos) : block.getWeakPower(face); } diff --git a/src/main/java/cn/nukkit/level/format/anvil/Chunk.java b/src/main/java/cn/nukkit/level/format/anvil/Chunk.java index 722271bff00..b0d15b46e28 100644 --- a/src/main/java/cn/nukkit/level/format/anvil/Chunk.java +++ b/src/main/java/cn/nukkit/level/format/anvil/Chunk.java @@ -275,7 +275,7 @@ public byte[] toFastBinary() { if (section instanceof EmptyChunkSection) { continue; } - CompoundTag s = new CompoundTag(null); + CompoundTag s = new CompoundTag(); s.putByte("Y", section.getY()); s.putByteArray("Blocks", section.getIdArray()); s.putByteArray("Data", section.getDataArray()); @@ -358,7 +358,7 @@ public byte[] toBinary() { if (section instanceof EmptyChunkSection) { continue; } - CompoundTag s = new CompoundTag(null); + CompoundTag s = new CompoundTag(); s.putByte("Y", (section.getY())); s.putByteArray("Blocks", section.getIdArray()); s.putByteArray("Data", section.getDataArray()); diff --git a/src/main/java/cn/nukkit/plugin/PluginManager.java b/src/main/java/cn/nukkit/plugin/PluginManager.java index fab4bc5c7a1..dfb694a51cf 100644 --- a/src/main/java/cn/nukkit/plugin/PluginManager.java +++ b/src/main/java/cn/nukkit/plugin/PluginManager.java @@ -244,7 +244,7 @@ public Map loadPlugins(File dictionary, List newLoaders, dependencies.get(name).remove(dependency); } else if (!plugins.containsKey(dependency)) { this.server.getLogger().critical(this.server.getLanguage().translateString("nukkit" + - ".plugin.loadError", new String[]{name, "%nukkit.plugin.unknownDependency"})); + ".plugin.loadError", name, "%nukkit.plugin.unknownDependency", dependency)); break; } } From 079f47ee8c92fab5940173440be0cfbf54b9dea1 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 18 Aug 2019 16:56:56 +0200 Subject: [PATCH 04/52] plugin unknown dependency message --- src/main/resources/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/lang b/src/main/resources/lang index 6bc53fad382..c63bafbc9ae 160000 --- a/src/main/resources/lang +++ b/src/main/resources/lang @@ -1 +1 @@ -Subproject commit 6bc53fad382c615843fcb2adaf2b05839b64e7e6 +Subproject commit c63bafbc9aea902cac375e48d13944c4232a22c5 From 2aaa64d24db5670e1c0167bd8d29a675c5d74f75 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 18 Aug 2019 17:01:26 +0200 Subject: [PATCH 05/52] fix build --- src/main/java/cn/nukkit/nbt/NBTIO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/nbt/NBTIO.java b/src/main/java/cn/nukkit/nbt/NBTIO.java index b0b073e035c..614eae3130f 100644 --- a/src/main/java/cn/nukkit/nbt/NBTIO.java +++ b/src/main/java/cn/nukkit/nbt/NBTIO.java @@ -28,7 +28,7 @@ public static CompoundTag putItemHelper(Item item) { } public static CompoundTag putItemHelper(Item item, Integer slot) { - CompoundTag tag = new CompoundTag(null) + CompoundTag tag = new CompoundTag() .putShort("id", item.getId()) .putByte("Count", item.getCount()) .putShort("Damage", item.getDamage()); From e38921ebc3e2b60bf7b75d0054006ba252a3f5e6 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Wed, 18 Sep 2019 18:40:27 +0200 Subject: [PATCH 06/52] fixes --- src/main/java/cn/nukkit/Player.java | 2 +- src/main/java/cn/nukkit/block/BlockPistonBase.java | 4 ++-- src/main/java/cn/nukkit/blockentity/BlockEntity.java | 1 + .../java/cn/nukkit/blockentity/BlockEntityPistonArm.java | 5 ++++- src/main/java/cn/nukkit/level/GlobalBlockPalette.java | 4 ++++ src/main/java/cn/nukkit/nbt/tag/ShortTag.java | 2 +- 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index 2710b6b0aa1..4a9d0d9321d 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -1064,7 +1064,7 @@ public int dataPacket(DataPacket packet, boolean needACK) { Integer identifier = this.interfaz.putPacket(this, packet, needACK, false); if (needACK && identifier != null) { - this.needACK.put(identifier, Boolean.FALSE); + this.needACK.put(identifier.intValue(), Boolean.FALSE); return identifier; } } diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index fa54d022976..73691a42225 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -222,7 +222,7 @@ private boolean doMove(boolean extending) { BlockEntity blockEntity = this.level.getBlockEntity(oldPos); - this.level.setBlock(newBlock, Block.get(BlockID.MOVING_BLOCK)); + this.level.setBlock(newBlock, Block.get(BlockID.MOVING_BLOCK), true); CompoundTag nbt = BlockEntity.getDefaultCompound(newBlock, BlockEntity.MOVING_BLOCK) .putInt("pistonPosX", this.getFloorX()) @@ -235,7 +235,7 @@ private boolean doMove(boolean extending) { .putString("name", GlobalBlockPalette.getName(newBlock.getId(), newBlock.getDamage())) ); - if (blockEntity != null) { + if (blockEntity != null && !(blockEntity instanceof BlockEntityMovingBlock)) { blockEntity.saveNBT(); CompoundTag t = new CompoundTag(blockEntity.namedTag.getTags()); diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java index b86775a48fe..b17985a1c31 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java @@ -82,6 +82,7 @@ public BlockEntity(FullChunk chunk, CompoundTag nbt) { this.movable = this.namedTag.getBoolean("isMovable"); } else { this.movable = true; + namedTag.putBoolean("isMovable", true); } this.initBlockEntity(); diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index 79d23fd4f8e..02f52659d6b 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -107,6 +107,7 @@ public void move(boolean extending, List attachedBlocks) { this.lastProgress = this.progress = extending ? 0 : 1; this.state = this.newState = extending ? 1 : 3; this.attachedBlocks = attachedBlocks; + this.movable = false; this.level.addChunkPacket(getChunkX(), getChunkZ(), getSpawnPacket()); this.lastProgress = extending ? -MOVE_STEP : 1 + MOVE_STEP; @@ -126,7 +127,6 @@ public boolean onUpdate() { this.lastProgress = Math.max(0, this.lastProgress - MOVE_STEP); } - this.level.addChunkPacket(getChunkX(), getChunkZ(), getSpawnPacket()); this.moveCollidedEntities(); if (this.progress == this.lastProgress) { @@ -162,9 +162,12 @@ public boolean onUpdate() { this.level.scheduleUpdate(this.getLevelBlock(), 1); this.attachedBlocks.clear(); + this.movable = true; hasUpdate = false; } + this.level.addChunkPacket(getChunkX(), getChunkZ(), getSpawnPacket()); + return super.onUpdate() || hasUpdate; } diff --git a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java index a183dd6e7e8..4d9c28aa9ae 100644 --- a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java +++ b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java @@ -69,6 +69,10 @@ public static int getOrCreateRuntimeId(int legacyId) throws NoSuchElementExcepti return runtimeId; } + public static int getLegacyId(int runtimeId) { + return runtimeIdToLegacy.get(runtimeId); + } + public static String getName(int id, int meta) { return legacyIdToString.get((id << 4) | meta); } diff --git a/src/main/java/cn/nukkit/nbt/tag/ShortTag.java b/src/main/java/cn/nukkit/nbt/tag/ShortTag.java index cb4405468be..7c6b13add40 100644 --- a/src/main/java/cn/nukkit/nbt/tag/ShortTag.java +++ b/src/main/java/cn/nukkit/nbt/tag/ShortTag.java @@ -49,7 +49,7 @@ public byte getId() { @Override public String toString() { - return "" + data; + return "ShortTag " + this.getName() + "(data: " + data + ")"; } @Override From 6318c0e77b2da3d46c70b694463afee8ffc08e97 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sat, 12 Oct 2019 14:55:16 +0200 Subject: [PATCH 07/52] Fix slimeblocks --- src/main/java/cn/nukkit/Player.java | 2 +- .../java/cn/nukkit/block/BlockPistonBase.java | 138 ++++++++++-------- 2 files changed, 77 insertions(+), 63 deletions(-) diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index 18920a3a5f6..8168578ffae 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -1022,7 +1022,7 @@ public boolean batchDataPacket(DataPacket packet) { return false; } - try (Timing timing = Timings.getSendDataPacketTiming(packet)) { + try (Timing ignored = Timings.getSendDataPacketTiming(packet)) { DataPacketSendEvent event = new DataPacketSendEvent(this, packet); this.server.getPluginManager().callEvent(event); if (event.isCancelled()) { diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 73691a42225..312553f98f3 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -15,6 +15,7 @@ import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.LevelSoundEventPacket; import cn.nukkit.utils.Faceable; +import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collections; @@ -212,6 +213,7 @@ private boolean doMove(boolean extending) { } List newBlocks = calculator.getBlocksToMove(); + attached = newBlocks.stream().map(Vector3::asBlockVector3).collect(Collectors.toList()); BlockFace side = extending ? direction : direction.getOpposite(); @@ -313,20 +315,24 @@ public boolean canMove() { if (block instanceof BlockFlowable) { this.toDestroy.add(this.blockToMove); return true; - } else { - return false; } - } else if (!this.addBlockLine(this.blockToMove)) { + return false; - } else { - for (Block b : new ArrayList<>(this.toMove)) { - if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) { - return false; - } - } + } - return true; + if (!this.addBlockLine(this.blockToMove)) { + return false; + } + + for (int i = 0; i < this.toMove.size(); ++i) { + Block b = this.toMove.get(i); + + if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) { + return false; + } } + + return true; } private boolean addBlockLine(Block origin) { @@ -334,81 +340,89 @@ private boolean addBlockLine(Block origin) { if (block.getId() == AIR) { return true; - } else if (!canPush(origin, this.moveDirection, false)) { + } + + if (!canPush(origin, this.moveDirection, false)) { return true; - } else if (origin.equals(this.pistonPos)) { + } + + if (origin.equals(this.pistonPos)) { return true; - } else if (this.toMove.contains(origin)) { + } + + if (this.toMove.contains(origin)) { return true; - } else { - int count = 1; + } - if (count + this.toMove.size() > 12) { - return false; - } else { - while (block.getId() == SLIME_BLOCK) { - block = origin.getSide(this.moveDirection.getOpposite(), count); + if (this.toMove.size() >= 12) { + return false; + } - if (block.getId() == AIR || !canPush(block, this.moveDirection, false) || block.equals(this.pistonPos)) { - break; - } + this.toMove.add(block); - ++count; + int count = 1; + List sticked = new ArrayList<>(); - if (count + this.toMove.size() > 12) { - return false; - } - } + while (block.getId() == SLIME_BLOCK) { + block = origin.getSide(this.moveDirection.getOpposite(), count); - int blockCount = 0; + if (block.getId() == AIR || !canPush(block, this.moveDirection, false) || block.equals(this.pistonPos)) { + break; + } - for (int step = count - 1; step >= 0; step--) { - this.toMove.add(origin.getSide(this.moveDirection.getOpposite(), step)); - ++blockCount; - } + if (++count + this.toMove.size() > 12) { + return false; + } - int steps = 1; + sticked.add(block); + } - while (true) { - Block nextBlock = origin.getSide(this.moveDirection, steps); - int index = this.toMove.indexOf(nextBlock); + int stickedCount = sticked.size(); - if (index > -1) { - this.reorderListAtCollision(blockCount, index); + if (stickedCount > 0) { + this.toMove.addAll(Lists.reverse(sticked)); + } - for (int l = 0; l <= index + blockCount; ++l) { - Block b = this.toMove.get(l); + int step = 1; - if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) { - return false; - } - } + while (true) { + Block nextBlock = origin.getSide(this.moveDirection, step); + int index = this.toMove.indexOf(nextBlock); - return true; - } + if (index > -1) { + this.reorderListAtCollision(stickedCount, index); - if (nextBlock.getId() == AIR || nextBlock.equals(armPos)) { - return true; - } + for (int i = 0; i <= index + stickedCount; ++i) { + Block b = this.toMove.get(i); - if (!canPush(nextBlock, this.moveDirection, true) || nextBlock.equals(this.pistonPos)) { + if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) { return false; } + } - if (nextBlock instanceof BlockFlowable) { - this.toDestroy.add(nextBlock); - return true; - } + return true; + } - if (this.toMove.size() >= 12) { - return false; - } + if (nextBlock.getId() == AIR || nextBlock.equals(armPos)) { + return true; + } - this.toMove.add(nextBlock); - ++blockCount; - ++steps; - } + if (!canPush(nextBlock, this.moveDirection, true) || nextBlock.equals(this.pistonPos)) { + return false; } + + if (nextBlock instanceof BlockFlowable) { + this.toDestroy.add(nextBlock); + return true; + } + + if (this.toMove.size() >= 12) { + return false; + } + + this.toMove.add(nextBlock); + ++stickedCount; + ++step; } } From 55e12a1dbecb070619e6b19c0f406f54546989a2 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 13 Oct 2019 09:57:30 +0200 Subject: [PATCH 08/52] Implement entity collision --- .../java/cn/nukkit/block/BlockMoving.java | 5 ++ .../blockentity/BlockEntityMovingBlock.java | 15 +++++- .../blockentity/BlockEntityPistonArm.java | 47 ++++++++++++++----- src/main/java/cn/nukkit/entity/Entity.java | 4 ++ .../java/cn/nukkit/math/AxisAlignedBB.java | 4 ++ 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockMoving.java b/src/main/java/cn/nukkit/block/BlockMoving.java index c693449632b..8b385b6a91c 100644 --- a/src/main/java/cn/nukkit/block/BlockMoving.java +++ b/src/main/java/cn/nukkit/block/BlockMoving.java @@ -31,4 +31,9 @@ public boolean canBePushed() { public boolean isBreakable(Item item) { return false; } + + @Override + public boolean canPassThrough() { + return true; + } } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java index eec12f3412f..998688784a3 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java @@ -2,7 +2,10 @@ import cn.nukkit.block.Block; import cn.nukkit.block.BlockID; +import cn.nukkit.entity.Entity; import cn.nukkit.level.format.FullChunk; +import cn.nukkit.math.AxisAlignedBB; +import cn.nukkit.math.BlockFace; import cn.nukkit.math.BlockVector3; import cn.nukkit.nbt.tag.CompoundTag; @@ -56,8 +59,18 @@ public String getMovingBlockString() { return this.blockString; } - public void moveCollidedEntities(BlockEntityPistonArm piston) { + public void moveCollidedEntities(BlockEntityPistonArm piston, BlockFace moveDirection) { + AxisAlignedBB bb = block.getBoundingBox().getOffsetBoundingBox( + this.x + (piston.progress * moveDirection.getXOffset()) - moveDirection.getXOffset(), + this.y + (piston.progress * moveDirection.getYOffset()) - moveDirection.getYOffset(), + this.z + (piston.progress * moveDirection.getZOffset()) - moveDirection.getZOffset() + ); + Entity[] entities = this.level.getCollidingEntities(bb); + + for (Entity entity : entities) { + piston.moveEntity(entity, moveDirection); + } } @Override diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index 02f52659d6b..ffc0bcb3663 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -1,12 +1,16 @@ package cn.nukkit.blockentity; +import cn.nukkit.Player; import cn.nukkit.block.Block; import cn.nukkit.block.BlockAir; import cn.nukkit.block.BlockID; +import cn.nukkit.entity.Entity; import cn.nukkit.level.Level; import cn.nukkit.level.format.FullChunk; +import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.BlockFace; import cn.nukkit.math.BlockVector3; +import cn.nukkit.math.SimpleAxisAlignedBB; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.IntTag; import cn.nukkit.nbt.tag.ListTag; @@ -82,24 +86,45 @@ protected void initBlockEntity() { } private void moveCollidedEntities() { -// float lastProgress = this.getExtendedProgress(this.lastProgress); -// double x = (double) (lastProgress * (float) this.facing.getXOffset()); -// double y = (double) (lastProgress * (float) this.facing.getYOffset()); -// double z = (double) (lastProgress * (float) this.facing.getZOffset()); -// AxisAlignedBB bb = new SimpleAxisAlignedBB(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D); -// Entity[] entities = this.level.getCollidingEntities(bb); -// if (entities.length != 0) { -// -// } - BlockFace pushDir = this.extending ? facing : facing.getOpposite(); for (BlockVector3 pos : this.attachedBlocks) { BlockEntity blockEntity = this.level.getBlockEntity(pos.getSide(pushDir)); if (blockEntity instanceof BlockEntityMovingBlock) { - ((BlockEntityMovingBlock) blockEntity).moveCollidedEntities(this); + ((BlockEntityMovingBlock) blockEntity).moveCollidedEntities(this, pushDir); } } + + AxisAlignedBB bb = new SimpleAxisAlignedBB(0, 0, 0, 1, 1, 1).getOffsetBoundingBox( + this.x + (pushDir.getXOffset() * progress), + this.y + (pushDir.getYOffset() * progress), + this.z + (pushDir.getZOffset() * progress) + ); + + Entity[] entities = this.level.getCollidingEntities(bb); + + for (Entity entity : entities) { + moveEntity(entity, pushDir); + } + } + + void moveEntity(Entity entity, BlockFace moveDirection) { + if (!entity.canBePushed()) { + return; + } + + //TODO: event + + if (entity instanceof Player) { + return; + } + + float diff = this.progress - this.lastProgress; + entity.move( + diff * moveDirection.getXOffset(), + diff * moveDirection.getYOffset(), + diff * moveDirection.getZOffset() + ); } public void move(boolean extending, List attachedBlocks) { diff --git a/src/main/java/cn/nukkit/entity/Entity.java b/src/main/java/cn/nukkit/entity/Entity.java index cd5f5d595c3..4b98082adc2 100644 --- a/src/main/java/cn/nukkit/entity/Entity.java +++ b/src/main/java/cn/nukkit/entity/Entity.java @@ -1494,6 +1494,10 @@ public void setAbsorption(float absorption) { } } + public boolean canBePushed() { + return true; + } + public BlockFace getDirection() { double rotation = this.yaw % 360; if (rotation < 0) { diff --git a/src/main/java/cn/nukkit/math/AxisAlignedBB.java b/src/main/java/cn/nukkit/math/AxisAlignedBB.java index 640c07a3991..792afbbd7ff 100644 --- a/src/main/java/cn/nukkit/math/AxisAlignedBB.java +++ b/src/main/java/cn/nukkit/math/AxisAlignedBB.java @@ -87,6 +87,10 @@ default AxisAlignedBB setBB(AxisAlignedBB bb) { return this; } + default AxisAlignedBB getOffsetBoundingBox(BlockFace face, double x, double y, double z) { + return getOffsetBoundingBox(face.getXOffset() * x, face.getYOffset() * y, face.getZOffset() * z); + } + default AxisAlignedBB getOffsetBoundingBox(double x, double y, double z) { return new SimpleAxisAlignedBB(this.getMinX() + x, this.getMinY() + y, this.getMinZ() + z, this.getMaxX() + x, this.getMaxY() + y, this.getMaxZ() + z); } From f2969ac99175d396a7521cf78b97bd3781f3651e Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 13 Oct 2019 18:49:10 +0200 Subject: [PATCH 09/52] Dropper --- src/main/java/cn/nukkit/Server.java | 1 + src/main/java/cn/nukkit/block/Block.java | 2 +- .../java/cn/nukkit/block/BlockDispenser.java | 35 +++-- .../java/cn/nukkit/block/BlockDropper.java | 56 ++++++++ .../java/cn/nukkit/block/BlockPistonBase.java | 2 +- .../cn/nukkit/blockentity/BlockEntity.java | 1 + .../blockentity/BlockEntityDropper.java | 135 ++++++++++++++++++ .../cn/nukkit/inventory/DropperInventory.java | 15 ++ 8 files changed, 237 insertions(+), 10 deletions(-) create mode 100644 src/main/java/cn/nukkit/block/BlockDropper.java create mode 100644 src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java create mode 100644 src/main/java/cn/nukkit/inventory/DropperInventory.java diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index 3f33d49ef1b..ca76c33031c 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -2335,6 +2335,7 @@ private void registerBlockEntities() { BlockEntity.registerBlockEntity(BlockEntity.BANNER, BlockEntityBanner.class); BlockEntity.registerBlockEntity(BlockEntity.MUSIC, BlockEntityMusic.class); BlockEntity.registerBlockEntity(BlockEntity.DISPENSER, BlockEntityDispenser.class); + BlockEntity.registerBlockEntity(BlockEntity.DROPPER, BlockEntityDropper.class); BlockEntity.registerBlockEntity(BlockEntity.MOVING_BLOCK, BlockEntityMovingBlock.class); } diff --git a/src/main/java/cn/nukkit/block/Block.java b/src/main/java/cn/nukkit/block/Block.java index 8246d3eba8a..3e171f936a6 100644 --- a/src/main/java/cn/nukkit/block/Block.java +++ b/src/main/java/cn/nukkit/block/Block.java @@ -179,7 +179,7 @@ public static void init() { list[DRAGON_EGG] = BlockDragonEgg.class; //122 list[REDSTONE_LAMP] = BlockRedstoneLamp.class; //123 list[LIT_REDSTONE_LAMP] = BlockRedstoneLampLit.class; //124 - //TODO: list[DROPPER] = BlockDropper.class; //125 + list[DROPPER] = BlockDropper.class; //125 list[ACTIVATOR_RAIL] = BlockRailActivator.class; //126 list[COCOA] = BlockCocoa.class; //127 list[SANDSTONE_STAIRS] = BlockStairsSandstone.class; //128 diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index 6914f60ccc3..f2db0624b45 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -7,6 +7,7 @@ import cn.nukkit.dispenser.DispenseBehaviorRegister; import cn.nukkit.inventory.ContainerInventory; import cn.nukkit.inventory.Inventory; +import cn.nukkit.inventory.InventoryHolder; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; import cn.nukkit.level.Level; @@ -88,13 +89,13 @@ public boolean onActivate(Item item, Player player) { return false; } - BlockEntity blockEntity = this.level.getBlockEntity(this); + InventoryHolder blockEntity = getBlockEntity(); - if (!(blockEntity instanceof BlockEntityDispenser)) { + if (blockEntity == null) { return false; } - player.addWindow(((BlockEntityDispenser) blockEntity).getInventory()); + player.addWindow(blockEntity.getInventory()); return true; } @@ -118,9 +119,23 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl this.getLevel().setBlock(block, this, true); + createBlockEntity(); + return true; + } + + protected void createBlockEntity() { new BlockEntityDispenser(this.level.getChunk(getChunkX(), getChunkZ()), BlockEntity.getDefaultCompound(this, BlockEntity.DISPENSER)); - return true; + } + + protected InventoryHolder getBlockEntity() { + BlockEntity blockEntity = this.level.getBlockEntity(this); + + if (!(blockEntity instanceof BlockEntityDispenser)) { + return null; + } + + return (InventoryHolder) blockEntity; } @Override @@ -150,9 +165,9 @@ public int onUpdate(int type) { } public void dispense() { - BlockEntity blockEntity = this.level.getBlockEntity(this); + InventoryHolder blockEntity = getBlockEntity(); - if (!(blockEntity instanceof BlockEntityDispenser)) { + if (blockEntity == null) { return; } @@ -161,7 +176,7 @@ public void dispense() { int slot = -1; Item target = null; - Inventory inv = ((BlockEntityDispenser) blockEntity).getInventory(); + Inventory inv = blockEntity.getInventory(); for (Entry entry : inv.getContents().entrySet()) { Item item = entry.getValue(); @@ -178,7 +193,7 @@ public void dispense() { Item origin = target; target = target.clone(); - DispenseBehavior behavior = DispenseBehaviorRegister.getBehavior(target.getId()); + DispenseBehavior behavior = getDispenseBehavior(target); Item result = behavior.dispense(this, getBlockFace(), target); if (result == null) { @@ -199,6 +214,10 @@ public void dispense() { } } + protected DispenseBehavior getDispenseBehavior(Item item) { + return DispenseBehaviorRegister.getBehavior(item.getId()); + } + @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockDropper.java b/src/main/java/cn/nukkit/block/BlockDropper.java new file mode 100644 index 00000000000..83665f74023 --- /dev/null +++ b/src/main/java/cn/nukkit/block/BlockDropper.java @@ -0,0 +1,56 @@ +package cn.nukkit.block; + +import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityDropper; +import cn.nukkit.dispenser.DefaultDispenseBehavior; +import cn.nukkit.dispenser.DispenseBehavior; +import cn.nukkit.inventory.InventoryHolder; +import cn.nukkit.item.Item; + +public class BlockDropper extends BlockDispenser { + + public BlockDropper() { + this(0); + } + + public BlockDropper(int meta) { + super(meta); + } + + @Override + public String getName() { + return "Dropper"; + } + + @Override + public int getId() { + return DROPPER; + } + + @Override + public void dispense() { + super.dispense(); + } + + @Override + protected void createBlockEntity() { + new BlockEntityDropper(this.level.getChunk(getChunkX(), getChunkZ()), + BlockEntity.getDefaultCompound(this, BlockEntity.DROPPER)); + } + + @Override + protected InventoryHolder getBlockEntity() { + BlockEntity blockEntity = this.level.getBlockEntity(this); + + if (!(blockEntity instanceof BlockEntityDropper)) { + return null; + } + + return (InventoryHolder) blockEntity; + } + + @Override + protected DispenseBehavior getDispenseBehavior(Item item) { + return new DefaultDispenseBehavior(); + } +} diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 312553f98f3..e380167e4ee 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -411,7 +411,7 @@ private boolean addBlockLine(Block origin) { return false; } - if (nextBlock instanceof BlockFlowable) { + if (nextBlock instanceof BlockFlowable && !nextBlock.canBePushed()) { this.toDestroy.add(nextBlock); return true; } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java index b17985a1c31..6aa3054cd7c 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java @@ -43,6 +43,7 @@ public abstract class BlockEntity extends Position { public static final String SHULKER_BOX = "ShulkerBox"; public static final String BANNER = "Banner"; public static final String DISPENSER = "Dispenser"; + public static final String DROPPER = "Dropper"; public static long count = 1; diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java b/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java new file mode 100644 index 00000000000..6baec6cb645 --- /dev/null +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java @@ -0,0 +1,135 @@ +package cn.nukkit.blockentity; + +import cn.nukkit.block.BlockAir; +import cn.nukkit.block.BlockID; +import cn.nukkit.inventory.DropperInventory; +import cn.nukkit.inventory.InventoryHolder; +import cn.nukkit.item.Item; +import cn.nukkit.item.ItemBlock; +import cn.nukkit.level.format.FullChunk; +import cn.nukkit.nbt.NBTIO; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.ListTag; + +public class BlockEntityDropper extends BlockEntitySpawnable implements BlockEntityContainer, InventoryHolder, BlockEntityNameable { + + protected DropperInventory inventory; + + public BlockEntityDropper(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + } + + @Override + protected void initBlockEntity() { + this.inventory = new DropperInventory(this); + + if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) { + this.namedTag.putList(new ListTag("Items")); + } + + for (int i = 0; i < this.getSize(); i++) { + this.inventory.setItem(i, this.getItem(i)); + } + + super.initBlockEntity(); + } + + @Override + public int getSize() { + return 9; + } + + protected int getSlotIndex(int index) { + ListTag list = this.namedTag.getList("Items", CompoundTag.class); + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getByte("Slot") == index) { + return i; + } + } + + return -1; + } + + @Override + public Item getItem(int index) { + int i = this.getSlotIndex(index); + if (i < 0) { + return new ItemBlock(new BlockAir(), 0, 0); + } else { + CompoundTag data = (CompoundTag) this.namedTag.getList("Items").get(i); + return NBTIO.getItemHelper(data); + } + } + + @Override + public void setItem(int index, Item item) { + int i = this.getSlotIndex(index); + + CompoundTag d = NBTIO.putItemHelper(item, index); + + if (item.getId() == Item.AIR || item.getCount() <= 0) { + if (i >= 0) { + this.namedTag.getList("Items").getAll().remove(i); + } + } else if (i < 0) { + (this.namedTag.getList("Items", CompoundTag.class)).add(d); + } else { + (this.namedTag.getList("Items", CompoundTag.class)).add(i, d); + } + } + + @Override + public DropperInventory getInventory() { + return inventory; + } + + @Override + public CompoundTag getSpawnCompound() { + CompoundTag c = new CompoundTag() + .putString("id", BlockEntity.DROPPER) + .putInt("x", (int) this.x) + .putInt("y", (int) this.y) + .putInt("z", (int) this.z); + + if (this.hasName()) { + c.put("CustomName", this.namedTag.get("CustomName")); + } + + return c; + } + + @Override + public void saveNBT() { + this.namedTag.putList(new ListTag("Items")); + for (int index = 0; index < this.getSize(); index++) { + this.setItem(index, this.inventory.getItem(index)); + } + + super.saveNBT(); + } + + @Override + public boolean isBlockEntityValid() { + return this.getLevelBlock().getId() == BlockID.DROPPER; + } + + @Override + public String getName() { + return this.hasName() ? this.namedTag.getString("CustomName") : "Dropper"; + } + + @Override + public boolean hasName() { + return this.namedTag.contains("CustomName"); + } + + @Override + public void setName(String name) { + if (name == null || name.isEmpty()) { + this.namedTag.remove("CustomName"); + return; + } + + this.namedTag.putString("CustomName", name); + } +} diff --git a/src/main/java/cn/nukkit/inventory/DropperInventory.java b/src/main/java/cn/nukkit/inventory/DropperInventory.java new file mode 100644 index 00000000000..c349445a45b --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/DropperInventory.java @@ -0,0 +1,15 @@ +package cn.nukkit.inventory; + +import cn.nukkit.blockentity.BlockEntityDropper; + +public class DropperInventory extends ContainerInventory { + + public DropperInventory(BlockEntityDropper blockEntity) { + super(blockEntity, InventoryType.DROPPER); + } + + @Override + public BlockEntityDropper getHolder() { + return (BlockEntityDropper) super.getHolder(); + } +} From c9d3a55ca7d79063d1a395ad46276db921d9e741 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 13 Oct 2019 19:02:03 +0200 Subject: [PATCH 10/52] Bumb API version --- src/main/java/cn/nukkit/Nukkit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/Nukkit.java b/src/main/java/cn/nukkit/Nukkit.java index 8b9101a0ea7..ce0fea09dd0 100644 --- a/src/main/java/cn/nukkit/Nukkit.java +++ b/src/main/java/cn/nukkit/Nukkit.java @@ -39,7 +39,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.9"; + public final static String API_VERSION = "1.0.10"; public final static String CODENAME = ""; @Deprecated public final static String MINECRAFT_VERSION = ProtocolInfo.MINECRAFT_VERSION; From b0a1d5f41220a7d00db7463bccdb24f34f39ce3f Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Tue, 15 Oct 2019 19:17:44 +0200 Subject: [PATCH 11/52] allow more digits in the API version --- src/main/java/cn/nukkit/plugin/PluginManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/plugin/PluginManager.java b/src/main/java/cn/nukkit/plugin/PluginManager.java index dfb694a51cf..f62ba0d9fba 100644 --- a/src/main/java/cn/nukkit/plugin/PluginManager.java +++ b/src/main/java/cn/nukkit/plugin/PluginManager.java @@ -177,7 +177,7 @@ public Map loadPlugins(File dictionary, List newLoaders, try { //Check the format: majorVersion.minorVersion.patch - if (!Pattern.matches("[0-9]\\.[0-9]\\.[0-9]", version)) { + if (!Pattern.matches("^[0-9]+(\\.[0-9]+){0,2}$", version)) { throw new IllegalArgumentException(); } } catch (NullPointerException | IllegalArgumentException e) { From 717d44b0ae165d6932434c8f09dcb68473da7a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Bedn=C3=A1=C5=99?= Date: Wed, 16 Oct 2019 13:45:08 +0200 Subject: [PATCH 12/52] Add option to disable redstone --- src/main/java/cn/nukkit/Server.java | 11 ++++ .../java/cn/nukkit/block/BlockButton.java | 20 ++++-- .../java/cn/nukkit/block/BlockDispenser.java | 4 ++ src/main/java/cn/nukkit/block/BlockDoor.java | 10 ++- .../java/cn/nukkit/block/BlockFenceGate.java | 4 ++ .../java/cn/nukkit/block/BlockHopper.java | 14 +++-- src/main/java/cn/nukkit/block/BlockLever.java | 14 +++-- .../java/cn/nukkit/block/BlockPistonBase.java | 8 +++ .../nukkit/block/BlockPressurePlateBase.java | 4 ++ .../cn/nukkit/block/BlockRailPowered.java | 6 +- .../nukkit/block/BlockRedstoneComparator.java | 4 ++ .../cn/nukkit/block/BlockRedstoneDiode.java | 18 ++++-- .../cn/nukkit/block/BlockRedstoneLamp.java | 3 + .../cn/nukkit/block/BlockRedstoneLampLit.java | 4 ++ .../cn/nukkit/block/BlockRedstoneTorch.java | 18 ++++-- .../nukkit/block/BlockRedstoneTorchUnlit.java | 6 +- .../cn/nukkit/block/BlockRedstoneWire.java | 62 +++++++++++-------- src/main/java/cn/nukkit/block/BlockTNT.java | 4 ++ .../java/cn/nukkit/block/BlockTrapdoor.java | 2 +- .../java/cn/nukkit/block/BlockTripWire.java | 14 ++++- .../cn/nukkit/block/BlockTripWireHook.java | 4 ++ src/main/resources/lang | 2 +- 22 files changed, 178 insertions(+), 58 deletions(-) diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index ca76c33031c..3ade62f608c 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -157,6 +157,8 @@ public class Server { private boolean autoSave = true; + private boolean redstoneEnabled = true; + private RCON rcon; private EntityMetadataStore entityMetadata; @@ -388,6 +390,7 @@ public Level remove(Object key) { this.autoTickRateLimit = this.getConfig("level-settings.auto-tick-rate-limit", 20); this.alwaysTickPlayers = this.getConfig("level-settings.always-tick-players", false); this.baseTickRate = this.getConfig("level-settings.base-tick-rate", 1); + this.redstoneEnabled = this.getConfig("level-settings.tick-redstone", true); this.scheduler = new ServerScheduler(); @@ -2028,6 +2031,14 @@ public boolean isLanguageForced() { return forceLanguage; } + public boolean isRedstoneEnabled() { + return redstoneEnabled; + } + + public void setRedstoneEnabled(boolean redstoneEnabled) { + this.redstoneEnabled = redstoneEnabled; + } + public Network getNetwork() { return network; } diff --git a/src/main/java/cn/nukkit/block/BlockButton.java b/src/main/java/cn/nukkit/block/BlockButton.java index 08d2924c5a7..30810d610d6 100644 --- a/src/main/java/cn/nukkit/block/BlockButton.java +++ b/src/main/java/cn/nukkit/block/BlockButton.java @@ -54,15 +54,19 @@ public boolean onActivate(Item item, Player player) { return false; } - this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 0, 15)); + if (this.level.getServer().isRedstoneEnabled()) { + this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 0, 15)); + this.level.scheduleUpdate(this, 30); + + Vector3 pos = getLocation(); + + level.updateAroundRedstone(pos, null); + level.updateAroundRedstone(pos.getSide(getFacing().getOpposite()), null); + } + this.setDamage(this.getDamage() ^ 0x08); this.level.setBlock(this, this, true, false); this.level.addSound(this.add(0.5, 0.5, 0.5), Sound.RANDOM_CLICK); - this.level.scheduleUpdate(this, 30); - Vector3 pos = getLocation(); - - level.updateAroundRedstone(pos, null); - level.updateAroundRedstone(pos.getSide(getFacing().getOpposite()), null); return true; } @@ -74,6 +78,10 @@ public int onUpdate(int type) { return Level.BLOCK_UPDATE_NORMAL; } } else if (type == Level.BLOCK_UPDATE_SCHEDULED) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if (this.isActivated()) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 15, 0)); diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index f2db0624b45..bdb5b19b7bc 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -140,6 +140,10 @@ protected InventoryHolder getBlockEntity() { @Override public int onUpdate(int type) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if (type == Level.BLOCK_UPDATE_SCHEDULED) { this.setTriggered(false); this.level.setBlock(this, this, false, false); diff --git a/src/main/java/cn/nukkit/block/BlockDoor.java b/src/main/java/cn/nukkit/block/BlockDoor.java index 297bf58682a..a3bc09486dc 100644 --- a/src/main/java/cn/nukkit/block/BlockDoor.java +++ b/src/main/java/cn/nukkit/block/BlockDoor.java @@ -219,6 +219,10 @@ public int onUpdate(int type) { } if (type == Level.BLOCK_UPDATE_REDSTONE) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if ((!isOpen() && this.level.isBlockPowered(this.getLocation())) || (isOpen() && !this.level.isBlockPowered(this.getLocation()))) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, isOpen() ? 15 : 0, isOpen() ? 0 : 15)); @@ -252,8 +256,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl this.getLevel().setBlock(block, this, true, true); //Bottom this.getLevel().setBlock(blockUp, Block.get(this.getId(), metaUp), true); //Top - if (!this.isOpen() && this.level.isBlockPowered(this.getLocation())) { - this.toggle(null); + if (this.level.getServer().isRedstoneEnabled()) { + if (!this.isOpen() && this.level.isBlockPowered(this.getLocation())) { + this.toggle(null); + } } return true; } diff --git a/src/main/java/cn/nukkit/block/BlockFenceGate.java b/src/main/java/cn/nukkit/block/BlockFenceGate.java index 7784b7b0157..059159b1ecd 100644 --- a/src/main/java/cn/nukkit/block/BlockFenceGate.java +++ b/src/main/java/cn/nukkit/block/BlockFenceGate.java @@ -184,6 +184,10 @@ public boolean isOpen() { @Override public int onUpdate(int type) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if (type == Level.BLOCK_UPDATE_REDSTONE) { if ((!isOpen() && this.level.isBlockPowered(this.getLocation())) || (isOpen() && !this.level.isBlockPowered(this.getLocation()))) { this.toggle(null); diff --git a/src/main/java/cn/nukkit/block/BlockHopper.java b/src/main/java/cn/nukkit/block/BlockHopper.java index cf602dcac7e..a7230a144c6 100644 --- a/src/main/java/cn/nukkit/block/BlockHopper.java +++ b/src/main/java/cn/nukkit/block/BlockHopper.java @@ -56,10 +56,12 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl this.setDamage(facing.getIndex()); - boolean powered = this.level.isBlockPowered(this.getLocation()); + if (this.level.getServer().isRedstoneEnabled()) { + boolean powered = this.level.isBlockPowered(this.getLocation()); - if (powered == this.isEnabled()) { - this.setEnabled(!powered); + if (powered == this.isEnabled()) { + this.setEnabled(!powered); + } } this.level.setBlock(this, this); @@ -122,7 +124,11 @@ public void setEnabled(boolean enabled) { @Override public int onUpdate(int type) { - if (type == Level.BLOCK_UPDATE_NORMAL) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + + if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) { boolean powered = this.level.isBlockPowered(this.getLocation()); if (powered == this.isEnabled()) { diff --git a/src/main/java/cn/nukkit/block/BlockLever.java b/src/main/java/cn/nukkit/block/BlockLever.java index 9556ca13545..dfd984c7a7e 100644 --- a/src/main/java/cn/nukkit/block/BlockLever.java +++ b/src/main/java/cn/nukkit/block/BlockLever.java @@ -66,17 +66,21 @@ public boolean onActivate(Item item, Player player) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, isPowerOn() ? 15 : 0, isPowerOn() ? 0 : 15)); this.setDamage(this.getDamage() ^ 0x08); - this.getLevel().setBlock(this, this, false, false); + boolean redstone = this.level.getServer().isRedstoneEnabled(); + + this.getLevel().setBlock(this, this, false, !redstone); this.getLevel().addSound(this, Sound.RANDOM_CLICK); //TODO: correct pitch LeverOrientation orientation = LeverOrientation.byMetadata(this.isPowerOn() ? this.getDamage() ^ 0x08 : this.getDamage()); BlockFace face = orientation.getFacing(); - Block target = this.getSide(face.getOpposite()); - target.onUpdate(Level.BLOCK_UPDATE_REDSTONE); + if (redstone) { + Block target = this.getSide(face.getOpposite()); + target.onUpdate(Level.BLOCK_UPDATE_REDSTONE); - this.level.updateAroundRedstone(this.getLocation(), isPowerOn() ? face.getOpposite() : null); - this.level.updateAroundRedstone(target.getLocation(), isPowerOn() ? face : null); + this.level.updateAroundRedstone(this.getLocation(), isPowerOn() ? face.getOpposite() : null); + this.level.updateAroundRedstone(target.getLocation(), isPowerOn() ? face : null); + } return true; } diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index e380167e4ee..a978c0d2ba1 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -102,6 +102,10 @@ public int onUpdate(int type) { if (type != Level.BLOCK_UPDATE_NORMAL && type != Level.BLOCK_UPDATE_REDSTONE && type != Level.BLOCK_UPDATE_SCHEDULED) { return 0; } else { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + BlockEntity blockEntity = this.level.getBlockEntity(this); if (blockEntity instanceof BlockEntityPistonArm) { BlockEntityPistonArm arm = (BlockEntityPistonArm) blockEntity; @@ -125,6 +129,10 @@ public int onUpdate(int type) { } private boolean checkState(Boolean isPowered) { + if (!this.level.getServer().isRedstoneEnabled()) { + return false; + } + BlockFace facing = getBlockFace(); if (isPowered == null) { isPowered = this.isPowered(); diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java b/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java index b189a6f9679..c2d663468cc 100644 --- a/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java +++ b/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java @@ -115,6 +115,10 @@ protected AxisAlignedBB recalculateCollisionBoundingBox() { @Override public void onEntityCollide(Entity entity) { + if (!this.level.getServer().isRedstoneEnabled()) { + return; + } + int power = getRedstonePower(); if (power == 0) { diff --git a/src/main/java/cn/nukkit/block/BlockRailPowered.java b/src/main/java/cn/nukkit/block/BlockRailPowered.java index 83a88072314..272a732d4d3 100644 --- a/src/main/java/cn/nukkit/block/BlockRailPowered.java +++ b/src/main/java/cn/nukkit/block/BlockRailPowered.java @@ -44,6 +44,10 @@ public int onUpdate(int type) { if (super.onUpdate(type) == Level.BLOCK_UPDATE_NORMAL) { return 0; // Already broken } + + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } boolean wasPowered = isActive(); boolean isPowered = level.isBlockPowered(this.getLocation()) || checkSurrounding(this, true, 0) @@ -70,7 +74,7 @@ public int onUpdate(int type) { * @param power The count of the rail that had been counted * @return Boolean of the surrounding area. Where the powered rail on! */ - protected boolean checkSurrounding(Vector3 pos, boolean relative, int power) { + private boolean checkSurrounding(Vector3 pos, boolean relative, int power) { // The powered rail can power up to 8 blocks only if (power >= 8) { return false; diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java b/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java index c12a6a86f86..c1fb51c94c6 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java @@ -137,6 +137,10 @@ public int onUpdate(int type) { } private void onChange() { + if (!this.level.getServer().isRedstoneEnabled()) { + return; + } + int output = this.calculateOutput(); BlockEntity blockEntity = this.level.getBlockEntity(this); int currentOutput = 0; diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java index 795d5752ca6..795c6c6f0d1 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java @@ -30,8 +30,10 @@ public boolean onBreak(Item item) { Vector3 pos = getLocation(); this.level.setBlock(this, new BlockAir(), true, true); - for (BlockFace face : BlockFace.values()) { - this.level.updateAroundRedstone(pos.getSide(face), null); + if (this.level.getServer().isRedstoneEnabled()) { + for (BlockFace face : BlockFace.values()) { + this.level.updateAroundRedstone(pos.getSide(face), null); + } } return true; } @@ -45,8 +47,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl this.setDamage(player != null ? player.getDirection().getOpposite().getHorizontalIndex() : 0); this.level.setBlock(block, this, true, true); - if (shouldBePowered()) { - this.level.scheduleUpdate(this, 1); + if (this.level.getServer().isRedstoneEnabled()) { + if (shouldBePowered()) { + this.level.scheduleUpdate(this, 1); + } } return true; } @@ -54,6 +58,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl @Override public int onUpdate(int type) { if (type == Level.BLOCK_UPDATE_SCHEDULED) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if (!this.isLocked()) { Vector3 pos = getLocation(); boolean shouldBePowered = this.shouldBePowered(); @@ -82,7 +90,7 @@ public int onUpdate(int type) { if (type == Level.BLOCK_UPDATE_NORMAL && this.getSide(BlockFace.DOWN).isTransparent()) { this.level.useBreakOn(this); return Level.BLOCK_UPDATE_NORMAL; - } else { + } else if (this.level.getServer().isRedstoneEnabled()) { this.updateState(); return Level.BLOCK_UPDATE_NORMAL; } diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java b/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java index a984441b0c2..dd278b47975 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java @@ -54,6 +54,9 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl @Override public int onUpdate(int type) { if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } // Redstone event RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this); getLevel().getServer().getPluginManager().callEvent(ev); diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java b/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java index 7d2b6ee7d69..02020fd640c 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java @@ -35,6 +35,10 @@ public Item toItem() { @Override public int onUpdate(int type) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if ((type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) && !this.level.isBlockPowered(this.getLocation())) { // Redstone event RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this); diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java b/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java index 451cfe8b678..aeaa3a71693 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java @@ -78,12 +78,14 @@ public boolean onBreak(Item item) { BlockFace face = getBlockFace().getOpposite(); - for (BlockFace side : BlockFace.values()) { - if (side == face) { - continue; - } + if (this.level.getServer().isRedstoneEnabled()) { + for (BlockFace side : BlockFace.values()) { + if (side == face) { + continue; + } - this.level.updateAroundRedstone(pos.getSide(side), null); + this.level.updateAroundRedstone(pos.getSide(side), null); + } } return true; } @@ -91,6 +93,10 @@ public boolean onBreak(Item item) { @Override public int onUpdate(int type) { if (super.onUpdate(type) == 0) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) { this.level.scheduleUpdate(this, tickRate()); } else if (type == Level.BLOCK_UPDATE_SCHEDULED) { @@ -110,7 +116,7 @@ public int onUpdate(int type) { return 0; } - protected boolean checkState() { + private boolean checkState() { if (isPoweredFromSide()) { BlockFace face = getBlockFace().getOpposite(); Vector3 pos = getLocation(); diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java b/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java index 880fd0ac3fb..e6be8d1e619 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java @@ -53,6 +53,10 @@ public Item toItem() { @Override public int onUpdate(int type) { if (super.onUpdate(type) == 0) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) { this.level.scheduleUpdate(this, tickRate()); } else if (type == Level.BLOCK_UPDATE_SCHEDULED) { @@ -71,7 +75,7 @@ public int onUpdate(int type) { return 0; } - protected boolean checkState() { + private boolean checkState() { BlockFace face = getBlockFace().getOpposite(); Vector3 pos = getLocation(); diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java index 2d058a000aa..3747a0a47c3 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java @@ -53,26 +53,31 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl return false; } - this.getLevel().setBlock(block, this, true, false); - this.updateSurroundingRedstone(true); - Vector3 pos = getLocation(); + if (this.level.getServer().isRedstoneEnabled()) { + this.getLevel().setBlock(block, this, true, false); - for (BlockFace blockFace : Plane.VERTICAL) { - this.level.updateAroundRedstone(pos.getSide(blockFace), blockFace.getOpposite()); - } + this.updateSurroundingRedstone(true); + Vector3 pos = getLocation(); - for (BlockFace blockFace : Plane.VERTICAL) { - this.updateAround(pos.getSide(blockFace), blockFace.getOpposite()); - } + for (BlockFace blockFace : Plane.VERTICAL) { + this.level.updateAroundRedstone(pos.getSide(blockFace), blockFace.getOpposite()); + } + + for (BlockFace blockFace : Plane.VERTICAL) { + this.updateAround(pos.getSide(blockFace), blockFace.getOpposite()); + } - for (BlockFace blockFace : Plane.HORIZONTAL) { - Vector3 v = pos.getSide(blockFace); + for (BlockFace blockFace : Plane.HORIZONTAL) { + Vector3 v = pos.getSide(blockFace); - if (this.level.getBlock(v).isNormalBlock()) { - this.updateAround(v.up(), BlockFace.DOWN); - } else { - this.updateAround(v.down(), BlockFace.UP); + if (this.level.getBlock(v).isNormalBlock()) { + this.updateAround(v.up(), BlockFace.DOWN); + } else { + this.updateAround(v.down(), BlockFace.UP); + } } + } else { + this.getLevel().setBlock(block, this, true, true); } return true; } @@ -172,19 +177,21 @@ public boolean onBreak(Item item) { Vector3 pos = getLocation(); - this.updateSurroundingRedstone(false); + if (this.level.getServer().isRedstoneEnabled()) { + this.updateSurroundingRedstone(false); - for (BlockFace blockFace : BlockFace.values()) { - this.level.updateAroundRedstone(pos.getSide(blockFace), null); - } + for (BlockFace blockFace : BlockFace.values()) { + this.level.updateAroundRedstone(pos.getSide(blockFace), null); + } - for (BlockFace blockFace : Plane.HORIZONTAL) { - Vector3 v = pos.getSide(blockFace); + for (BlockFace blockFace : Plane.HORIZONTAL) { + Vector3 v = pos.getSide(blockFace); - if (this.level.getBlock(v).isNormalBlock()) { - this.updateAround(v.up(), BlockFace.DOWN); - } else { - this.updateAround(v.down(), BlockFace.UP); + if (this.level.getBlock(v).isNormalBlock()) { + this.updateAround(v.up(), BlockFace.DOWN); + } else { + this.updateAround(v.down(), BlockFace.UP); + } } } return true; @@ -205,6 +212,11 @@ public int onUpdate(int type) { if (type != Level.BLOCK_UPDATE_NORMAL && type != Level.BLOCK_UPDATE_REDSTONE) { return 0; } + + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + // Redstone event RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this); getLevel().getServer().getPluginManager().callEvent(ev); diff --git a/src/main/java/cn/nukkit/block/BlockTNT.java b/src/main/java/cn/nukkit/block/BlockTNT.java index e887d48199d..1a1b22060a6 100644 --- a/src/main/java/cn/nukkit/block/BlockTNT.java +++ b/src/main/java/cn/nukkit/block/BlockTNT.java @@ -91,6 +91,10 @@ public void prime(int fuse, Entity source) { @Override public int onUpdate(int type) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if ((type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) && this.level.isBlockPowered(this.getLocation())) { this.prime(); } diff --git a/src/main/java/cn/nukkit/block/BlockTrapdoor.java b/src/main/java/cn/nukkit/block/BlockTrapdoor.java index e84ad84e06c..1c17ff4d846 100644 --- a/src/main/java/cn/nukkit/block/BlockTrapdoor.java +++ b/src/main/java/cn/nukkit/block/BlockTrapdoor.java @@ -162,7 +162,7 @@ public double getMaxZ() { @Override public int onUpdate(int type) { - if (type == Level.BLOCK_UPDATE_REDSTONE) { + if (type == Level.BLOCK_UPDATE_REDSTONE && this.level.getServer().isRedstoneEnabled()) { if ((!isOpen() && this.level.isBlockPowered(this.getLocation())) || (isOpen() && !this.level.isBlockPowered(this.getLocation()))) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, isOpen() ? 15 : 0, isOpen() ? 0 : 15)); this.setDamage(this.getDamage() ^ 0x08); diff --git a/src/main/java/cn/nukkit/block/BlockTripWire.java b/src/main/java/cn/nukkit/block/BlockTripWire.java index 2619f415224..0de4c1295d1 100644 --- a/src/main/java/cn/nukkit/block/BlockTripWire.java +++ b/src/main/java/cn/nukkit/block/BlockTripWire.java @@ -88,6 +88,10 @@ public void setDisarmed(boolean value) { @Override public void onEntityCollide(Entity entity) { + if (!this.level.getServer().isRedstoneEnabled()) { + return; + } + if (!entity.doesTriggerPressurePlate()) { return; } @@ -103,7 +107,11 @@ public void onEntityCollide(Entity entity) { } } - public void updateHook(boolean scheduleUpdate) { + private void updateHook(boolean scheduleUpdate) { + if (!this.level.getServer().isRedstoneEnabled()) { + return; + } + for (BlockFace side : new BlockFace[]{BlockFace.SOUTH, BlockFace.WEST}) { for (int i = 1; i < 42; ++i) { Block block = this.getSide(side, i); @@ -130,6 +138,10 @@ public void updateHook(boolean scheduleUpdate) { @Override public int onUpdate(int type) { + if (!this.level.getServer().isRedstoneEnabled()) { + return 0; + } + if (type == Level.BLOCK_UPDATE_SCHEDULED) { if (!isPowered()) { return type; diff --git a/src/main/java/cn/nukkit/block/BlockTripWireHook.java b/src/main/java/cn/nukkit/block/BlockTripWireHook.java index 80f30023043..0b1ab9c571f 100644 --- a/src/main/java/cn/nukkit/block/BlockTripWireHook.java +++ b/src/main/java/cn/nukkit/block/BlockTripWireHook.java @@ -89,6 +89,10 @@ public boolean onBreak(Item item) { } public void calculateState(boolean onBreak, boolean updateAround, int pos, Block block) { + if (!this.level.getServer().isRedstoneEnabled()) { + return; + } + BlockFace facing = getFacing(); Vector3 v = this.getLocation(); boolean attached = isAttached(); diff --git a/src/main/resources/lang b/src/main/resources/lang index c63bafbc9ae..29847f208f7 160000 --- a/src/main/resources/lang +++ b/src/main/resources/lang @@ -1 +1 @@ -Subproject commit c63bafbc9aea902cac375e48d13944c4232a22c5 +Subproject commit 29847f208f75811ba8a941cffeb459bf3aa26b29 From b679e7093511f82b61956a810e4a2776864d6d7c Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 20 Oct 2019 14:17:59 +0200 Subject: [PATCH 13/52] fix submodule --- src/main/resources/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/lang b/src/main/resources/lang index 29847f208f7..3ff7b21cb59 160000 --- a/src/main/resources/lang +++ b/src/main/resources/lang @@ -1 +1 @@ -Subproject commit 29847f208f75811ba8a941cffeb459bf3aa26b29 +Subproject commit 3ff7b21cb59ddebc6bd551032e8b60c8d1df94e2 From 38820ae0b285258874ce3453c9137dbbe7653a92 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 20 Oct 2019 20:19:30 +0200 Subject: [PATCH 14/52] test --- .gitmodules | 3 --- src/main/resources/lang | 1 - 2 files changed, 4 deletions(-) delete mode 160000 src/main/resources/lang diff --git a/.gitmodules b/.gitmodules index febb0db9cb2..e69de29bb2d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "src/main/resources/lang"] - path = src/main/resources/lang - url = https://github.com/NukkitX/Languages.git diff --git a/src/main/resources/lang b/src/main/resources/lang deleted file mode 160000 index 3ff7b21cb59..00000000000 --- a/src/main/resources/lang +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3ff7b21cb59ddebc6bd551032e8b60c8d1df94e2 From 3d7e846d4277c7d6e9ca7526751095a13ef21986 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Mon, 21 Oct 2019 07:24:40 +0200 Subject: [PATCH 15/52] test --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index e69de29bb2d..febb0db9cb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/main/resources/lang"] + path = src/main/resources/lang + url = https://github.com/NukkitX/Languages.git From 9004211866be8dbc0540d1770b217ac71cc00823 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Mon, 21 Oct 2019 07:28:04 +0200 Subject: [PATCH 16/52] Fix submodule --- src/main/resources/lang | 1 + 1 file changed, 1 insertion(+) create mode 160000 src/main/resources/lang diff --git a/src/main/resources/lang b/src/main/resources/lang new file mode 160000 index 00000000000..49caa033925 --- /dev/null +++ b/src/main/resources/lang @@ -0,0 +1 @@ +Subproject commit 49caa033925b316d6a5738c6cb221253c4f70dc9 From 9a38c49658a434913b2b57e4ce2ab36e981b6e9d Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Tue, 22 Oct 2019 16:26:37 +0200 Subject: [PATCH 17/52] Fix duplication bug --- .../java/cn/nukkit/block/BlockButton.java | 19 +++++++++---------- .../blockentity/BlockEntityPistonArm.java | 7 ++----- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockButton.java b/src/main/java/cn/nukkit/block/BlockButton.java index 30810d610d6..9150302436c 100644 --- a/src/main/java/cn/nukkit/block/BlockButton.java +++ b/src/main/java/cn/nukkit/block/BlockButton.java @@ -54,9 +54,10 @@ public boolean onActivate(Item item, Player player) { return false; } + this.level.scheduleUpdate(this, 30); + if (this.level.getServer().isRedstoneEnabled()) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 0, 15)); - this.level.scheduleUpdate(this, 30); Vector3 pos = getLocation(); @@ -78,20 +79,18 @@ public int onUpdate(int type) { return Level.BLOCK_UPDATE_NORMAL; } } else if (type == Level.BLOCK_UPDATE_SCHEDULED) { - if (!this.level.getServer().isRedstoneEnabled()) { - return 0; - } - if (this.isActivated()) { - this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 15, 0)); - this.setDamage(this.getDamage() ^ 0x08); this.level.setBlock(this, this, true, false); this.level.addSound(this.add(0.5, 0.5, 0.5), Sound.RANDOM_CLICK); - Vector3 pos = getLocation(); - level.updateAroundRedstone(pos, null); - level.updateAroundRedstone(pos.getSide(getFacing().getOpposite()), null); + if (!this.level.getServer().isRedstoneEnabled()) { + this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 15, 0)); + + Vector3 pos = getLocation(); + level.updateAroundRedstone(pos, null); + level.updateAroundRedstone(pos.getSide(getFacing().getOpposite()), null); + } } return Level.BLOCK_UPDATE_SCHEDULED; diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index ffc0bcb3663..527d89899c2 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -5,7 +5,6 @@ import cn.nukkit.block.BlockAir; import cn.nukkit.block.BlockID; import cn.nukkit.entity.Entity; -import cn.nukkit.level.Level; import cn.nukkit.level.format.FullChunk; import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.BlockFace; @@ -27,7 +26,7 @@ public class BlockEntityPistonArm extends BlockEntitySpawnable { public static final float MOVE_STEP = Float.valueOf(0.5f); public float progress; - public float lastProgress = 1.0F; + public float lastProgress = 1; public BlockFace facing; public boolean extending; public boolean sticky; @@ -166,8 +165,6 @@ public boolean onUpdate() { movingBlock.close(); Block moved = ((BlockEntityMovingBlock) movingBlock).getMovingBlock(); - this.level.setBlock(movingBlock, moved); - CompoundTag blockEntity = ((BlockEntityMovingBlock) movingBlock).getBlockEntity(); if (blockEntity != null) { @@ -177,7 +174,7 @@ public boolean onUpdate() { BlockEntity.createBlockEntity(blockEntity.getString("id"), this.level.getChunk(movingBlock.getChunkX(), movingBlock.getChunkZ()), blockEntity); } - moved.onUpdate(Level.BLOCK_UPDATE_NORMAL); + this.level.setBlock(movingBlock, moved); } } From 9ab951fb3f0d5a4930912e7a5c696288ba8658ab Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Tue, 22 Oct 2019 16:42:34 +0200 Subject: [PATCH 18/52] Fix dispenser duplication --- src/main/java/cn/nukkit/block/BlockDispenser.java | 8 ++++---- .../java/cn/nukkit/blockentity/BlockEntityDispenser.java | 7 +++++++ .../java/cn/nukkit/blockentity/BlockEntityDropper.java | 7 +++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index bdb5b19b7bc..6d9b2da18ab 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -200,10 +200,10 @@ public void dispense() { DispenseBehavior behavior = getDispenseBehavior(target); Item result = behavior.dispense(this, getBlockFace(), target); - if (result == null) { - target.count--; - inv.setItem(slot, target); - } else { + target.count--; + inv.setItem(slot, target); + + if (result != null) { if (result.getId() != origin.getId() || result.getDamage() != origin.getDamage()) { Item[] fit = inv.addItem(result); diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java b/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java index 1df276eb105..0f19bec7ab1 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java @@ -132,4 +132,11 @@ public void setName(String name) { this.namedTag.putString("CustomName", name); } + + @Override + public void onBreak() { + for (Item content : inventory.getContents().values()) { + level.dropItem(this, content); + } + } } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java b/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java index 6baec6cb645..5cae1660f38 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java @@ -132,4 +132,11 @@ public void setName(String name) { this.namedTag.putString("CustomName", name); } + + @Override + public void onBreak() { + for (Item content : inventory.getContents().values()) { + level.dropItem(this, content); + } + } } From d323b523e16a216864bb4546ed0f26d53fcb4b64 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Tue, 22 Oct 2019 17:20:59 +0200 Subject: [PATCH 19/52] Fix compatibility --- src/main/java/cn/nukkit/block/BlockDispenser.java | 1 + .../java/cn/nukkit/dispenser/DefaultDispenseBehavior.java | 4 +++- src/main/java/cn/nukkit/item/Item.java | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index 6d9b2da18ab..6b7c55a12b6 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -194,6 +194,7 @@ public void dispense() { // this.level.addLevelSoundEvent(this); //TODO: sound return; } + Item origin = target; target = target.clone(); diff --git a/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java b/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java index f88956d80e1..e24a39006b2 100644 --- a/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java +++ b/src/main/java/cn/nukkit/dispenser/DefaultDispenseBehavior.java @@ -39,8 +39,10 @@ public Item dispense(BlockDispenser block, BlockFace face, Item item) { motion.y += rand.nextGaussian() * 0.007499999832361937 * 6; motion.z += rand.nextGaussian() * 0.007499999832361937 * 6; + Item clone = item.clone(); + clone.count = 1; - block.level.dropItem(dispensePos, item.clone().setCount(1), motion); + block.level.dropItem(dispensePos, clone, motion); return null; } diff --git a/src/main/java/cn/nukkit/item/Item.java b/src/main/java/cn/nukkit/item/Item.java index db84d80ba32..a13d80f235f 100644 --- a/src/main/java/cn/nukkit/item/Item.java +++ b/src/main/java/cn/nukkit/item/Item.java @@ -811,9 +811,8 @@ public int getCount() { return count; } - public Item setCount(int count) { + public void setCount(int count) { this.count = count; - return this; } public boolean isNull() { From 64de48559ad17fa989bfdc3ce35cbdc432bf5fbf Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Tue, 22 Oct 2019 18:55:11 +0200 Subject: [PATCH 20/52] Prevent NPE --- .../cn/nukkit/blockentity/BlockEntityMovingBlock.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java index 998688784a3..64bd3214224 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java @@ -60,7 +60,13 @@ public String getMovingBlockString() { } public void moveCollidedEntities(BlockEntityPistonArm piston, BlockFace moveDirection) { - AxisAlignedBB bb = block.getBoundingBox().getOffsetBoundingBox( + AxisAlignedBB bb = block.getBoundingBox(); + + if (bb == null) { + return; + } + + bb = bb.getOffsetBoundingBox( this.x + (piston.progress * moveDirection.getXOffset()) - moveDirection.getXOffset(), this.y + (piston.progress * moveDirection.getYOffset()) - moveDirection.getYOffset(), this.z + (piston.progress * moveDirection.getZOffset()) - moveDirection.getZOffset() From bd2d2bafe8c3a16aa24c342757755effd0537ef7 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Wed, 23 Oct 2019 18:02:52 +0200 Subject: [PATCH 21/52] Add BlockPistonEvent --- .../java/cn/nukkit/block/BlockPistonBase.java | 11 +++- .../event/block/BlockPistonChangeEvent.java | 3 ++ .../nukkit/event/block/BlockPistonEvent.java | 53 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/java/cn/nukkit/event/block/BlockPistonEvent.java diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index a978c0d2ba1..ed85db70c9d 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -5,6 +5,7 @@ import cn.nukkit.blockentity.BlockEntityMovingBlock; import cn.nukkit.blockentity.BlockEntityPistonArm; import cn.nukkit.event.block.BlockPistonChangeEvent; +import cn.nukkit.event.block.BlockPistonEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; import cn.nukkit.level.GlobalBlockPalette; @@ -139,7 +140,8 @@ private boolean checkState(Boolean isPowered) { } if (isPowered && !isExtended()) { - if (new BlocksCalculator(true).canMove()) { + BlocksCalculator calculator = new BlocksCalculator(true); + if (calculator.canMove()) { if (!this.doMove(true)) { return false; } @@ -213,6 +215,13 @@ private boolean doMove(boolean extending) { } else { List attached = Collections.emptyList(); + BlockPistonEvent event = new BlockPistonEvent(this, direction, calculator.getBlocksToMove(), calculator.getBlocksToDestroy(), extending); + this.level.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + if (this.sticky || extending) { List destroyBlocks = calculator.getBlocksToDestroy(); for (int i = destroyBlocks.size() - 1; i >= 0; --i) { diff --git a/src/main/java/cn/nukkit/event/block/BlockPistonChangeEvent.java b/src/main/java/cn/nukkit/event/block/BlockPistonChangeEvent.java index ab5d0184f07..81930ae1ae7 100644 --- a/src/main/java/cn/nukkit/event/block/BlockPistonChangeEvent.java +++ b/src/main/java/cn/nukkit/event/block/BlockPistonChangeEvent.java @@ -5,7 +5,10 @@ /** * Created by CreeperFace on 2.8.2017. + * + * @deprecated Use BlockPistonEvent */ +@Deprecated public class BlockPistonChangeEvent extends BlockEvent { private static final HandlerList handlers = new HandlerList(); diff --git a/src/main/java/cn/nukkit/event/block/BlockPistonEvent.java b/src/main/java/cn/nukkit/event/block/BlockPistonEvent.java new file mode 100644 index 00000000000..2d1cc3debd7 --- /dev/null +++ b/src/main/java/cn/nukkit/event/block/BlockPistonEvent.java @@ -0,0 +1,53 @@ +package cn.nukkit.event.block; + +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockPistonBase; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.math.BlockFace; + +import java.util.ArrayList; +import java.util.List; + +public class BlockPistonEvent extends BlockEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final BlockFace direction; + private final List blocks; + private final List destroyedBlocks; + private final boolean extending; + + public BlockPistonEvent(BlockPistonBase piston, BlockFace direction, List blocks, List destroyedBlocks, boolean extending) { + super(piston); + this.direction = direction; + this.blocks = blocks; + this.destroyedBlocks = destroyedBlocks; + this.extending = extending; + } + + public BlockFace getDirection() { + return direction; + } + + public List getBlocks() { + return new ArrayList<>(blocks); + } + + public List getDestroyedBlocks() { + return destroyedBlocks; + } + + public boolean isExtending() { + return extending; + } + + @Override + public BlockPistonBase getBlock() { + return (BlockPistonBase) super.getBlock(); + } +} From f1360bea2158d5713009d7f59a0cebc97500afa6 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Thu, 24 Oct 2019 19:02:37 +0200 Subject: [PATCH 22/52] Fix comparator updates --- .../cn/nukkit/blockentity/BlockEntity.java | 5 +++ .../cn/nukkit/inventory/BaseInventory.java | 31 +++++++++++++++++-- .../java/cn/nukkit/inventory/Inventory.java | 4 +++ .../nukkit/inventory/InventoryListener.java | 8 +++++ src/main/java/cn/nukkit/level/Level.java | 4 +++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/main/java/cn/nukkit/inventory/InventoryListener.java diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java index 6aa3054cd7c..c29e0aa034c 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java @@ -2,6 +2,7 @@ import cn.nukkit.Server; import cn.nukkit.block.Block; +import cn.nukkit.block.BlockID; import cn.nukkit.level.Position; import cn.nukkit.level.format.FullChunk; import cn.nukkit.math.Vector3; @@ -207,6 +208,10 @@ public void onBreak() { public void setDirty() { chunk.setChanged(); + + if (this.getLevelBlock().getId() != BlockID.AIR) { + this.level.updateComparatorOutputLevel(this); + } } public String getName() { diff --git a/src/main/java/cn/nukkit/inventory/BaseInventory.java b/src/main/java/cn/nukkit/inventory/BaseInventory.java index ee24195ab21..92ced4f4199 100644 --- a/src/main/java/cn/nukkit/inventory/BaseInventory.java +++ b/src/main/java/cn/nukkit/inventory/BaseInventory.java @@ -36,6 +36,8 @@ public abstract class BaseInventory implements Inventory { protected InventoryHolder holder; + private List listeners; + public BaseInventory(InventoryHolder holder, InventoryType type) { this(holder, type, new HashMap<>()); } @@ -379,9 +381,6 @@ public boolean clear(int index, boolean send) { } item = ev.getNewItem(); } - if (holder instanceof BlockEntity) { - ((BlockEntity) holder).setDirty(); - } if (item.getId() != Item.AIR) { this.slots.put(index, item.clone()); @@ -449,6 +448,16 @@ public void onSlotChange(int index, Item before, boolean send) { if (send) { this.sendSlot(index, this.getViewers()); } + + if (holder instanceof BlockEntity) { + ((BlockEntity) holder).setDirty(); + } + + if (this.listeners != null) { + for (InventoryListener listener : listeners) { + listener.onInventoryChanged(this, before, index); + } + } } @Override @@ -555,6 +564,22 @@ public void sendSlot(int index, Collection players) { this.sendSlot(index, players.toArray(new Player[0])); } + @Override + public void addListener(InventoryListener listener) { + if (this.listeners == null) { + this.listeners = new ArrayList<>(); + } + + this.listeners.add(listener); + } + + @Override + public void removeListener(InventoryListener listener) { + if (this.listeners != null) { + this.listeners.remove(listener); + } + } + @Override public InventoryType getType() { return type; diff --git a/src/main/java/cn/nukkit/inventory/Inventory.java b/src/main/java/cn/nukkit/inventory/Inventory.java index 9b62bfd3bf8..1d8c90feeae 100644 --- a/src/main/java/cn/nukkit/inventory/Inventory.java +++ b/src/main/java/cn/nukkit/inventory/Inventory.java @@ -98,4 +98,8 @@ default boolean clear(int index) { void onClose(Player who); void onSlotChange(int index, Item before, boolean send); + + void addListener(InventoryListener listener); + + void removeListener(InventoryListener listener); } diff --git a/src/main/java/cn/nukkit/inventory/InventoryListener.java b/src/main/java/cn/nukkit/inventory/InventoryListener.java new file mode 100644 index 00000000000..889b24bfc26 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/InventoryListener.java @@ -0,0 +1,8 @@ +package cn.nukkit.inventory; + +import cn.nukkit.item.Item; + +public interface InventoryListener { + + void onInventoryChanged(Inventory inventory, Item oldItem, int slot); +} diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index 73169167621..45dee8d1829 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -1692,6 +1692,10 @@ public synchronized boolean setBlock(int x, int y, int z, Block block, boolean d block = ev.getBlock(); block.onUpdate(BLOCK_UPDATE_NORMAL); this.updateAround(x, y, z); + + if (block.hasComparatorInputOverride()) { + this.updateComparatorOutputLevel(block); + } } } return true; From 23adeaa06d6d289a59439c8363aef4614751371b Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Thu, 24 Oct 2019 20:50:47 +0200 Subject: [PATCH 23/52] Fix hopper disabled behavior --- src/main/java/cn/nukkit/block/BlockDispenser.java | 6 +++--- src/main/java/cn/nukkit/block/BlockHopper.java | 10 +++++++++- .../java/cn/nukkit/blockentity/BlockEntityHopper.java | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index 6b7c55a12b6..aba6486cd45 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -54,10 +54,10 @@ public Item toItem() { @Override public int getComparatorInputOverride() { - BlockEntity blockEntity = this.level.getBlockEntity(this); + InventoryHolder blockEntity = this.getBlockEntity(); - if (blockEntity instanceof BlockEntityDispenser) { - return ContainerInventory.calculateRedstone(((BlockEntityDispenser) blockEntity).getInventory()); + if (blockEntity != null) { + return ContainerInventory.calculateRedstone(blockEntity.getInventory()); } return 0; diff --git a/src/main/java/cn/nukkit/block/BlockHopper.java b/src/main/java/cn/nukkit/block/BlockHopper.java index a7230a144c6..d8a41be6823 100644 --- a/src/main/java/cn/nukkit/block/BlockHopper.java +++ b/src/main/java/cn/nukkit/block/BlockHopper.java @@ -133,7 +133,15 @@ public int onUpdate(int type) { if (powered == this.isEnabled()) { this.setEnabled(!powered); - this.level.setBlock(this, this, true, false); + this.level.setBlock(this, this, false, false); + + if (!powered) { + BlockEntity be = this.level.getBlockEntity(this); + + if (be != null) { + be.scheduleUpdate(); + } + } } return type; diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java index b6ebec2ec91..45efebe2235 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java @@ -158,6 +158,10 @@ public boolean onUpdate() { this.transferCooldown--; if (!this.isOnTransferCooldown()) { + if ((this.level.getBlockDataAt(getFloorX(), getFloorY(), getFloorZ()) & 0x08) == 8) { //is hopper disabled? + return false; + } + BlockEntity blockEntity = this.level.getBlockEntity(this.up()); boolean changed = pushItems(); From 4f2b31c30c9fe811dd27b4aff8d8ff03f6933dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Bedn=C3=A1=C5=99?= Date: Tue, 29 Oct 2019 21:45:47 +0100 Subject: [PATCH 24/52] Fix default facing --- src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index 527d89899c2..4479b5940b4 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -61,6 +61,8 @@ protected void initBlockEntity() { if (b instanceof Faceable) { this.facing = ((Faceable) b).getBlockFace(); + } else { + this.facing = BlockFace.NORTH; } } From 3a210f7c8cf032e5b718ecb05ca759359f2cb30f Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Thu, 31 Oct 2019 21:54:57 +0100 Subject: [PATCH 25/52] Add sounds and particles --- .../java/cn/nukkit/block/BlockDispenser.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index aba6486cd45..7ed473e1c77 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -13,6 +13,7 @@ import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; import cn.nukkit.math.Vector3; +import cn.nukkit.network.protocol.LevelEventPacket; import cn.nukkit.utils.Faceable; import java.util.Map.Entry; @@ -190,16 +191,39 @@ public void dispense() { } } + LevelEventPacket pk = new LevelEventPacket(); + + BlockFace facing = getBlockFace(); + + pk.x = 0.5f + facing.getXOffset() * 0.7f; + pk.y = 0.5f + facing.getYOffset() * 0.7f; + pk.z = 0.5f + facing.getZOffset() * 0.7f; + if (target == null) { -// this.level.addLevelSoundEvent(this); //TODO: sound + pk.evid = LevelEventPacket.EVENT_SOUND_CLICK_FAIL; + pk.data = 1200; + + this.level.addChunkPacket(getChunkX(), getChunkZ(), pk.clone()); return; + } else { + pk.evid = LevelEventPacket.EVENT_SOUND_CLICK; + pk.data = 1000; + + this.level.addChunkPacket(getChunkX(), getChunkZ(), pk.clone()); } + pk.evid = LevelEventPacket.EVENT_PARTICLE_SHOOT; + pk.data = 7; + this.level.addChunkPacket(getChunkX(), getChunkZ(), pk); + Item origin = target; target = target.clone(); DispenseBehavior behavior = getDispenseBehavior(target); - Item result = behavior.dispense(this, getBlockFace(), target); + Item result = behavior.dispense(this, facing, target); + + + pk.evid = LevelEventPacket.EVENT_SOUND_CLICK; target.count--; inv.setItem(slot, target); From 56c0840f010cb4ef016c937eafa9a1774199a36c Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 3 Nov 2019 16:16:24 +0100 Subject: [PATCH 26/52] Merge 1.13 --- src/main/java/cn/nukkit/Player.java | 75 +- .../java/cn/nukkit/block/BlockLeaves2.java | 2 +- .../blockentity/BlockEntityFurnace.java | 2 - .../java/cn/nukkit/entity/EntityHuman.java | 84 +- src/main/java/cn/nukkit/entity/data/Skin.java | 163 +- .../transaction/data/UseItemData.java | 2 + src/main/java/cn/nukkit/item/Item.java | 5 + .../nukkit/item/ItemAppleGoldEnchanted.java | 8 + src/main/java/cn/nukkit/item/ItemBucket.java | 28 + src/main/java/cn/nukkit/item/ItemEdible.java | 39 + src/main/java/cn/nukkit/level/Explosion.java | 9 - .../cn/nukkit/level/GlobalBlockPalette.java | 62 +- src/main/java/cn/nukkit/level/Sound.java | 30 +- src/main/java/cn/nukkit/nbt/NBTIO.java | 16 +- src/main/java/cn/nukkit/nbt/tag/ListTag.java | 2 +- src/main/java/cn/nukkit/network/Network.java | 2 +- .../protocol/CompletedUsingItemPacket.java | 46 + .../network/protocol/EntityEventPacket.java | 2 +- .../network/protocol/ExplodePacket.java | 50 - .../nukkit/network/protocol/LoginPacket.java | 67 +- .../network/protocol/PlayerListPacket.java | 10 +- .../network/protocol/PlayerSkinPacket.java | 19 +- .../nukkit/network/protocol/ProtocolInfo.java | 17 +- .../protocol/ResourcePackChunkDataPacket.java | 5 +- .../protocol/ResourcePackDataInfoPacket.java | 16 +- .../protocol/ResourcePackStackPacket.java | 4 +- .../network/protocol/RespawnPacket.java | 10 + .../network/protocol/StartGamePacket.java | 8 +- .../java/cn/nukkit/utils/BinaryStream.java | 55 +- .../java/cn/nukkit/utils/SerializedImage.java | 37 + .../java/cn/nukkit/utils/SkinAnimation.java | 16 + src/main/resources/biome_definitions.dat | Bin 3413 -> 27359 bytes src/main/resources/creativeitems.json | 3829 +- src/main/resources/entity_identifiers.dat | Bin 8249 -> 8403 bytes src/main/resources/recipes.json | 45015 ++++++++-------- src/main/resources/runtime_block_states.dat | Bin 0 -> 208936 bytes src/main/resources/runtimeid_table.json | 16557 ------ 37 files changed, 25518 insertions(+), 40774 deletions(-) create mode 100644 src/main/java/cn/nukkit/network/protocol/CompletedUsingItemPacket.java delete mode 100644 src/main/java/cn/nukkit/network/protocol/ExplodePacket.java create mode 100644 src/main/java/cn/nukkit/utils/SerializedImage.java create mode 100644 src/main/java/cn/nukkit/utils/SkinAnimation.java create mode 100644 src/main/resources/runtime_block_states.dat delete mode 100644 src/main/resources/runtimeid_table.json diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index 8cabbbc673e..302dd822e3d 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -40,7 +40,6 @@ import cn.nukkit.inventory.transaction.data.UseItemOnEntityData; import cn.nukkit.item.*; import cn.nukkit.item.enchantment.Enchantment; -import cn.nukkit.item.food.Food; import cn.nukkit.lang.TextContainer; import cn.nukkit.lang.TranslationContainer; import cn.nukkit.level.*; @@ -2442,12 +2441,6 @@ public void onCompletion(Server server) { this.teleport(respawnPos, null); - RespawnPacket respawnPacket = new RespawnPacket(); - respawnPacket.x = (float) respawnPos.x; - respawnPacket.y = (float) respawnPos.y; - respawnPacket.z = (float) respawnPos.z; - this.dataPacket(respawnPacket); - this.setSprinting(false); this.setSneaking(false); @@ -3059,12 +3052,25 @@ public void onCompletion(Server server) { break packetswitch; } - if (item.onClickAir(this, directionVector) && this.isSurvival()) { - this.inventory.setItemInHand(item); - } + if (item.onClickAir(this, directionVector)) { + if (this.isSurvival()) { + this.inventory.setItemInHand(item); + } - this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, true); - this.startAction = this.server.getTick(); + if (this.startAction == -1) { + this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, true); + this.startAction = this.server.getTick(); + break packetswitch; + } + + // Used item + int ticksUsed = this.server.getTick() - this.startAction; + this.stopAction(); + CompletedUsingItemPacket completedUsingItem = new CompletedUsingItemPacket(); + completedUsingItem.itemId = item.getId(); + completedUsingItem.action = item.completeAction(this, ticksUsed); + this.dataPacket(completedUsingItem); + } break packetswitch; default: @@ -3204,37 +3210,6 @@ public void onCompletion(Server server) { if (potion != null) { potion.applyPotion(this); } - - } else if (itemInHand.getId() == Item.BUCKET && itemInHand.getDamage() == 1) { //milk - this.server.getPluginManager().callEvent(consumeEvent); - if (consumeEvent.isCancelled()) { - this.inventory.sendContents(this); - break; - } - - EntityEventPacket eventPacket = new EntityEventPacket(); - eventPacket.eid = this.getId(); - eventPacket.event = EntityEventPacket.USE_ITEM; - this.dataPacket(eventPacket); - Server.broadcastPacket(this.getViewers().values(), eventPacket); - - if (this.isSurvival()) { - itemInHand.count--; - this.inventory.setItemInHand(itemInHand); - this.inventory.addItem(new ItemBucket()); - } - - this.removeAllEffects(); - } else { - this.server.getPluginManager().callEvent(consumeEvent); - if (consumeEvent.isCancelled()) { - this.inventory.sendContents(this); - break; - } - - Food food = Food.getByRelative(itemInHand); - if (food != null && food.eatenBy(this)) --itemInHand.count; - this.inventory.setItemInHand(itemInHand); } return; default: @@ -3271,6 +3246,19 @@ public void onCompletion(Server server) { }); } break; + case ProtocolInfo.RESPAWN_PACKET: + if (this.isAlive()) { + break; + } + RespawnPacket respawnPacket = (RespawnPacket) packet; + if (respawnPacket.respawnState == RespawnPacket.STATE_CLIENT_READY_TO_SPAWN) { + RespawnPacket respawn1 = new RespawnPacket(); + respawn1.x = (float) this.getX(); + respawn1.y = (float) this.getY(); + respawn1.z = (float) this.getZ(); + respawn1.respawnState = RespawnPacket.STATE_READY_TO_SPAWN; + this.dataPacket(respawn1); + } default: break; } @@ -3856,6 +3844,7 @@ public void kill() { pk.x = (float) pos.x; pk.y = (float) pos.y; pk.z = (float) pos.z; + pk.respawnState = RespawnPacket.STATE_SEARCHING_FOR_SPAWN; //this is a dirty hack to prevent dying in a different level than the respawn point from breaking everything if (this.level != pos.level) { diff --git a/src/main/java/cn/nukkit/block/BlockLeaves2.java b/src/main/java/cn/nukkit/block/BlockLeaves2.java index 113f3773a6d..c6936242834 100644 --- a/src/main/java/cn/nukkit/block/BlockLeaves2.java +++ b/src/main/java/cn/nukkit/block/BlockLeaves2.java @@ -14,7 +14,7 @@ public BlockLeaves2() { } public BlockLeaves2(int meta) { - super(meta); + super(meta & 0x7); // Anything above this range is invalid } public String getName() { diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java b/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java index e1e338075e6..5e5e15ba611 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java @@ -12,7 +12,6 @@ import cn.nukkit.inventory.InventoryHolder; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; -import cn.nukkit.level.Sound; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; @@ -197,7 +196,6 @@ protected void checkFuel(Item fuel) { burnDuration = 0; if (this.getBlock().getId() == Item.FURNACE) { this.getLevel().setBlock(this, new BlockFurnaceBurning(this.getBlock().getDamage()), true); - this.getLevel().addSound(this, Sound.FURNACE_LIT); } if (burnTime > 0 && ev.isBurning()) { diff --git a/src/main/java/cn/nukkit/entity/EntityHuman.java b/src/main/java/cn/nukkit/entity/EntityHuman.java index 7129215b109..8937ce756cb 100644 --- a/src/main/java/cn/nukkit/entity/EntityHuman.java +++ b/src/main/java/cn/nukkit/entity/EntityHuman.java @@ -5,12 +5,16 @@ import cn.nukkit.entity.data.Skin; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.network.protocol.AddPlayerPacket; import cn.nukkit.network.protocol.RemoveEntityPacket; import cn.nukkit.network.protocol.SetEntityLinkPacket; +import cn.nukkit.utils.SerializedImage; +import cn.nukkit.utils.SkinAnimation; import cn.nukkit.utils.Utils; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.UUID; /** @@ -104,22 +108,66 @@ protected void initEntity() { newSkin.setSkinId(skinTag.getString("ModelId")); } if (skinTag.contains("Data")) { - newSkin.setSkinData(skinTag.getByteArray("Data")); + byte[] data = skinTag.getByteArray("Data"); + if (skinTag.contains("SkinImageWidth") && skinTag.contains("SkinImageHeight")) { + int width = skinTag.getInt("SkinImageWidth"); + int height = skinTag.getInt("SkinImageHeight"); + newSkin.setSkinData(new SerializedImage(width, height, data)); + } else { + newSkin.setSkinData(data); + } + } + if (skinTag.contains("CapeId")) { + newSkin.setCapeId(skinTag.getString("CapeId")); } if (skinTag.contains("CapeData")) { newSkin.setCapeData(skinTag.getByteArray("CapeData")); + byte[] data = skinTag.getByteArray("CapeData"); + if (skinTag.contains("CapeImageWidth") && skinTag.contains("CapeImageHeight")) { + int width = skinTag.getInt("CapeImageWidth"); + int height = skinTag.getInt("CapeImageHeight"); + newSkin.setSkinData(new SerializedImage(width, height, data)); + } else { + newSkin.setSkinData(data); + } } if (skinTag.contains("GeometryName")) { newSkin.setGeometryName(skinTag.getString("GeometryName")); } + if (skinTag.contains("SkinResourcePatch")) { + newSkin.setSkinResourcePatch(new String(skinTag.getByteArray("SkinResourcePatch"), StandardCharsets.UTF_8)); + } if (skinTag.contains("GeometryData")) { newSkin.setGeometryData(new String(skinTag.getByteArray("GeometryData"), StandardCharsets.UTF_8)); } + if (skinTag.contains("AnimationData")) { + newSkin.setAnimationData(new String(skinTag.getByteArray("Animation"), StandardCharsets.UTF_8)); + } + if (skinTag.contains("PremiumSkin")) { + newSkin.setPremium(skinTag.getBoolean("PremiumSkin")); + } + if (skinTag.contains("PersonaSkin")) { + newSkin.setPersona(skinTag.getBoolean("PersonaSkin")); + } + if (skinTag.contains("CapeOnClassicSkin")) { + newSkin.setCapeOnClassic(skinTag.getBoolean("CapeOnClassicSkin")); + } + if (skinTag.contains("AnimatedImageData")) { + ListTag list = skinTag.getList("AnimatedImageData", CompoundTag.class); + for (CompoundTag animationTag : list.getAll()) { + float frames = animationTag.getFloat("Frames"); + int type = animationTag.getInt("Type"); + byte[] image = animationTag.getByteArray("Image"); + int width = animationTag.getInt("ImageWidth"); + int height = animationTag.getInt("ImageHeight"); + skin.getAnimations().add(new SkinAnimation(new SerializedImage(width, height, image), type, frames)); + } + } this.setSkin(newSkin); } this.uuid = Utils.dataToUUID(String.valueOf(this.getId()).getBytes(StandardCharsets.UTF_8), this.getSkin() - .getSkinData(), this.getNameTag().getBytes(StandardCharsets.UTF_8)); + .getSkinData().data, this.getNameTag().getBytes(StandardCharsets.UTF_8)); } super.initEntity(); @@ -135,13 +183,35 @@ public void saveNBT() { super.saveNBT(); if (skin != null) { - this.namedTag.putCompound("Skin", new CompoundTag() - .putByteArray("Data", this.getSkin().getSkinData()) + CompoundTag skinTag = new CompoundTag() + .putByteArray("Data", this.getSkin().getSkinData().data) + .putInt("SkinImageWidth", this.getSkin().getSkinData().width) + .putInt("SkinImageHeight", this.getSkin().getSkinData().height) .putString("ModelId", this.getSkin().getSkinId()) - .putByteArray("CapeData", this.getSkin().getCapeData()) - .putString("GeometryName", this.getSkin().getGeometryName()) + .putString("CapeId", this.getSkin().getCapeId()) + .putByteArray("CapeData", this.getSkin().getCapeData().data) + .putInt("CapeImageWidth", this.getSkin().getCapeData().width) + .putInt("CapeImageHeight", this.getSkin().getCapeData().height) + .putByteArray("SkinResourcePatch", this.getSkin().getSkinResourcePatch().getBytes(StandardCharsets.UTF_8)) .putByteArray("GeometryData", this.getSkin().getGeometryData().getBytes(StandardCharsets.UTF_8)) - ); + .putByteArray("AnimationData", this.getSkin().getAnimationData().getBytes(StandardCharsets.UTF_8)) + .putBoolean("PremiumSkin", this.getSkin().isPremium()) + .putBoolean("PersonaSkin", this.getSkin().isPersona()) + .putBoolean("CapeOnClassicSkin", this.getSkin().isCapeOnClassic()); + List animations = this.getSkin().getAnimations(); + if (!animations.isEmpty()) { + ListTag animationsTag = new ListTag<>("AnimationImageData"); + for (SkinAnimation animation : animations) { + animationsTag.add(new CompoundTag() + .putFloat("Frames", animation.frames) + .putInt("Type", animation.type) + .putInt("ImageWidth", animation.image.width) + .putInt("ImageHeight", animation.image.height) + .putByteArray("Image", animation.image.data)); + } + skinTag.putList(animationsTag); + } + this.namedTag.putCompound("Skin", skinTag); } } diff --git a/src/main/java/cn/nukkit/entity/data/Skin.java b/src/main/java/cn/nukkit/entity/data/Skin.java index a2cd954c9c7..b90f9fbe4c8 100644 --- a/src/main/java/cn/nukkit/entity/data/Skin.java +++ b/src/main/java/cn/nukkit/entity/data/Skin.java @@ -1,18 +1,22 @@ package cn.nukkit.entity.data; import cn.nukkit.nbt.stream.FastByteArrayOutputStream; +import cn.nukkit.utils.SerializedImage; +import cn.nukkit.utils.SkinAnimation; import com.google.common.base.Preconditions; import lombok.ToString; import java.awt.*; import java.awt.image.BufferedImage; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; /** * author: MagicDroidX * Nukkit Project */ -@ToString(exclude = {"skinData", "capeData", "geometryData"}) +@ToString public class Skin { private static final int PIXEL_SIZE = 4; @@ -21,35 +25,32 @@ public class Skin { public static final int SKIN_128_64_SIZE = 128 * 64 * PIXEL_SIZE; public static final int SKIN_128_128_SIZE = 128 * 128 * PIXEL_SIZE; - public static final String GEOMETRY_CUSTOM = "geometry.humanoid.custom"; - public static final String GEOMETRY_CUSTOM_SLIM = "geometry.humanoid.customSlim"; - - private static final byte[] EMPTY = new byte[SINGLE_SKIN_SIZE]; - - private String skinId = "Steve"; - private byte[] skinData = null; - private byte[] capeData = null; - private String geometryName = GEOMETRY_CUSTOM; - private String geometryData = null; + public static final String GEOMETRY_CUSTOM = convertLegacyGeometryName("geometry.humanoid.custom"); + public static final String GEOMETRY_CUSTOM_SLIM = convertLegacyGeometryName("geometry.humanoid.customSlim"); + + private String skinId; + private String skinResourcePatch; + private SerializedImage skinData; + private final List animations = new ArrayList<>(); + private SerializedImage capeData; + private String geometryData; + private String animationData; + private boolean premium; + private boolean persona; + private boolean capeOnClassic; + private String capeId; public boolean isValid() { - if (skinData != null) { - return isValidSkin(skinData.length); - } - return false; + return true; //TODO: some sort of validation } - public byte[] getSkinData() { + public SerializedImage getSkinData() { if (skinData == null) { - return EMPTY; + return SerializedImage.EMPTY; } return skinData; } - public String getGeometryName() { - return geometryName; - } - public String getSkinId() { return skinId; } @@ -61,38 +62,76 @@ public void setSkinId(String skinId) { this.skinId = skinId; } + public void setSkinData(byte[] skinData) { + setSkinData(SerializedImage.fromLegacy(skinData)); + } + public void setSkinData(BufferedImage image) { setSkinData(parseBufferedImage(image)); } - public void setSkinData(byte[] skinData) { - if (skinData == null || !isValidSkin(skinData.length)) { - throw new IllegalArgumentException("Invalid skin"); - } + public void setSkinData(SerializedImage skinData) { + Objects.requireNonNull(skinData, "skinData"); this.skinData = skinData; } + public void setSkinResourcePatch(String skinResourcePatch) { + if (skinResourcePatch == null || skinResourcePatch.trim().isEmpty()) { + skinResourcePatch = GEOMETRY_CUSTOM; + return; + } + this.skinResourcePatch = skinResourcePatch; + } + public void setGeometryName(String geometryName) { if (geometryName == null || geometryName.trim().isEmpty()) { - geometryName = GEOMETRY_CUSTOM; + skinResourcePatch = GEOMETRY_CUSTOM; + return; } - this.geometryName = geometryName; + this.skinResourcePatch = "{\"geometry\" : {\"default\" : \"" + geometryName + "\"}}"; } - public byte[] getCapeData() { + public String getSkinResourcePatch() { + if (this.skinResourcePatch == null) { + return ""; + } + return skinResourcePatch; + } + + public SerializedImage getCapeData() { if (capeData == null) { - return EMPTY; + return SerializedImage.EMPTY; } return capeData; } + public String getCapeId() { + if (capeId == null) { + return ""; + } + return capeId; + } + + public void setCapeId(String capeId) { + if (capeId == null || capeId.trim().isEmpty()) { + capeId = null; + } + this.capeId = capeId; + } + + public void setCapeData(byte[] capeData) { + Objects.requireNonNull(capeData, "capeData"); + Preconditions.checkArgument(capeData.length == SINGLE_SKIN_SIZE, "Invalid legacy cape"); + setCapeData(new SerializedImage(64, 32, capeData)); + } + public void setCapeData(BufferedImage image) { setCapeData(parseBufferedImage(image)); } - public void setCapeData(byte[] capeData) { - Preconditions.checkNotNull(capeData, "capeData"); + public void setCapeData(SerializedImage capeData) { + Objects.requireNonNull(capeData, "capeData"); this.capeData = capeData; } @@ -110,17 +149,53 @@ public void setGeometryData(String geometryData) { } } - public Skin copy() { - Skin skin = new Skin(); - skin.skinId = this.skinId; - skin.skinData = Arrays.copyOf(this.skinData, this.skinData.length); - skin.capeData = Arrays.copyOf(this.capeData, this.capeData.length); - skin.geometryName = this.geometryName; - skin.geometryData = this.geometryData; - return skin; + public String getAnimationData() { + if (animationData == null) { + return ""; + } + return animationData; + } + + public void setAnimationData(String animationData) { + Preconditions.checkNotNull(animationData, "animationData"); + if (!animationData.equals(this.animationData)) { + this.animationData = animationData; + } + } + + public List getAnimations() { + return animations; + } + + public boolean isPremium() { + return premium; + } + + public void setPremium(boolean premium) { + this.premium = premium; + } + + public boolean isPersona() { + return persona; + } + + public void setPersona(boolean persona) { + this.persona = persona; + } + + public boolean isCapeOnClassic() { + return capeOnClassic; + } + + public void setCapeOnClassic(boolean capeOnClassic) { + this.capeOnClassic = capeOnClassic; + } + + public String getFullSkinId() { + return getSkinId() + '_' + getCapeId(); } - private static byte[] parseBufferedImage(BufferedImage image) { + private static SerializedImage parseBufferedImage(BufferedImage image) { FastByteArrayOutputStream outputStream = new FastByteArrayOutputStream(); for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { @@ -132,7 +207,7 @@ private static byte[] parseBufferedImage(BufferedImage image) { } } image.flush(); - return outputStream.toByteArray(); + return new SerializedImage(image.getWidth(), image.getHeight(), outputStream.toByteArray()); } private static boolean isValidSkin(int length) { @@ -142,7 +217,7 @@ private static boolean isValidSkin(int length) { length == SKIN_128_128_SIZE; } - public boolean isPremiumGeometry() { - return !geometryName.equalsIgnoreCase(GEOMETRY_CUSTOM) && !geometryName.equalsIgnoreCase(GEOMETRY_CUSTOM_SLIM); + private static String convertLegacyGeometryName(String geometryName) { + return "{\"geometry\" : {\"default\" : \"" + geometryName + "\"}}"; } } diff --git a/src/main/java/cn/nukkit/inventory/transaction/data/UseItemData.java b/src/main/java/cn/nukkit/inventory/transaction/data/UseItemData.java index 6355b055f0d..e57815bf48b 100644 --- a/src/main/java/cn/nukkit/inventory/transaction/data/UseItemData.java +++ b/src/main/java/cn/nukkit/inventory/transaction/data/UseItemData.java @@ -5,10 +5,12 @@ import cn.nukkit.math.BlockVector3; import cn.nukkit.math.Vector3; import cn.nukkit.math.Vector3f; +import lombok.ToString; /** * @author CreeperFace */ +@ToString public class UseItemData implements TransactionData { public int actionType; diff --git a/src/main/java/cn/nukkit/item/Item.java b/src/main/java/cn/nukkit/item/Item.java index a13d80f235f..318a188008e 100644 --- a/src/main/java/cn/nukkit/item/Item.java +++ b/src/main/java/cn/nukkit/item/Item.java @@ -16,6 +16,7 @@ import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.nbt.tag.StringTag; import cn.nukkit.nbt.tag.Tag; +import cn.nukkit.network.protocol.CompletedUsingItemPacket; import cn.nukkit.utils.Binary; import cn.nukkit.utils.Config; import cn.nukkit.utils.MainLogger; @@ -949,6 +950,10 @@ public boolean isUnbreakable() { return false; } + public int completeAction(Player player, int ticksUsed) { + return CompletedUsingItemPacket.ACTION_UNKNOWN; + } + @Override final public String toString() { return "Item " + this.name + " (" + this.id + ":" + (!this.hasMeta ? "?" : this.meta) + ")x" + this.count + (this.hasCompoundTag() ? " tags:0x" + Binary.bytesToHexString(this.getCompoundTag()) : ""); diff --git a/src/main/java/cn/nukkit/item/ItemAppleGoldEnchanted.java b/src/main/java/cn/nukkit/item/ItemAppleGoldEnchanted.java index e59685ac1c4..3196050edc1 100644 --- a/src/main/java/cn/nukkit/item/ItemAppleGoldEnchanted.java +++ b/src/main/java/cn/nukkit/item/ItemAppleGoldEnchanted.java @@ -1,5 +1,8 @@ package cn.nukkit.item; +import cn.nukkit.Player; +import cn.nukkit.math.Vector3; + /** * Created by Snake1999 on 2016/1/14. * Package cn.nukkit.item in project nukkit. @@ -16,4 +19,9 @@ public ItemAppleGoldEnchanted(Integer meta) { public ItemAppleGoldEnchanted(Integer meta, int count) { super(GOLDEN_APPLE_ENCHANTED, meta, count, "Enchanted Golden Apple"); } + + @Override + public boolean onClickAir(Player player, Vector3 directionVector) { + return true; + } } diff --git a/src/main/java/cn/nukkit/item/ItemBucket.java b/src/main/java/cn/nukkit/item/ItemBucket.java index 3462e8a1648..cc9c1ea1dab 100644 --- a/src/main/java/cn/nukkit/item/ItemBucket.java +++ b/src/main/java/cn/nukkit/item/ItemBucket.java @@ -4,9 +4,12 @@ import cn.nukkit.block.*; import cn.nukkit.event.player.PlayerBucketEmptyEvent; import cn.nukkit.event.player.PlayerBucketFillEvent; +import cn.nukkit.event.player.PlayerItemConsumeEvent; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; import cn.nukkit.math.BlockFace.Plane; +import cn.nukkit.math.Vector3; +import cn.nukkit.network.protocol.CompletedUsingItemPacket; import cn.nukkit.network.protocol.LevelSoundEventPacket; /** @@ -146,4 +149,29 @@ public boolean onActivate(Level level, Player player, Block block, Block target, return false; } + + @Override + public boolean onClickAir(Player player, Vector3 directionVector) { + return this.getDamage() == 1; // Milk + } + + @Override + public int completeAction(Player player, int ticksUsed) { + PlayerItemConsumeEvent consumeEvent = new PlayerItemConsumeEvent(player, this); + + player.getServer().getPluginManager().callEvent(consumeEvent); + if (consumeEvent.isCancelled()) { + player.getInventory().sendContents(player); + return CompletedUsingItemPacket.ACTION_UNKNOWN; + } + + if (player.isSurvival()) { + this.count--; + player.getInventory().setItemInHand(this); + player.getInventory().addItem(new ItemBucket()); + } + + player.removeAllEffects(); + return CompletedUsingItemPacket.ACTION_UNKNOWN; + } } diff --git a/src/main/java/cn/nukkit/item/ItemEdible.java b/src/main/java/cn/nukkit/item/ItemEdible.java index 6530db9d539..1cee5fcd61b 100644 --- a/src/main/java/cn/nukkit/item/ItemEdible.java +++ b/src/main/java/cn/nukkit/item/ItemEdible.java @@ -1,5 +1,11 @@ package cn.nukkit.item; +import cn.nukkit.Player; +import cn.nukkit.event.player.PlayerItemConsumeEvent; +import cn.nukkit.item.food.Food; +import cn.nukkit.math.Vector3; +import cn.nukkit.network.protocol.CompletedUsingItemPacket; + /** * author: MagicDroidX * Nukkit Project @@ -21,4 +27,37 @@ public ItemEdible(int id, Integer meta, int count) { super(id, meta, count); } + @Override + public boolean onClickAir(Player player, Vector3 directionVector) { + if (player.getFoodData().getLevel() < player.getFoodData().getMaxLevel() || player.isCreative()) { + return true; + } + player.getFoodData().sendFoodLevel(); + return false; + } + + @Override + public int completeAction(Player player, int ticksUsed) { + if (consume(player)) { + return CompletedUsingItemPacket.ACTION_EAT; + } + return CompletedUsingItemPacket.ACTION_UNKNOWN; + } + + protected boolean consume(Player player) { + PlayerItemConsumeEvent consumeEvent = new PlayerItemConsumeEvent(player, this); + + player.getServer().getPluginManager().callEvent(consumeEvent); + if (consumeEvent.isCancelled()) { + player.getInventory().sendContents(player); + return false; + } + + Food food = Food.getByRelative(this); + if (player.isSurvival() && food != null && food.eatenBy(player)) { + --this.count; + player.getInventory().setItemInHand(this); + } + return true; + } } diff --git a/src/main/java/cn/nukkit/level/Explosion.java b/src/main/java/cn/nukkit/level/Explosion.java index a4d12c3f04f..b5d1382a43a 100644 --- a/src/main/java/cn/nukkit/level/Explosion.java +++ b/src/main/java/cn/nukkit/level/Explosion.java @@ -15,7 +15,6 @@ import cn.nukkit.item.ItemBlock; import cn.nukkit.level.particle.HugeExplodeSeedParticle; import cn.nukkit.math.*; -import cn.nukkit.network.protocol.ExplodePacket; import cn.nukkit.network.protocol.LevelSoundEventPacket; import cn.nukkit.utils.Hash; import it.unimi.dsi.fastutil.longs.LongArraySet; @@ -196,14 +195,6 @@ public boolean explodeB() { send.add(new Vector3(block.x - source.x, block.y - source.y, block.z - source.z)); } - ExplodePacket pk = new ExplodePacket(); - pk.x = (float) this.source.x; - pk.y = (float) this.source.y; - pk.z = (float) this.source.z; - pk.radius = (float) this.size; - pk.records = send.toArray(new Vector3[0]); - - this.level.addChunkPacket((int) source.x >> 4, (int) source.z >> 4, pk); this.level.addParticle(new HugeExplodeSeedParticle(this.source)); this.level.addLevelSoundEvent(source, LevelSoundEventPacket.SOUND_EXPLODE); diff --git a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java index 4d9c28aa9ae..3a1844fdb9b 100644 --- a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java +++ b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java @@ -1,20 +1,17 @@ package cn.nukkit.level; import cn.nukkit.Server; -import cn.nukkit.utils.BinaryStream; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; +import cn.nukkit.nbt.NBTIO; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.ListTag; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.util.Collection; +import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; @@ -26,34 +23,37 @@ public class GlobalBlockPalette { private static final Int2ObjectMap legacyIdToString = new Int2ObjectOpenHashMap<>(); private static final Map stringToLegacyId = new HashMap<>(); private static final AtomicInteger runtimeIdAllocator = new AtomicInteger(0); - private static final byte[] compiledPalette; + public static final byte[] BLOCK_PALETTE; static { legacyToRuntimeId.defaultReturnValue(-1); runtimeIdToLegacy.defaultReturnValue(-1); - InputStream stream = Server.class.getClassLoader().getResourceAsStream("runtimeid_table.json"); + InputStream stream = Server.class.getClassLoader().getResourceAsStream("runtime_block_states.dat"); if (stream == null) { - throw new AssertionError("Unable to locate RuntimeID table"); + throw new AssertionError("Unable to locate block state nbt"); + } + CompoundTag tag; + try { + tag = NBTIO.read(stream); + } catch (IOException e) { + throw new AssertionError(e); } - Reader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); - - Gson gson = new Gson(); - Type collectionType = new TypeToken>() { - }.getType(); - Collection entries = gson.fromJson(reader, collectionType); - BinaryStream table = new BinaryStream(); - table.putUnsignedVarInt(entries.size()); + ListTag states = tag.getList("Palette", CompoundTag.class); + for (CompoundTag state : states.getAll()) { + int id = state.getShort("id"); + int meta = state.getShort("meta"); + String name = state.getCompound("block").getString("name"); - for (TableEntry entry : entries) { - registerMapping((entry.id << 4) | entry.data, entry.name); - table.putString(entry.name); - table.putLShort(entry.data); - table.putLShort(entry.id); + registerMapping(id << 4 | meta, name); + state.remove("meta"); // No point in sending this since the client doesn't use it. + } + try { + BLOCK_PALETTE = NBTIO.write(tag.getList("Palette"), ByteOrder.LITTLE_ENDIAN, true); + } catch (IOException e) { + throw new AssertionError(e); } - - compiledPalette = table.getBuffer(); } public static int getOrCreateRuntimeId(int id, int meta) { @@ -85,14 +85,4 @@ private static int registerMapping(int legacyId, String name) { legacyToRuntimeId.put(legacyId, runtimeId); return runtimeId; } - - public static byte[] getCompiledPalette() { - return compiledPalette; - } - - private static class TableEntry { - private int id; - private int data; - private String name; - } } diff --git a/src/main/java/cn/nukkit/level/Sound.java b/src/main/java/cn/nukkit/level/Sound.java index 86656957437..97318545f68 100644 --- a/src/main/java/cn/nukkit/level/Sound.java +++ b/src/main/java/cn/nukkit/level/Sound.java @@ -130,7 +130,7 @@ public enum Sound { BUBBLE_DOWNINSIDE("bubble.downinside"), MINECART_BASE("minecart.base"), MINECART_INSIDE("minecart.inside"), - FURNACE_LIT("furnace.lit"), + BLOCK_FURNACE_LIT("block.furnace.lit"), BLOCK_BLASTFURNACE_FIRE_CRACKLE("block.blastfurnace.fire_crackle"), BLOCK_SMOKER_SMOKE("block.smoker.smoke"), MOB_AGENT_SPAWN("mob.agent.spawn"), @@ -187,6 +187,16 @@ public enum Sound { MOB_ENDERDRAGON_HIT("mob.enderdragon.hit"), MOB_ENDERDRAGON_FLAP("mob.enderdragon.flap"), MOB_ENDERDRAGON_GROWL("mob.enderdragon.growl"), + MOB_FOX_AMBIENT("mob.fox.ambient"), + MOB_FOX_HURT("mob.fox.hurt"), + MOB_FOX_DEATH("mob.fox.death"), + MOB_FOX_AGGRO("mob.fox.aggro"), + MOB_FOX_SNIFF("mob.fox.sniff"), + MOB_FOX_BITE("mob.fox.bite"), + MOB_FOX_EAT("mob.fox.eat"), + MOB_FOX_SCREECH("mob.fox.screech"), + MOB_FOX_SLEEP("mob.fox.sleep"), + MOB_FOX_SPIT("mob.fox.spit"), MOB_GHAST_AFFECTIONATE_SCREAM("mob.ghast.affectionate_scream"), MOB_GHAST_CHARGE("mob.ghast.charge"), MOB_GHAST_DEATH("mob.ghast.death"), @@ -245,6 +255,7 @@ public enum Sound { MOB_HUSK_STEP("mob.husk.step"), MOB_RAVAGER_AMBIENT("mob.ravager.ambient"), MOB_RAVAGER_BITE("mob.ravager.bite"), + MOB_RAVAGER_CELEBRATE("mob.ravager.celebrate"), MOB_RAVAGER_DEATH("mob.ravager.death"), MOB_RAVAGER_HURT("mob.ravager.hurt"), MOB_RAVAGER_ROAR("mob.ravager.roar"), @@ -266,6 +277,9 @@ public enum Sound { MOB_MAGMACUBE_BIG("mob.magmacube.big"), MOB_MAGMACUBE_JUMP("mob.magmacube.jump"), MOB_MAGMACUBE_SMALL("mob.magmacube.small"), + MOB_MOOSHROOM_CONVERT("mob.mooshroom.convert"), + MOB_MOOSHROOM_EAT("mob.mooshroom.eat"), + MOB_MOOSHROOM_SUSPICIOUS_MILK("mob.mooshroom.suspicious_milk"), MOB_PARROT_IDLE("mob.parrot.idle"), MOB_PARROT_HURT("mob.parrot.hurt"), MOB_PARROT_DEATH("mob.parrot.death"), @@ -281,6 +295,7 @@ public enum Sound { MOB_PIG_BOOST("mob.pig.boost"), MOB_PIG_SAY("mob.pig.say"), MOB_PIG_STEP("mob.pig.step"), + MOB_PILLAGER_CELEBRATE("mob.pillager.celebrate"), MOB_PILLAGER_DEATH("mob.pillager.death"), MOB_PILLAGER_HURT("mob.pillager.hurt"), MOB_PILLAGER_IDLE("mob.pillager.idle"), @@ -338,12 +353,14 @@ public enum Sound { MOB_VILLAGER_IDLE("mob.villager.idle"), MOB_VILLAGER_NO("mob.villager.no"), MOB_VILLAGER_YES("mob.villager.yes"), + MOB_VINDICATOR_CELEBRATE("mob.vindicator.celebrate"), MOB_VINDICATOR_DEATH("mob.vindicator.death"), MOB_VINDICATOR_HURT("mob.vindicator.hurt"), MOB_VINDICATOR_IDLE("mob.vindicator.idle"), MOB_EVOCATION_FANGS_ATTACK("mob.evocation_fangs.attack"), MOB_EVOCATION_ILLAGER_AMBIENT("mob.evocation_illager.ambient"), MOB_EVOCATION_ILLAGER_CAST_SPELL("mob.evocation_illager.cast_spell"), + MOB_EVOCATION_ILLAGER_CELEBRATE("mob.evocation_illager.celebrate"), MOB_EVOCATION_ILLAGER_DEATH("mob.evocation_illager.death"), MOB_EVOCATION_ILLAGER_HURT("mob.evocation_illager.hurt"), MOB_EVOCATION_ILLAGER_PREPARE_ATTACK("mob.evocation_illager.prepare_attack"), @@ -376,6 +393,7 @@ public enum Sound { MOB_WANDERINGTRADER_HURT("mob.wanderingtrader.hurt"), MOB_WANDERINGTRADER_REAPPEARED("mob.wanderingtrader.reappeared"), MOB_WITCH_AMBIENT("mob.witch.ambient"), + MOB_WITCH_CELEBRATE("mob.witch.celebrate"), MOB_WITCH_DEATH("mob.witch.death"), MOB_WITCH_HURT("mob.witch.hurt"), MOB_WITCH_DRINK("mob.witch.drink"), @@ -437,13 +455,23 @@ public enum Sound { MOB_ZOMBIE_VILLAGER_SAY("mob.zombie_villager.say"), MOB_ZOMBIE_VILLAGER_DEATH("mob.zombie_villager.death"), MOB_ZOMBIE_VILLAGER_HURT("mob.zombie_villager.hurt"), + NOTE_BANJO("note.banjo"), NOTE_BASS("note.bass"), NOTE_BASSATTACK("note.bassattack"), NOTE_BD("note.bd"), + NOTE_BELL("note.bell"), + NOTE_BIT("note.bit"), + NOTE_COW_BELL("note.cow_bell"), + NOTE_DIDGERIDOO("note.didgeridoo"), + NOTE_FLUTE("note.flute"), + NOTE_GUITAR("note.guitar"), NOTE_HARP("note.harp"), NOTE_HAT("note.hat"), + NOTE_CHIME("note.chime"), + NOTE_IRON_XYLOPHONE("note.iron_xylophone"), NOTE_PLING("note.pling"), NOTE_SNARE("note.snare"), + NOTE_XYLOPHONE("note.xylophone"), PORTAL_PORTAL("portal.portal"), PORTAL_TRAVEL("portal.travel"), PORTAL_TRIGGER("portal.trigger"), diff --git a/src/main/java/cn/nukkit/nbt/NBTIO.java b/src/main/java/cn/nukkit/nbt/NBTIO.java index 614eae3130f..25c9cb678fb 100644 --- a/src/main/java/cn/nukkit/nbt/NBTIO.java +++ b/src/main/java/cn/nukkit/nbt/NBTIO.java @@ -92,6 +92,12 @@ public static CompoundTag read(InputStream inputStream, ByteOrder endianness, bo } } + public static Tag readNetwork(InputStream inputStream) throws IOException { + try (NBTInputStream stream = new NBTInputStream(inputStream, ByteOrder.LITTLE_ENDIAN, true)) { + return Tag.readNamedTag(stream); + } + } + public static CompoundTag read(byte[] data) throws IOException { return read(data, ByteOrder.BIG_ENDIAN); } @@ -144,7 +150,7 @@ public static byte[] write(CompoundTag tag, ByteOrder endianness) throws IOExcep return write(tag, endianness, false); } - public static byte[] write(CompoundTag tag, ByteOrder endianness, boolean network) throws IOException { + public static byte[] write(Tag tag, ByteOrder endianness, boolean network) throws IOException { FastByteArrayOutputStream baos = ThreadCache.fbaos.get().reset(); try (NBTOutputStream stream = new NBTOutputStream(baos, endianness, network)) { Tag.writeNamedTag(tag, stream); @@ -192,6 +198,14 @@ public static void write(CompoundTag tag, OutputStream outputStream, ByteOrder e } } + public static byte[] writeNetwork(Tag tag) throws IOException { + FastByteArrayOutputStream baos = ThreadCache.fbaos.get().reset(); + try (NBTOutputStream stream = new NBTOutputStream(baos, ByteOrder.LITTLE_ENDIAN, true)) { + Tag.writeNamedTag(tag, stream); + } + return baos.toByteArray(); + } + public static byte[] writeGZIPCompressed(CompoundTag tag) throws IOException { return writeGZIPCompressed(tag, ByteOrder.BIG_ENDIAN); } diff --git a/src/main/java/cn/nukkit/nbt/tag/ListTag.java b/src/main/java/cn/nukkit/nbt/tag/ListTag.java index 3ebc00e3071..30565b335da 100644 --- a/src/main/java/cn/nukkit/nbt/tag/ListTag.java +++ b/src/main/java/cn/nukkit/nbt/tag/ListTag.java @@ -40,7 +40,7 @@ void load(NBTInputStream dis) throws IOException { type = dis.readByte(); int size = dis.readInt(); - list = new ArrayList<>(); + list = new ArrayList<>(size); for (int i = 0; i < size; i++) { Tag tag = Tag.newTag(type, null); tag.load(dis); diff --git a/src/main/java/cn/nukkit/network/Network.java b/src/main/java/cn/nukkit/network/Network.java index 0c5d13c2377..7ea61fc318a 100644 --- a/src/main/java/cn/nukkit/network/Network.java +++ b/src/main/java/cn/nukkit/network/Network.java @@ -167,6 +167,7 @@ public void processBatch(BatchPacket packet, Player player) { pk.decode(); } catch (Exception e) { log.warn("Unable to decode {} from {}", pk.getClass().getSimpleName(), player.getName()); + log.throwing(e); if (log.isTraceEnabled()) { log.trace("Dumping Packet\n{}", ByteBufUtil.prettyHexDump(Unpooled.wrappedBuffer(packet.payload))); } @@ -257,7 +258,6 @@ private void registerPackets() { this.registerPacket(ProtocolInfo.DISCONNECT_PACKET, DisconnectPacket.class); this.registerPacket(ProtocolInfo.ENTITY_EVENT_PACKET, EntityEventPacket.class); this.registerPacket(ProtocolInfo.ENTITY_FALL_PACKET, EntityFallPacket.class); - this.registerPacket(ProtocolInfo.EXPLODE_PACKET, ExplodePacket.class); this.registerPacket(ProtocolInfo.FULL_CHUNK_DATA_PACKET, LevelChunkPacket.class); this.registerPacket(ProtocolInfo.GAME_RULES_CHANGED_PACKET, GameRulesChangedPacket.class); this.registerPacket(ProtocolInfo.HURT_ARMOR_PACKET, HurtArmorPacket.class); diff --git a/src/main/java/cn/nukkit/network/protocol/CompletedUsingItemPacket.java b/src/main/java/cn/nukkit/network/protocol/CompletedUsingItemPacket.java new file mode 100644 index 00000000000..8f34df797d2 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/CompletedUsingItemPacket.java @@ -0,0 +1,46 @@ +package cn.nukkit.network.protocol; + +import lombok.ToString; + +@ToString +public class CompletedUsingItemPacket extends DataPacket { + + public static final byte NETWORK_ID = ProtocolInfo.COMPLETED_USING_ITEM_PACKET; + + public static final int ACTION_UNKNOWN = -1; + public static final int ACTION_EQUIP_ARMOR = 0; + public static final int ACTION_EAT = 1; + public static final int ACTION_ATTACK = 2; + public static final int ACTION_CONSUME = 3; + public static final int ACTION_THROW = 4; + public static final int ACTION_SHOOT = 5; + public static final int ACTION_PLACE = 6; + public static final int ACTION_FILL_BOTTLE = 7; + public static final int ACTION_FILL_BUCKET = 8; + public static final int ACTION_POUR_BUCKET = 9; + public static final int ACTION_USE_TOOL = 10; + public static final int ACTION_INTERACT = 11; + public static final int ACTION_RETRIEVE = 12; + public static final int ACTION_DYED = 13; + public static final int ACTION_TRADED = 14; + + public int itemId; + public int action; + + + @Override + public byte pid() { + return NETWORK_ID; + } + + @Override + public void decode() { + } + + @Override + public void encode() { + this.reset(); + this.putLShort(itemId); + this.putLInt(action); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java b/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java index 8e38137e2cb..b57cb21530b 100644 --- a/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java @@ -59,6 +59,6 @@ public void encode() { this.reset(); this.putEntityRuntimeId(this.eid); this.putByte((byte) this.event); - this.putVarInt((byte) this.data); + this.putVarInt(this.data); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ExplodePacket.java b/src/main/java/cn/nukkit/network/protocol/ExplodePacket.java deleted file mode 100644 index 954a41b020c..00000000000 --- a/src/main/java/cn/nukkit/network/protocol/ExplodePacket.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.math.Vector3; -import lombok.ToString; - -/** - * author: MagicDroidX - * Nukkit Project - */ -@ToString -public class ExplodePacket extends DataPacket { - - public static final byte NETWORK_ID = ProtocolInfo.EXPLODE_PACKET; - - public float x; - public float y; - public float z; - public float radius; - public Vector3[] records = new Vector3[0]; - - @Override - public byte pid() { - return NETWORK_ID; - } - - @Override - public DataPacket clean() { - this.records = new Vector3[0]; - return super.clean(); - } - - @Override - public void decode() { - - } - - @Override - public void encode() { - this.reset(); - this.putVector3f(this.x, this.y, this.z); - this.putVarInt((int) (this.radius * 32)); - this.putUnsignedVarInt(this.records.length); - if (this.records.length > 0) { - for (Vector3 record : records) { - this.putSignedBlockPosition(record.asBlockVector3()); - } - } - } - -} diff --git a/src/main/java/cn/nukkit/network/protocol/LoginPacket.java b/src/main/java/cn/nukkit/network/protocol/LoginPacket.java index 9f2c1df594f..103e84597e0 100644 --- a/src/main/java/cn/nukkit/network/protocol/LoginPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/LoginPacket.java @@ -1,7 +1,11 @@ package cn.nukkit.network.protocol; import cn.nukkit.entity.data.Skin; +import cn.nukkit.utils.SerializedImage; +import cn.nukkit.utils.SkinAnimation; import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import lombok.ToString; @@ -39,6 +43,11 @@ public void decode() { setOffset(getOffset() + 2); this.protocol = getInt(); } + if (protocol != ProtocolInfo.CURRENT_PROTOCOL) { + // decoding the chain could cause issues on newer or older versions. + return; + } + this.setBuffer(this.getByteArray(), 0); decodeChainData(); decodeSkinData(); @@ -77,20 +86,39 @@ private void decodeSkinData() { if (skinToken.has("SkinId")) { skin.setSkinId(skinToken.get("SkinId").getAsString()); } - if (skinToken.has("SkinData")) { - skin.setSkinData(Base64.getDecoder().decode(skinToken.get("SkinData").getAsString())); + if (skinToken.has("CapeId")) { + skin.setSkinId(skinToken.get("CapeId").getAsString()); + } + + skin.setSkinData(getImage(skinToken, "Skin")); + skin.setCapeData(getImage(skinToken, "Cape")); + if (skinToken.has("PremiumSkin")) { + skin.setPremium(skinToken.get("PremiumSkin").getAsBoolean()); + } + if (skinToken.has("PersonaSkin")) { + skin.setPersona(skinToken.get("PersonaSkin").getAsBoolean()); + } + if (skinToken.has("CapeOnClassicSkin")) { + skin.setCapeOnClassic(skinToken.get("CapeOnClassicSkin").getAsBoolean()); } - if (skinToken.has("CapeData")) { - this.skin.setCapeData(Base64.getDecoder().decode(skinToken.get("CapeData").getAsString())); + if (skinToken.has("SkinResourcePatch")) { + skin.setSkinResourcePatch(new String(Base64.getDecoder().decode(skinToken.get("SkinResourcePatch").getAsString()), StandardCharsets.UTF_8)); } - if (skinToken.has("SkinGeometryName")) { - skin.setGeometryName(skinToken.get("SkinGeometryName").getAsString()); + if (skinToken.has("SkinGeometryData")) { + skin.setGeometryData(new String(Base64.getDecoder().decode(skinToken.get("SkinGeometryData").getAsString()), StandardCharsets.UTF_8)); } - if (skinToken.has("SkinGeometry")) { - skin.setGeometryData(new String(Base64.getDecoder().decode(skinToken.get("SkinGeometry").getAsString()), StandardCharsets.UTF_8)); + if (skinToken.has("AnimationData")) { + skin.setGeometryData(new String(Base64.getDecoder().decode(skinToken.get("AnimationData").getAsString()), StandardCharsets.UTF_8)); + } + + if (skinToken.has("AnimatedImageData")) { + JsonArray array = skinToken.get("AnimatedImageData").getAsJsonArray(); + for (JsonElement element : array) { + skin.getAnimations().add(getAnimation(element.getAsJsonObject())); + } } } @@ -99,4 +127,27 @@ private JsonObject decodeToken(String token) { if (base.length < 2) return null; return new Gson().fromJson(new String(Base64.getDecoder().decode(base[1]), StandardCharsets.UTF_8), JsonObject.class); } + + private static SkinAnimation getAnimation(JsonObject element) { + float frames = element.get("Frames").getAsFloat(); + int type = element.get("Type").getAsInt(); + byte[] data = Base64.getDecoder().decode(element.get("Image").getAsString()); + int width = element.get("ImageWidth").getAsInt(); + int height = element.get("ImageHeight").getAsInt(); + return new SkinAnimation(new SerializedImage(width, height, data), type, frames); + } + + private static SerializedImage getImage(JsonObject token, String name) { + if (token.has(name + "Data")) { + byte[] skinImage = Base64.getDecoder().decode(token.get(name + "Data").getAsString()); + if (token.has(name + "ImageHeight") && token.has(name + "ImageWidth")) { + int width = token.get(name + "ImageWidth").getAsInt(); + int height = token.get(name + "ImageHeight").getAsInt(); + return new SerializedImage(width, height, skinImage); + } else { + return SerializedImage.fromLegacy(skinImage); + } + } + return SerializedImage.EMPTY; + } } diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java index 15fa7b3bca8..432b5b0f165 100644 --- a/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java @@ -34,9 +34,12 @@ public void encode() { if (type == TYPE_ADD) { this.putVarLong(entry.entityId); this.putString(entry.name); - this.putSkin(entry.skin); this.putString(entry.xboxUserId); this.putString(entry.platformChatId); + this.putLInt(entry.buildPlatform); + this.putSkin(entry.skin); + this.putBoolean(entry.isTeacher); + this.putBoolean(entry.isHost); } } @@ -53,9 +56,12 @@ public static class Entry { public final UUID uuid; public long entityId = 0; public String name = ""; - public Skin skin; public String xboxUserId = ""; //TODO public String platformChatId = ""; //TODO + public int buildPlatform = -1; + public Skin skin; + public boolean isTeacher; + public boolean isHost; public Entry(UUID uuid) { this.uuid = uuid; diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerSkinPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerSkinPacket.java index 19b54250450..91cf887e041 100644 --- a/src/main/java/cn/nukkit/network/protocol/PlayerSkinPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/PlayerSkinPacket.java @@ -22,28 +22,13 @@ public byte pid() { @Override public void decode() { uuid = getUUID(); - skin = new Skin(); - skin.setSkinId(getString()); - newSkinName = getString(); - oldSkinName = getString(); - skin.setSkinData(getByteArray()); - skin.setCapeData(getByteArray()); - skin.setGeometryName(getString()); - skin.setGeometryData(getString()); - premium = getBoolean(); + skin = getSkin(); } @Override public void encode() { reset(); putUUID(uuid); - putString(skin.getGeometryName()); - putString(newSkinName); - putString(oldSkinName); - putByteArray(skin.getSkinData()); - putByteArray(skin.getCapeData()); - putString(skin.getGeometryName()); - putString(skin.getGeometryData()); - putBoolean(premium); + putSkin(skin); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java index dfd029cd2eb..af9e121aca3 100644 --- a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java +++ b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java @@ -13,12 +13,13 @@ public interface ProtocolInfo { /** * Actual Minecraft: PE protocol version */ - int CURRENT_PROTOCOL = Integer.valueOf("361"); //plugins can change it + @SuppressWarnings("UnnecessaryBoxing") + int CURRENT_PROTOCOL = Integer.valueOf("388"); // DO NOT REMOVE BOXING List SUPPORTED_PROTOCOLS = Ints.asList(CURRENT_PROTOCOL); - String MINECRAFT_VERSION = "v1.12.0"; - String MINECRAFT_VERSION_NETWORK = "1.12.0"; + String MINECRAFT_VERSION = "v1.13.0"; + String MINECRAFT_VERSION_NETWORK = "1.13.0"; byte LOGIN_PACKET = 0x01; byte PLAY_STATUS_PACKET = 0x02; @@ -41,7 +42,7 @@ public interface ProtocolInfo { byte RIDER_JUMP_PACKET = 0x14; byte UPDATE_BLOCK_PACKET = 0x15; byte ADD_PAINTING_PACKET = 0x16; - byte EXPLODE_PACKET = 0x17; + byte TICK_SYNC_PACKET = 0x17; byte LEVEL_SOUND_EVENT_PACKET_V1 = 0x18; byte LEVEL_EVENT_PACKET = 0x19; byte BLOCK_EVENT_PACKET = 0x1a; @@ -150,6 +151,14 @@ public interface ProtocolInfo { byte UPDATE_BLOCK_PROPERTIES = (byte) 0x86; byte CLIENT_CACHE_BLOB_STATUS_PACKET = (byte) 0x87; byte CLIENT_CACHE_MISS_RESPONSE_PACKET = (byte) 0x88; + byte EDUCATION_SETTINGS_PACKET = (byte) 0x89; + byte EMOTE_PACKET = (byte) 0x8a; + byte MULTIPLAYER_SETTINGS_PACKET = (byte) 0x8b; + byte SETTINGS_COMMAND_PACKET = (byte) 0x8c; + byte ANVIL_DAMAGE_PACKET = (byte) 0x8d; + byte COMPLETED_USING_ITEM_PACKET = (byte) 0x8e; + byte NETWORK_SETTINGS_PACKET = (byte) 0x8f; + byte PLAYER_AUTH_INPUT_PACKET = (byte) 0x90; byte BATCH_PACKET = (byte) 0xff; } diff --git a/src/main/java/cn/nukkit/network/protocol/ResourcePackChunkDataPacket.java b/src/main/java/cn/nukkit/network/protocol/ResourcePackChunkDataPacket.java index 27071ed026a..72232810409 100644 --- a/src/main/java/cn/nukkit/network/protocol/ResourcePackChunkDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ResourcePackChunkDataPacket.java @@ -19,7 +19,7 @@ public void decode() { this.packId = UUID.fromString(this.getString()); this.chunkIndex = this.getLInt(); this.progress = this.getLLong(); - this.data = this.get(this.getLInt()); + this.data = this.getByteArray(); } @Override @@ -28,8 +28,7 @@ public void encode() { this.putString(this.packId.toString()); this.putLInt(this.chunkIndex); this.putLLong(this.progress); - this.putLInt(this.data.length); - this.put(this.data); + this.putByteArray(this.data); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/ResourcePackDataInfoPacket.java b/src/main/java/cn/nukkit/network/protocol/ResourcePackDataInfoPacket.java index 7fd64ce92f3..d1960c9ec2c 100644 --- a/src/main/java/cn/nukkit/network/protocol/ResourcePackDataInfoPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ResourcePackDataInfoPacket.java @@ -10,13 +10,15 @@ public class ResourcePackDataInfoPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.RESOURCE_PACK_DATA_INFO_PACKET; public static final int TYPE_INVALID = 0; - public static final int TYPE_RESOURCE = 1; - public static final int TYPE_BEHAVIOR = 2; - public static final int TYPE_WORLD_TEMPLATE = 3; - public static final int TYPE_ADDON = 4; - public static final int TYPE_SKINS = 5; - public static final int TYPE_CACHED = 6; - public static final int TYPE_COPY_PROTECTED = 7; + public static final int TYPE_ADDON = 1; + public static final int TYPE_CACHED = 2; + public static final int TYPE_COPY_PROTECTED = 3; + public static final int TYPE_BEHAVIOR = 4; + public static final int TYPE_PERSONA_PIECE = 5; + public static final int TYPE_RESOURCE = 6; + public static final int TYPE_SKINS = 7; + public static final int TYPE_WORLD_TEMPLATE = 8; + public static final int TYPE_COUNT = 9; public UUID packId; public int maxChunkSize; diff --git a/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java b/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java index 049a847c0a0..c9b04913ef1 100644 --- a/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java @@ -11,7 +11,7 @@ public class ResourcePackStackPacket extends DataPacket { public boolean mustAccept = false; public ResourcePack[] behaviourPackStack = new ResourcePack[0]; public ResourcePack[] resourcePackStack = new ResourcePack[0]; - public boolean isExperimental = false; + public String gameVersion = ProtocolInfo.MINECRAFT_VERSION_NETWORK; @Override public void decode() { @@ -37,7 +37,7 @@ public void encode() { this.putString(""); //TODO: subpack name } - this.putBoolean(isExperimental); + this.putString(this.gameVersion); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/RespawnPacket.java b/src/main/java/cn/nukkit/network/protocol/RespawnPacket.java index 22f4cc73e07..9cdc223ba94 100644 --- a/src/main/java/cn/nukkit/network/protocol/RespawnPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/RespawnPacket.java @@ -11,9 +11,15 @@ public class RespawnPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.RESPAWN_PACKET; + public static final int STATE_SEARCHING_FOR_SPAWN = 0; + public static final int STATE_READY_TO_SPAWN = 1; + public static final int STATE_CLIENT_READY_TO_SPAWN = 2; + public float x; public float y; public float z; + public int respawnState = STATE_SEARCHING_FOR_SPAWN; + public long runtimeEntityId; @Override public void decode() { @@ -21,12 +27,16 @@ public void decode() { this.x = v.x; this.y = v.y; this.z = v.z; + this.respawnState = this.getByte(); + this.runtimeEntityId = this.getEntityRuntimeId(); } @Override public void encode() { this.reset(); this.putVector3f(this.x, this.y, this.z); + this.putByte((byte) respawnState); + this.putEntityRuntimeId(runtimeEntityId); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java b/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java index 5d059375afa..064afa31c22 100644 --- a/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java @@ -100,14 +100,18 @@ public byte pid() { public boolean isFromWorldTemplate = false; public boolean isWorldTemplateOptionLocked = false; public boolean isOnlySpawningV1Villagers = false; + public String vanillaVersion = ProtocolInfo.MINECRAFT_VERSION_NETWORK; public String levelId = ""; //base64 string, usually the same as world folder name in vanilla public String worldName; public String premiumWorldTemplateId = ""; public boolean isTrial = false; + public boolean isMovementServerAuthoritative; public long currentTick; public int enchantmentSeed; + public byte[] blockPalette = GlobalBlockPalette.BLOCK_PALETTE; + public String multiplayerCorrelationId = ""; @Override @@ -156,14 +160,16 @@ public void encode() { this.putBoolean(this.isFromWorldTemplate); this.putBoolean(this.isWorldTemplateOptionLocked); this.putBoolean(this.isOnlySpawningV1Villagers); + this.putString(this.vanillaVersion); this.putString(this.levelId); this.putString(this.worldName); this.putString(this.premiumWorldTemplateId); this.putBoolean(this.isTrial); + this.putBoolean(this.isMovementServerAuthoritative); this.putLLong(this.currentTick); this.putVarInt(this.enchantmentSeed); - this.put(GlobalBlockPalette.getCompiledPalette()); + this.put(this.blockPalette); this.put(ITEM_DATA_PALETTE); this.putString(this.multiplayerCorrelationId); } diff --git a/src/main/java/cn/nukkit/utils/BinaryStream.java b/src/main/java/cn/nukkit/utils/BinaryStream.java index ba617f9cb56..8ae4eee6d35 100644 --- a/src/main/java/cn/nukkit/utils/BinaryStream.java +++ b/src/main/java/cn/nukkit/utils/BinaryStream.java @@ -258,22 +258,65 @@ public UUID getUUID() { public void putSkin(Skin skin) { this.putString(skin.getSkinId()); - this.putByteArray(skin.getSkinData()); - this.putByteArray(skin.getCapeData()); - this.putString(skin.getGeometryName()); + this.putString(skin.getSkinResourcePatch()); + this.putImage(skin.getSkinData()); + + List animations = skin.getAnimations(); + this.putLInt(animations.size()); + for (SkinAnimation animation : animations) { + this.putImage(animation.image); + this.putLInt(animation.type); + this.putLFloat(animation.frames); + } + + this.putImage(skin.getCapeData()); this.putString(skin.getGeometryData()); + this.putString(skin.getAnimationData()); + this.putBoolean(skin.isPremium()); + this.putBoolean(skin.isPersona()); + this.putBoolean(skin.isCapeOnClassic()); + this.putString(skin.getCapeId()); + this.putString(skin.getFullSkinId()); } public Skin getSkin() { Skin skin = new Skin(); skin.setSkinId(this.getString()); - skin.setSkinData(this.getByteArray()); - skin.setCapeData(this.getByteArray()); - skin.setGeometryName(this.getString()); + skin.setSkinResourcePatch(this.getString()); + skin.setSkinData(this.getImage()); + + int animationCount = this.getLInt(); + for (int i = 0; i < animationCount; i++) { + SerializedImage image = this.getImage(); + int type = this.getLInt(); + float frames = this.getLFloat(); + skin.getAnimations().add(new SkinAnimation(image, type, frames)); + } + + skin.setCapeData(this.getImage()); skin.setGeometryData(this.getString()); + skin.setAnimationData(this.getString()); + skin.setPremium(this.getBoolean()); + skin.setPersona(this.getBoolean()); + skin.setCapeOnClassic(this.getBoolean()); + skin.setCapeId(this.getString()); + this.getString(); // TODO: Full skin id return skin; } + public void putImage(SerializedImage image) { + this.putLInt(image.width); + this.putLInt(image.height); + this.putByteArray(image.data); + } + + public SerializedImage getImage() { + int width = this.getLInt(); + int height = this.getLInt(); + byte[] data = this.getByteArray(); + return new SerializedImage(width, height, data); + } + public Item getSlot() { int id = this.getVarInt(); diff --git a/src/main/java/cn/nukkit/utils/SerializedImage.java b/src/main/java/cn/nukkit/utils/SerializedImage.java new file mode 100644 index 00000000000..2fddf118d96 --- /dev/null +++ b/src/main/java/cn/nukkit/utils/SerializedImage.java @@ -0,0 +1,37 @@ +package cn.nukkit.utils; + +import lombok.ToString; + +import java.util.Objects; + +import static cn.nukkit.entity.data.Skin.*; + +@ToString(exclude = {"data"}) +public class SerializedImage { + public static final SerializedImage EMPTY = new SerializedImage(0, 0, new byte[0]); + + public final int width; + public final int height; + public final byte[] data; + + public SerializedImage(int width, int height, byte[] data) { + this.width = width; + this.height = height; + this.data = data; + } + + public static SerializedImage fromLegacy(byte[] skinData) { + Objects.requireNonNull(skinData, "skinData"); + switch (skinData.length) { + case SINGLE_SKIN_SIZE: + return new SerializedImage(64, 32, skinData); + case DOUBLE_SKIN_SIZE: + return new SerializedImage(64, 64, skinData); + case SKIN_128_64_SIZE: + return new SerializedImage(128, 64, skinData); + case SKIN_128_128_SIZE: + return new SerializedImage(128, 128, skinData); + } + throw new IllegalArgumentException("Unknown legacy skin size"); + } +} diff --git a/src/main/java/cn/nukkit/utils/SkinAnimation.java b/src/main/java/cn/nukkit/utils/SkinAnimation.java new file mode 100644 index 00000000000..99db3288b38 --- /dev/null +++ b/src/main/java/cn/nukkit/utils/SkinAnimation.java @@ -0,0 +1,16 @@ +package cn.nukkit.utils; + +import lombok.ToString; + +@ToString +public class SkinAnimation { + public final SerializedImage image; + public final int type; + public final float frames; + + public SkinAnimation(SerializedImage image, int type, float frames) { + this.image = image; + this.type = type; + this.frames = frames; + } +} diff --git a/src/main/resources/biome_definitions.dat b/src/main/resources/biome_definitions.dat index f45b76b3eb201a293a66c02a791bce1b28b4cb33..b8c6df4a686d41f62381547c6439d4e53552e616 100644 GIT binary patch literal 27359 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 pOWH34&3Q zPM(NCzb85A#hW2Jc`;%B0h(~&j3+O~i)TZ8TRA?-`+UBi@ADoX^&MT>oqF?0$M3#L zo+CIeC$PtB==AF_Bh~;%#I^cXhc;35$NM=#aNuSLN6xk%(+b_J;p@PvsE|f9k)=22x>sp?Pu4xiLs|geW+=;*u$_|eH)TT`oxsbnJ8UtE zxtTPyjDsEB2xrevqh}bnVmSgw23j4j@wD!-63M)VcX=JD>p8r*;o!rxiF-3G6j$*2w!@U;HQJ)hHs*^%K?!^>jN{wgELLX?bnjn9p_s?dVh*oLA)vP~9JXgq z&v7L3U%opX%I{VZA$))Ae2{hcK!NWj;Vcu+W3Ohzq&M-4PB8cAsk`NBC~stWj{66@ C|DR|8 diff --git a/src/main/resources/creativeitems.json b/src/main/resources/creativeitems.json index 407032fd5b0..c142d588a58 100644 --- a/src/main/resources/creativeitems.json +++ b/src/main/resources/creativeitems.json @@ -1,3763 +1,4182 @@ { - "items": [ + "items" : [ { - "id": 5 + "id" : 5 }, { - "id": 5, - "damage": 1 + "id" : 5, + "damage" : 1 }, { - "id": 5, - "damage": 2 + "id" : 5, + "damage" : 2 }, { - "id": 5, - "damage": 3 + "id" : 5, + "damage" : 3 }, { - "id": 5, - "damage": 4 + "id" : 5, + "damage" : 4 }, { - "id": 5, - "damage": 5 + "id" : 5, + "damage" : 5 }, { - "id": 139 + "id" : 139 }, { - "id": 139, - "damage": 1 + "id" : 139, + "damage" : 1 }, { - "id": 139, - "damage": 2 + "id" : 139, + "damage" : 2 }, { - "id": 139, - "damage": 3 + "id" : 139, + "damage" : 3 }, { - "id": 139, - "damage": 4 + "id" : 139, + "damage" : 4 }, { - "id": 139, - "damage": 5 + "id" : 139, + "damage" : 5 }, { - "id": 139, - "damage": 12 + "id" : 139, + "damage" : 12 }, { - "id": 139, - "damage": 7 + "id" : 139, + "damage" : 7 }, { - "id": 139, - "damage": 8 + "id" : 139, + "damage" : 8 }, { - "id": 139, - "damage": 6 + "id" : 139, + "damage" : 6 }, { - "id": 139, - "damage": 9 + "id" : 139, + "damage" : 9 }, { - "id": 139, - "damage": 13 + "id" : 139, + "damage" : 13 }, { - "id": 139, - "damage": 10 + "id" : 139, + "damage" : 10 }, { - "id": 139, - "damage": 11 + "id" : 139, + "damage" : 11 }, { - "id": 85 + "id" : 85 }, { - "id": 85, - "damage": 1 + "id" : 85, + "damage" : 1 }, { - "id": 85, - "damage": 2 + "id" : 85, + "damage" : 2 }, { - "id": 85, - "damage": 3 + "id" : 85, + "damage" : 3 }, { - "id": 85, - "damage": 4 + "id" : 85, + "damage" : 4 }, { - "id": 85, - "damage": 5 + "id" : 85, + "damage" : 5 }, { - "id": 113 + "id" : 113 }, { - "id": 107 + "id" : 107 }, { - "id": 183 + "id" : 183 }, { - "id": 184 + "id" : 184 }, { - "id": 185 + "id" : 185 }, { - "id": 187 + "id" : 187 }, { - "id": 186 + "id" : 186 }, { - "id": 67 + "id" : -180 }, { - "id": 53 + "id" : 67 }, { - "id": 134 + "id" : -179 }, { - "id": 135 + "id" : 53 }, { - "id": 136 + "id" : 134 }, { - "id": 163 + "id" : 135 }, { - "id": 164 + "id" : 136 }, { - "id": 109 + "id" : 163 }, { - "id": 128 + "id" : 164 }, { - "id": 180 + "id" : 109 }, { - "id": 108 + "id" : -175 }, { - "id": 114 + "id" : 128 }, { - "id": 156 + "id" : -177 }, { - "id": 203 + "id" : 180 }, { - "id": 324 + "id" : -176 }, { - "id": 427 + "id" : -169 }, { - "id": 428 + "id" : -172 }, { - "id": 429 + "id" : -170 }, { - "id": 430 + "id" : -173 }, { - "id": 431 + "id" : -171 }, { - "id": 330 + "id" : -174 }, { - "id": 96 + "id" : 108 }, { - "id": 167 + "id" : 114 }, { - "id": 101 + "id" : -184 }, { - "id": 20 + "id" : -178 }, { - "id": 241 + "id" : 156 }, { - "id": 241, - "damage": 8 + "id" : -185 }, { - "id": 241, - "damage": 7 + "id" : 203 }, { - "id": 241, - "damage": 15 + "id" : -2 }, { - "id": 241, - "damage": 12 + "id" : -3 }, { - "id": 241, - "damage": 14 + "id" : -4 }, { - "id": 241, - "damage": 1 + "id" : 324 }, { - "id": 241, - "damage": 4 + "id" : 427 }, { - "id": 241, - "damage": 5 + "id" : 428 }, { - "id": 241, - "damage": 13 + "id" : 429 }, { - "id": 241, - "damage": 9 + "id" : 430 }, { - "id": 241, - "damage": 3 + "id" : 431 }, { - "id": 241, - "damage": 11 + "id" : 330 }, { - "id": 241, - "damage": 10 + "id" : 96 }, { - "id": 241, - "damage": 2 + "id" : -149 }, { - "id": 241, - "damage": 6 + "id" : -146 }, { - "id": 102 + "id" : -148 }, { - "id": 160 + "id" : -145 }, { - "id": 160, - "damage": 8 + "id" : -147 }, { - "id": 160, - "damage": 7 + "id" : 167 }, { - "id": 160, - "damage": 15 + "id" : 101 }, { - "id": 160, - "damage": 12 + "id" : 20 }, { - "id": 160, - "damage": 14 + "id" : 241 }, { - "id": 160, - "damage": 1 + "id" : 241, + "damage" : 8 }, { - "id": 160, - "damage": 4 + "id" : 241, + "damage" : 7 }, { - "id": 160, - "damage": 5 + "id" : 241, + "damage" : 15 }, { - "id": 160, - "damage": 13 + "id" : 241, + "damage" : 12 }, { - "id": 160, - "damage": 9 + "id" : 241, + "damage" : 14 }, { - "id": 160, - "damage": 3 + "id" : 241, + "damage" : 1 }, { - "id": 160, - "damage": 11 + "id" : 241, + "damage" : 4 }, { - "id": 160, - "damage": 10 + "id" : 241, + "damage" : 5 }, { - "id": 160, - "damage": 2 + "id" : 241, + "damage" : 13 }, { - "id": 160, - "damage": 6 + "id" : 241, + "damage" : 9 }, { - "id": 65 + "id" : 241, + "damage" : 3 }, { - "id": 44 + "id" : 241, + "damage" : 11 }, { - "id": 44, - "damage": 3 + "id" : 241, + "damage" : 10 }, { - "id": 182, - "damage": 5 + "id" : 241, + "damage" : 2 }, { - "id": 158 + "id" : 241, + "damage" : 6 }, { - "id": 158, - "damage": 1 + "id" : 102 }, { - "id": 158, - "damage": 2 + "id" : 160 }, { - "id": 158, - "damage": 3 + "id" : 160, + "damage" : 8 }, { - "id": 158, - "damage": 4 + "id" : 160, + "damage" : 7 }, { - "id": 158, - "damage": 5 + "id" : 160, + "damage" : 15 }, { - "id": 44, - "damage": 5 + "id" : 160, + "damage" : 12 }, { - "id": 44, - "damage": 1 + "id" : 160, + "damage" : 14 }, { - "id": 182, - "damage": 6 + "id" : 160, + "damage" : 1 }, { - "id": 182 + "id" : 160, + "damage" : 4 }, { - "id": 44, - "damage": 4 + "id" : 160, + "damage" : 5 }, { - "id": 44, - "damage": 7 + "id" : 160, + "damage" : 13 }, { - "id": 182, - "damage": 7 + "id" : 160, + "damage" : 9 }, { - "id": 44, - "damage": 6 + "id" : 160, + "damage" : 3 }, { - "id": 182, - "damage": 1 + "id" : 160, + "damage" : 11 }, { - "id": 182, - "damage": 2 + "id" : 160, + "damage" : 10 }, { - "id": 182, - "damage": 3 + "id" : 160, + "damage" : 2 }, { - "id": 182, - "damage": 4 + "id" : 160, + "damage" : 6 }, { - "id": 45 + "id" : 65 }, { - "id": 98 + "id" : -165 }, { - "id": 98, - "damage": 1 + "id" : 44 }, { - "id": 98, - "damage": 2 + "id" : -166, + "damage" : 2 }, { - "id": 98, - "damage": 3 + "id" : 44, + "damage" : 3 }, { - "id": 206 + "id" : 182, + "damage" : 5 }, { - "id": 168, - "damage": 2 + "id" : 158 }, { - "id": 4 + "id" : 158, + "damage" : 1 }, { - "id": 48 + "id" : 158, + "damage" : 2 }, { - "id": 24 + "id" : 158, + "damage" : 3 }, { - "id": 24, - "damage": 1 + "id" : 158, + "damage" : 4 }, { - "id": 24, - "damage": 2 + "id" : 158, + "damage" : 5 }, { - "id": 24, - "damage": 3 + "id" : 44, + "damage" : 5 }, { - "id": 179 + "id" : -166 }, { - "id": 179, - "damage": 1 + "id" : 44, + "damage" : 1 }, { - "id": 179, - "damage": 2 + "id" : -166, + "damage" : 3 }, { - "id": 179, - "damage": 3 + "id" : 182, + "damage" : 6 }, { - "id": 173 + "id" : 182 }, { - "id": 41 + "id" : -166, + "damage" : 4 }, { - "id": 42 + "id" : -162, + "damage" : 1 }, { - "id": 133 + "id" : -162, + "damage" : 6 }, { - "id": 57 + "id" : -162, + "damage" : 7 }, { - "id": 22 + "id" : -162, + "damage" : 4 }, { - "id": 155 + "id" : -162, + "damage" : 5 }, { - "id": 155, - "damage": 2 + "id" : -162, + "damage" : 3 }, { - "id": 155, - "damage": 1 + "id" : -162, + "damage" : 2 }, { - "id": 155, - "damage": 3 + "id" : 44, + "damage" : 4 }, { - "id": 168 + "id" : 44, + "damage" : 7 }, { - "id": 168, - "damage": 1 + "id" : 182, + "damage" : 7 }, { - "id": 165 + "id" : -162 }, { - "id": 170 + "id" : 44, + "damage" : 6 }, { - "id": 216 + "id" : -166, + "damage" : 1 }, { - "id": 214 + "id" : 182, + "damage" : 1 }, { - "id": 112 + "id" : 182, + "damage" : 2 }, { - "id": 215 + "id" : 182, + "damage" : 3 }, { - "id": 35 + "id" : 182, + "damage" : 4 }, { - "id": 35, - "damage": 8 + "id" : 45 }, { - "id": 35, - "damage": 7 + "id" : 98 }, { - "id": 35, - "damage": 15 + "id" : 98, + "damage" : 1 }, { - "id": 35, - "damage": 12 + "id" : 98, + "damage" : 2 }, { - "id": 35, - "damage": 14 + "id" : 98, + "damage" : 3 }, { - "id": 35, - "damage": 1 + "id" : 206 }, { - "id": 35, - "damage": 4 + "id" : 168, + "damage" : 2 }, { - "id": 35, - "damage": 5 + "id" : 4 }, { - "id": 35, - "damage": 13 + "id" : 48 }, { - "id": 35, - "damage": 9 + "id" : -183 }, { - "id": 35, - "damage": 3 + "id" : 24 }, { - "id": 35, - "damage": 11 + "id" : 24, + "damage" : 1 }, { - "id": 35, - "damage": 10 + "id" : 24, + "damage" : 2 }, { - "id": 35, - "damage": 2 + "id" : 24, + "damage" : 3 }, { - "id": 35, - "damage": 6 + "id" : 179 }, { - "id": 171 + "id" : 179, + "damage" : 1 }, { - "id": 171, - "damage": 8 + "id" : 179, + "damage" : 2 }, { - "id": 171, - "damage": 7 + "id" : 179, + "damage" : 3 }, { - "id": 171, - "damage": 15 + "id" : 173 }, { - "id": 171, - "damage": 12 + "id" : -139 }, { - "id": 171, - "damage": 14 + "id" : 41 }, { - "id": 171, - "damage": 1 + "id" : 42 }, { - "id": 171, - "damage": 4 + "id" : 133 }, { - "id": 171, - "damage": 5 + "id" : 57 }, { - "id": 171, - "damage": 13 + "id" : 22 }, { - "id": 171, - "damage": 9 + "id" : 155 }, { - "id": 171, - "damage": 3 + "id" : 155, + "damage" : 2 }, { - "id": 171, - "damage": 11 + "id" : 155, + "damage" : 1 }, { - "id": 171, - "damage": 10 + "id" : 155, + "damage" : 3 }, { - "id": 171, - "damage": 2 + "id" : 168 }, { - "id": 171, - "damage": 6 + "id" : 168, + "damage" : 1 }, { - "id": 237 + "id" : 165 }, { - "id": 237, - "damage": 8 + "id" : 170 }, { - "id": 237, - "damage": 7 + "id" : 216 }, { - "id": 237, - "damage": 15 + "id" : 214 }, { - "id": 237, - "damage": 12 + "id" : 112 }, { - "id": 237, - "damage": 14 + "id" : 215 }, { - "id": 237, - "damage": 1 + "id" : 35 }, { - "id": 237, - "damage": 4 + "id" : 35, + "damage" : 8 }, { - "id": 237, - "damage": 5 + "id" : 35, + "damage" : 7 }, { - "id": 237, - "damage": 13 + "id" : 35, + "damage" : 15 }, { - "id": 237, - "damage": 9 + "id" : 35, + "damage" : 12 }, { - "id": 237, - "damage": 3 + "id" : 35, + "damage" : 14 }, { - "id": 237, - "damage": 11 + "id" : 35, + "damage" : 1 }, { - "id": 237, - "damage": 10 + "id" : 35, + "damage" : 4 }, { - "id": 237, - "damage": 2 + "id" : 35, + "damage" : 5 }, { - "id": 237, - "damage": 6 + "id" : 35, + "damage" : 13 }, { - "id": 236 + "id" : 35, + "damage" : 9 }, { - "id": 236, - "damage": 8 + "id" : 35, + "damage" : 3 }, { - "id": 236, - "damage": 7 + "id" : 35, + "damage" : 11 }, { - "id": 236, - "damage": 15 + "id" : 35, + "damage" : 10 }, { - "id": 236, - "damage": 12 + "id" : 35, + "damage" : 2 }, { - "id": 236, - "damage": 14 + "id" : 35, + "damage" : 6 }, { - "id": 236, - "damage": 1 + "id" : 171 }, { - "id": 236, - "damage": 4 + "id" : 171, + "damage" : 8 }, { - "id": 236, - "damage": 5 + "id" : 171, + "damage" : 7 }, { - "id": 236, - "damage": 13 + "id" : 171, + "damage" : 15 }, { - "id": 236, - "damage": 9 + "id" : 171, + "damage" : 12 }, { - "id": 236, - "damage": 3 + "id" : 171, + "damage" : 14 }, { - "id": 236, - "damage": 11 + "id" : 171, + "damage" : 1 }, { - "id": 236, - "damage": 10 + "id" : 171, + "damage" : 4 }, { - "id": 236, - "damage": 2 + "id" : 171, + "damage" : 5 }, { - "id": 236, - "damage": 6 + "id" : 171, + "damage" : 13 }, { - "id": 82 + "id" : 171, + "damage" : 9 }, { - "id": 172 + "id" : 171, + "damage" : 3 }, { - "id": 159 + "id" : 171, + "damage" : 11 }, { - "id": 159, - "damage": 8 + "id" : 171, + "damage" : 10 }, { - "id": 159, - "damage": 7 + "id" : 171, + "damage" : 2 }, { - "id": 159, - "damage": 15 + "id" : 171, + "damage" : 6 }, { - "id": 159, - "damage": 12 + "id" : 237 }, { - "id": 159, - "damage": 14 + "id" : 237, + "damage" : 8 }, { - "id": 159, - "damage": 1 + "id" : 237, + "damage" : 7 }, { - "id": 159, - "damage": 4 + "id" : 237, + "damage" : 15 }, { - "id": 159, - "damage": 5 + "id" : 237, + "damage" : 12 }, { - "id": 159, - "damage": 13 + "id" : 237, + "damage" : 14 }, { - "id": 159, - "damage": 9 + "id" : 237, + "damage" : 1 }, { - "id": 159, - "damage": 3 + "id" : 237, + "damage" : 4 }, { - "id": 159, - "damage": 11 + "id" : 237, + "damage" : 5 }, { - "id": 159, - "damage": 10 + "id" : 237, + "damage" : 13 }, { - "id": 159, - "damage": 2 + "id" : 237, + "damage" : 9 }, { - "id": 159, - "damage": 6 + "id" : 237, + "damage" : 3 }, { - "id": 220 + "id" : 237, + "damage" : 11 }, { - "id": 228 + "id" : 237, + "damage" : 10 }, { - "id": 227 + "id" : 237, + "damage" : 2 }, { - "id": 235 + "id" : 237, + "damage" : 6 }, { - "id": 232 + "id" : 236 }, { - "id": 234 + "id" : 236, + "damage" : 8 }, { - "id": 221 + "id" : 236, + "damage" : 7 }, { - "id": 224 + "id" : 236, + "damage" : 15 }, { - "id": 225 + "id" : 236, + "damage" : 12 }, { - "id": 233 + "id" : 236, + "damage" : 14 }, { - "id": 229 + "id" : 236, + "damage" : 1 }, { - "id": 223 + "id" : 236, + "damage" : 4 }, { - "id": 231 + "id" : 236, + "damage" : 5 }, { - "id": 219 + "id" : 236, + "damage" : 13 }, { - "id": 222 + "id" : 236, + "damage" : 9 }, { - "id": 226 + "id" : 236, + "damage" : 3 }, { - "id": 201 + "id" : 236, + "damage" : 11 }, { - "id": 201, - "damage": 2 + "id" : 236, + "damage" : 10 }, { - "id": 3 + "id" : 236, + "damage" : 2 }, { - "id": 3, - "damage": 1 + "id" : 236, + "damage" : 6 }, { - "id": 2 + "id" : 82 }, { - "id": 243 + "id" : 172 }, { - "id": 110 + "id" : 159 }, { - "id": 1 + "id" : 159, + "damage" : 8 }, { - "id": 15 + "id" : 159, + "damage" : 7 }, { - "id": 14 + "id" : 159, + "damage" : 15 }, { - "id": 56 + "id" : 159, + "damage" : 12 }, { - "id": 21 + "id" : 159, + "damage" : 14 }, { - "id": 73 + "id" : 159, + "damage" : 1 }, { - "id": 16 + "id" : 159, + "damage" : 4 }, { - "id": 129 + "id" : 159, + "damage" : 5 }, { - "id": 153 + "id" : 159, + "damage" : 13 }, { - "id": 13 + "id" : 159, + "damage" : 9 }, { - "id": 1, - "damage": 1 + "id" : 159, + "damage" : 3 }, { - "id": 1, - "damage": 3 + "id" : 159, + "damage" : 11 }, { - "id": 1, - "damage": 5 + "id" : 159, + "damage" : 10 }, { - "id": 1, - "damage": 2 + "id" : 159, + "damage" : 2 }, { - "id": 1, - "damage": 4 + "id" : 159, + "damage" : 6 }, { - "id": 1, - "damage": 6 + "id" : 220 }, { - "id": 12 + "id" : 228 }, { - "id": 12, - "damage": 1 + "id" : 227 }, { - "id": 81 + "id" : 235 }, { - "id": 17 + "id" : 232 }, { - "id": 17, - "damage": 1 + "id" : 234 }, { - "id": 17, - "damage": 2 + "id" : 221 }, { - "id": 17, - "damage": 3 + "id" : 224 }, { - "id": 162 + "id" : 225 }, { - "id": 162, - "damage": 1 + "id" : 233 }, { - "id": 18 + "id" : 229 }, { - "id": 18, - "damage": 1 + "id" : 223 }, { - "id": 18, - "damage": 2 + "id" : 231 }, { - "id": 18, - "damage": 3 + "id" : 219 }, { - "id": 161 + "id" : 222 }, { - "id": 161, - "damage": 1 + "id" : 226 }, { - "id": 6 + "id" : 201 }, { - "id": 6, - "damage": 1 + "id" : 201, + "damage" : 2 }, { - "id": 6, - "damage": 2 + "id" : 3 }, { - "id": 6, - "damage": 3 + "id" : 3, + "damage" : 1 }, { - "id": 6, - "damage": 4 + "id" : 2 }, { - "id": 6, - "damage": 5 + "id" : 198 }, { - "id": 295 + "id" : 243 }, { - "id": 361 + "id" : 110 }, { - "id": 362 + "id" : 1 }, { - "id": 458 + "id" : 15 }, { - "id": 296 + "id" : 14 }, { - "id": 457 + "id" : 56 }, { - "id": 392 + "id" : 21 }, { - "id": 394 + "id" : 73 }, { - "id": 391 + "id" : 16 }, { - "id": 396 + "id" : 129 }, { - "id": 260 + "id" : 153 }, { - "id": 322 + "id" : 13 }, { - "id": 466 + "id" : 1, + "damage" : 1 }, { - "id": 103 + "id" : 1, + "damage" : 3 }, { - "id": 360 + "id" : 1, + "damage" : 5 }, { - "id": 382 + "id" : 1, + "damage" : 2 }, { - "id": 477 + "id" : 1, + "damage" : 4 }, { - "id": 86 + "id" : 1, + "damage" : 6 }, { - "id": 91 + "id" : 12 }, { - "id": 31, - "damage": 2 + "id" : 12, + "damage" : 1 }, { - "id": 175, - "damage": 3 + "id" : 81 }, { - "id": 31, - "damage": 1 + "id" : 17 }, { - "id": 175, - "damage": 2 + "id" : -10 }, { - "id": 335 + "id" : 17, + "damage" : 1 }, { - "id": 37 + "id" : -5 }, { - "id": 38 + "id" : 17, + "damage" : 2 }, { - "id": 38, - "damage": 1 + "id" : -6 }, { - "id": 38, - "damage": 2 + "id" : 17, + "damage" : 3 }, { - "id": 38, - "damage": 3 + "id" : -7 }, { - "id": 38, - "damage": 4 + "id" : 162 }, { - "id": 38, - "damage": 5 + "id" : -8 }, { - "id": 38, - "damage": 6 + "id" : 162, + "damage" : 1 }, { - "id": 38, - "damage": 7 + "id" : -9 }, { - "id": 38, - "damage": 8 + "id" : -212 }, { - "id": 38, - "damage": 9 + "id" : -212, + "damage" : 8 }, { - "id": 38, - "damage": 10 + "id" : -212, + "damage" : 1 }, { - "id": 175 + "id" : -212, + "damage" : 9 }, { - "id": 175, - "damage": 1 + "id" : -212, + "damage" : 2 }, { - "id": 175, - "damage": 4 + "id" : -212, + "damage" : 10 }, { - "id": 175, - "damage": 5 + "id" : -212, + "damage" : 3 }, { - "id": 351, - "damage": 19 + "id" : -212, + "damage" : 11 }, { - "id": 351, - "damage": 7 + "id" : -212, + "damage" : 4 }, { - "id": 351, - "damage": 8 + "id" : -212, + "damage" : 12 }, { - "id": 351, - "damage": 16 + "id" : -212, + "damage" : 5 }, { - "id": 351, - "damage": 17 + "id" : -212, + "damage" : 13 }, { - "id": 351, - "damage": 1 + "id" : 18 }, { - "id": 351, - "damage": 14 + "id" : 18, + "damage" : 1 }, { - "id": 351, - "damage": 11 + "id" : 18, + "damage" : 2 }, { - "id": 351, - "damage": 10 + "id" : 18, + "damage" : 3 }, { - "id": 351, - "damage": 2 + "id" : 161 }, { - "id": 351, - "damage": 6 + "id" : 161, + "damage" : 1 }, { - "id": 351, - "damage": 12 + "id" : 6 }, { - "id": 351, - "damage": 18 + "id" : 6, + "damage" : 1 }, { - "id": 351, - "damage": 5 + "id" : 6, + "damage" : 2 }, { - "id": 351, - "damage": 13 + "id" : 6, + "damage" : 3 }, { - "id": 351, - "damage": 9 + "id" : 6, + "damage" : 4 }, { - "id": 351 + "id" : 6, + "damage" : 5 }, { - "id": 351, - "damage": 3 + "id" : 295 }, { - "id": 351, - "damage": 4 + "id" : 361 }, { - "id": 351, - "damage": 15 + "id" : 362 }, { - "id": 106 + "id" : 458 }, { - "id": 111 + "id" : 296 }, { - "id": 32 + "id" : 457 }, { - "id": 80 + "id" : 392 }, { - "id": 79 + "id" : 394 }, { - "id": 174 + "id" : 391 }, { - "id": 78 + "id" : 396 }, { - "id": 365 + "id" : 260 }, { - "id": 319 + "id" : 322 }, { - "id": 363 + "id" : 466 }, { - "id": 423 + "id" : 103 }, { - "id": 411 + "id" : 360 }, { - "id": 349 + "id" : 382 }, { - "id": 460 + "id" : 477 }, { - "id": 461 + "id" : 86 }, { - "id": 462 + "id" : -155 }, { - "id": 39 + "id" : 91 }, { - "id": 40 + "id" : 31, + "damage" : 2 }, { - "id": 99, - "damage": 14 + "id" : 175, + "damage" : 3 }, { - "id": 100, - "damage": 14 + "id" : 31, + "damage" : 1 }, { - "id": 99, - "damage": 15 + "id" : 175, + "damage" : 2 }, { - "id": 99 + "id" : -131, + "damage" : 3 }, { - "id": 344 + "id" : -131, + "damage" : 1 }, { - "id": 338 + "id" : -131, + "damage" : 2 }, { - "id": 353 + "id" : -131 }, { - "id": 367 + "id" : -131, + "damage" : 4 }, { - "id": 352 + "id" : -131, + "damage" : 11 }, { - "id": 30 + "id" : -131, + "damage" : 9 }, { - "id": 375 + "id" : -131, + "damage" : 10 }, { - "id": 52 + "id" : -131, + "damage" : 8 }, { - "id": 97 + "id" : -131, + "damage" : 12 }, { - "id": 97, - "damage": 1 + "id" : -133, + "damage" : 3 }, { - "id": 97, - "damage": 2 + "id" : -133, + "damage" : 1 }, { - "id": 97, - "damage": 3 + "id" : -133, + "damage" : 2 }, { - "id": 97, - "damage": 4 + "id" : -133 }, { - "id": 97, - "damage": 5 + "id" : -133, + "damage" : 4 }, { - "id": 122 + "id" : -134, + "damage" : 3 }, { - "id": 383, - "damage": 10 + "id" : -134, + "damage" : 1 }, { - "id": 383, - "damage": 11 + "id" : -134, + "damage" : 2 }, { - "id": 383, - "damage": 12 + "id" : -134 }, { - "id": 383, - "damage": 13 + "id" : -134, + "damage" : 4 }, { - "id": 383, - "damage": 14 + "id" : 335 }, { - "id": 383, - "damage": 28 + "id" : -130 }, { - "id": 383, - "damage": 22 + "id" : 37 }, { - "id": 383, - "damage": 75 + "id" : 38 }, { - "id": 383, - "damage": 16 + "id" : 38, + "damage" : 1 }, { - "id": 383, - "damage": 19 + "id" : 38, + "damage" : 2 }, { - "id": 383, - "damage": 30 + "id" : 38, + "damage" : 3 }, { - "id": 383, - "damage": 18 + "id" : 38, + "damage" : 4 }, { - "id": 383, - "damage": 29 + "id" : 38, + "damage" : 5 }, { - "id": 383, - "damage": 23 + "id" : 38, + "damage" : 6 }, { - "id": 383, - "damage": 24 + "id" : 38, + "damage" : 7 }, { - "id": 383, - "damage": 25 + "id" : 38, + "damage" : 8 }, { - "id": 383, - "damage": 26 + "id" : 38, + "damage" : 9 }, { - "id": 383, - "damage": 27 + "id" : 38, + "damage" : 10 }, { - "id": 383, - "damage": 111 + "id" : 175 }, { - "id": 383, - "damage": 112 + "id" : 175, + "damage" : 1 }, { - "id": 383, - "damage": 108 + "id" : 175, + "damage" : 4 }, { - "id": 383, - "damage": 109 + "id" : 175, + "damage" : 5 }, { - "id": 383, - "damage": 31 + "id" : -216 }, { - "id": 383, - "damage": 74 + "id" : 351, + "damage" : 19 }, { - "id": 383, - "damage": 113 + "id" : 351, + "damage" : 7 }, { - "id": 383, - "damage": 33 + "id" : 351, + "damage" : 8 }, { - "id": 383, - "damage": 38 + "id" : 351, + "damage" : 16 }, { - "id": 383, - "damage": 39 + "id" : 351, + "damage" : 17 }, { - "id": 383, - "damage": 34 + "id" : 351, + "damage" : 1 }, { - "id": 383, - "damage": 48 + "id" : 351, + "damage" : 14 }, { - "id": 383, - "damage": 46 + "id" : 351, + "damage" : 11 }, { - "id": 383, - "damage": 37 + "id" : 351, + "damage" : 10 }, { - "id": 383, - "damage": 35 + "id" : 351, + "damage" : 2 }, { - "id": 383, - "damage": 32 + "id" : 351, + "damage" : 6 }, { - "id": 383, - "damage": 36 + "id" : 351, + "damage" : 12 }, { - "id": 383, - "damage": 47 + "id" : 351, + "damage" : 18 }, { - "id": 383, - "damage": 110 + "id" : 351, + "damage" : 5 }, { - "id": 383, - "damage": 17 + "id" : 351, + "damage" : 13 }, { - "id": 383, - "damage": 40 + "id" : 351, + "damage" : 9 }, { - "id": 383, - "damage": 45 + "id" : 351 }, { - "id": 383, - "damage": 49 + "id" : 351, + "damage" : 3 }, { - "id": 383, - "damage": 50 + "id" : 351, + "damage" : 4 }, { - "id": 383, - "damage": 55 + "id" : 351, + "damage" : 15 }, { - "id": 383, - "damage": 42 + "id" : 106 }, { - "id": 383, - "damage": 41 + "id" : 111 }, { - "id": 383, - "damage": 43 + "id" : 32 }, { - "id": 383, - "damage": 54 + "id" : -163 }, { - "id": 383, - "damage": 57 + "id" : 80 }, { - "id": 383, - "damage": 104 + "id" : 79 }, { - "id": 383, - "damage": 105 + "id" : 174 }, { - "id": 383, - "damage": 115 + "id" : -11 }, { - "id": 383, - "damage": 118 + "id" : 78 }, { - "id": 383, - "damage": 116 + "id" : 365 }, { - "id": 383, - "damage": 58 + "id" : 319 }, { - "id": 383, - "damage": 114 + "id" : 363 }, { - "id": 383, - "damage": 59 + "id" : 423 }, { - "id": 49 + "id" : 411 }, { - "id": 7 + "id" : 349 }, { - "id": 88 + "id" : 460 }, { - "id": 87 + "id" : 461 }, { - "id": 213 + "id" : 462 }, { - "id": 372 + "id" : 39 }, { - "id": 121 + "id" : 40 }, { - "id": 200 + "id" : 99, + "damage" : 14 }, { - "id": 240 + "id" : 100, + "damage" : 14 }, { - "id": 432 + "id" : 99, + "damage" : 15 }, { - "id": 433 + "id" : 99 }, { - "id": 19 + "id" : 344 }, { - "id": 19, - "damage": 1 + "id" : 338 }, { - "id": 298 + "id" : 353 }, { - "id": 302 + "id" : 367 }, { - "id": 306 + "id" : 352 }, { - "id": 314 + "id" : 30 }, { - "id": 310 + "id" : 375 }, { - "id": 299 + "id" : 52 }, { - "id": 303 + "id" : 97 }, { - "id": 307 + "id" : 97, + "damage" : 1 }, { - "id": 315 + "id" : 97, + "damage" : 2 }, { - "id": 311 + "id" : 97, + "damage" : 3 }, { - "id": 300 + "id" : 97, + "damage" : 4 }, { - "id": 304 + "id" : 97, + "damage" : 5 }, { - "id": 308 + "id" : 122 }, { - "id": 316 + "id" : -159 }, { - "id": 312 + "id" : 383, + "damage" : 10 }, { - "id": 301 + "id" : 383, + "damage" : 11 }, { - "id": 305 + "id" : 383, + "damage" : 12 }, { - "id": 309 + "id" : 383, + "damage" : 13 }, { - "id": 317 + "id" : 383, + "damage" : 14 }, { - "id": 313 + "id" : 383, + "damage" : 28 }, { - "id": 268 + "id" : 383, + "damage" : 22 }, { - "id": 272 + "id" : 383, + "damage" : 75 }, { - "id": 267 + "id" : 383, + "damage" : 16 }, { - "id": 283 + "id" : 383, + "damage" : 19 }, { - "id": 276 + "id" : 383, + "damage" : 30 }, { - "id": 271 + "id" : 383, + "damage" : 18 }, { - "id": 275 + "id" : 383, + "damage" : 29 }, { - "id": 258 + "id" : 383, + "damage" : 23 }, { - "id": 286 + "id" : 383, + "damage" : 24 }, { - "id": 279 + "id" : 383, + "damage" : 25 }, { - "id": 270 + "id" : 383, + "damage" : 26 }, { - "id": 274 + "id" : 383, + "damage" : 27 }, { - "id": 257 + "id" : 383, + "damage" : 111 }, { - "id": 285 + "id" : 383, + "damage" : 112 }, { - "id": 278 + "id" : 383, + "damage" : 108 }, { - "id": 269 + "id" : 383, + "damage" : 109 }, { - "id": 273 + "id" : 383, + "damage" : 31 }, { - "id": 256 + "id" : 383, + "damage" : 74 }, { - "id": 284 + "id" : 383, + "damage" : 113 }, { - "id": 277 + "id" : 383, + "damage" : 121 }, { - "id": 290 + "id" : 383, + "damage" : 33 }, { - "id": 291 + "id" : 383, + "damage" : 38 }, { - "id": 292 + "id" : 383, + "damage" : 39 }, { - "id": 294 + "id" : 383, + "damage" : 34 }, { - "id": 293 + "id" : 383, + "damage" : 48 }, { - "id": 261 + "id" : 383, + "damage" : 46 }, { - "id": 471 + "id" : 383, + "damage" : 37 }, { - "id": 262 + "id" : 383, + "damage" : 35 }, { - "id": 262, - "damage": 6 + "id" : 383, + "damage" : 32 }, { - "id": 262, - "damage": 7 + "id" : 383, + "damage" : 36 }, { - "id": 262, - "damage": 8 + "id" : 383, + "damage" : 47 }, { - "id": 262, - "damage": 9 + "id" : 383, + "damage" : 110 }, { - "id": 262, - "damage": 10 + "id" : 383, + "damage" : 17 }, { - "id": 262, - "damage": 11 + "id" : 383, + "damage" : 40 }, { - "id": 262, - "damage": 12 + "id" : 383, + "damage" : 45 }, { - "id": 262, - "damage": 13 + "id" : 383, + "damage" : 49 }, { - "id": 262, - "damage": 14 + "id" : 383, + "damage" : 50 }, { - "id": 262, - "damage": 15 + "id" : 383, + "damage" : 55 }, { - "id": 262, - "damage": 16 + "id" : 383, + "damage" : 42 }, { - "id": 262, - "damage": 17 + "id" : 383, + "damage" : 41 }, { - "id": 262, - "damage": 18 + "id" : 383, + "damage" : 43 }, { - "id": 262, - "damage": 19 + "id" : 383, + "damage" : 54 }, { - "id": 262, - "damage": 20 + "id" : 383, + "damage" : 57 }, { - "id": 262, - "damage": 21 + "id" : 383, + "damage" : 104 }, { - "id": 262, - "damage": 22 + "id" : 383, + "damage" : 105 }, { - "id": 262, - "damage": 23 + "id" : 383, + "damage" : 115 }, { - "id": 262, - "damage": 24 + "id" : 383, + "damage" : 118 }, { - "id": 262, - "damage": 25 + "id" : 383, + "damage" : 116 }, { - "id": 262, - "damage": 26 + "id" : 383, + "damage" : 58 }, { - "id": 262, - "damage": 27 + "id" : 383, + "damage" : 114 }, { - "id": 262, - "damage": 28 + "id" : 383, + "damage" : 59 }, { - "id": 262, - "damage": 29 + "id" : 49 }, { - "id": 262, - "damage": 30 + "id" : 7 }, { - "id": 262, - "damage": 31 + "id" : 88 }, { - "id": 262, - "damage": 32 + "id" : 87 }, { - "id": 262, - "damage": 33 + "id" : 213 }, { - "id": 262, - "damage": 34 + "id" : 372 }, { - "id": 262, - "damage": 35 + "id" : 121 }, { - "id": 262, - "damage": 36 + "id" : 200 }, { - "id": 262, - "damage": 37 + "id" : 240 }, { - "id": 262, - "damage": 38 + "id" : 432 }, { - "id": 262, - "damage": 39 + "id" : 433 }, { - "id": 262, - "damage": 40 + "id" : 19 }, { - "id": 262, - "damage": 41 + "id" : 19, + "damage" : 1 }, { - "id": 262, - "damage": 42 + "id" : -132 }, { - "id": 366 + "id" : -132, + "damage" : 1 }, { - "id": 320 + "id" : -132, + "damage" : 2 }, { - "id": 364 + "id" : -132, + "damage" : 3 }, { - "id": 424 + "id" : -132, + "damage" : 4 }, { - "id": 412 + "id" : -132, + "damage" : 8 }, { - "id": 350 + "id" : -132, + "damage" : 9 }, { - "id": 463 + "id" : -132, + "damage" : 10 }, { - "id": 297 + "id" : -132, + "damage" : 11 }, { - "id": 282 + "id" : -132, + "damage" : 12 }, { - "id": 459 + "id" : 298 }, { - "id": 413 + "id" : 302 }, { - "id": 393 + "id" : 306 }, { - "id": 357 + "id" : 314 }, { - "id": 400 + "id" : 310 }, { - "id": 354 + "id" : 299 }, { - "id": 464 + "id" : 303 }, { - "id": 346 + "id" : 307 }, { - "id": 398 + "id" : 315 }, { - "id": 332 + "id" : 311 }, { - "id": 359 + "id" : 300 }, { - "id": 259 + "id" : 304 }, { - "id": 420 + "id" : 308 }, { - "id": 347 + "id" : 316 }, { - "id": 345 + "id" : 312 }, { - "id": 395 + "id" : 301 }, { - "id": 395, - "damage": 2 + "id" : 305 }, { - "id": 329 + "id" : 309 }, { - "id": 416 + "id" : 317 }, { - "id": 417 + "id" : 313 }, { - "id": 418 + "id" : 268 }, { - "id": 419 + "id" : 272 }, { - "id": 455 + "id" : 267 }, { - "id": 469 + "id" : 283 }, { - "id": 444 + "id" : 276 }, { - "id": 450 + "id" : 271 }, { - "id": 374 + "id" : 275 }, { - "id": 384 + "id" : 258 }, { - "id": 373 + "id" : 286 }, { - "id": 373, - "damage": 1 + "id" : 279 }, { - "id": 373, - "damage": 2 + "id" : 270 }, { - "id": 373, - "damage": 3 + "id" : 274 }, { - "id": 373, - "damage": 4 + "id" : 257 }, { - "id": 373, - "damage": 5 + "id" : 285 }, { - "id": 373, - "damage": 6 + "id" : 278 }, { - "id": 373, - "damage": 7 + "id" : 269 }, { - "id": 373, - "damage": 8 + "id" : 273 }, { - "id": 373, - "damage": 9 + "id" : 256 }, { - "id": 373, - "damage": 10 + "id" : 284 }, { - "id": 373, - "damage": 11 + "id" : 277 }, { - "id": 373, - "damage": 12 + "id" : 290 }, { - "id": 373, - "damage": 13 + "id" : 291 }, { - "id": 373, - "damage": 14 + "id" : 292 }, { - "id": 373, - "damage": 15 + "id" : 294 }, { - "id": 373, - "damage": 16 + "id" : 293 }, { - "id": 373, - "damage": 17 + "id" : 261 }, { - "id": 373, - "damage": 18 + "id" : 471 }, { - "id": 373, - "damage": 19 + "id" : 262 }, { - "id": 373, - "damage": 20 + "id" : 262, + "damage" : 6 }, { - "id": 373, - "damage": 21 + "id" : 262, + "damage" : 7 }, { - "id": 373, - "damage": 22 + "id" : 262, + "damage" : 8 }, { - "id": 373, - "damage": 23 + "id" : 262, + "damage" : 9 }, { - "id": 373, - "damage": 24 + "id" : 262, + "damage" : 10 }, { - "id": 373, - "damage": 25 + "id" : 262, + "damage" : 11 }, { - "id": 373, - "damage": 26 + "id" : 262, + "damage" : 12 }, { - "id": 373, - "damage": 27 + "id" : 262, + "damage" : 13 }, { - "id": 373, - "damage": 28 + "id" : 262, + "damage" : 14 }, { - "id": 373, - "damage": 29 + "id" : 262, + "damage" : 15 }, { - "id": 373, - "damage": 30 + "id" : 262, + "damage" : 16 }, { - "id": 373, - "damage": 31 + "id" : 262, + "damage" : 17 }, { - "id": 373, - "damage": 32 + "id" : 262, + "damage" : 18 }, { - "id": 373, - "damage": 33 + "id" : 262, + "damage" : 19 }, { - "id": 373, - "damage": 34 + "id" : 262, + "damage" : 20 }, { - "id": 373, - "damage": 35 + "id" : 262, + "damage" : 21 }, { - "id": 373, - "damage": 36 + "id" : 262, + "damage" : 22 }, { - "id": 373, - "damage": 37 + "id" : 262, + "damage" : 23 }, { - "id": 373, - "damage": 38 + "id" : 262, + "damage" : 24 }, { - "id": 373, - "damage": 39 + "id" : 262, + "damage" : 25 }, { - "id": 373, - "damage": 40 + "id" : 262, + "damage" : 26 }, { - "id": 373, - "damage": 41 + "id" : 262, + "damage" : 27 }, { - "id": 438 + "id" : 262, + "damage" : 28 }, { - "id": 438, - "damage": 1 + "id" : 262, + "damage" : 29 }, { - "id": 438, - "damage": 2 + "id" : 262, + "damage" : 30 }, { - "id": 438, - "damage": 3 + "id" : 262, + "damage" : 31 }, { - "id": 438, - "damage": 4 + "id" : 262, + "damage" : 32 }, { - "id": 438, - "damage": 5 + "id" : 262, + "damage" : 33 }, { - "id": 438, - "damage": 6 + "id" : 262, + "damage" : 34 }, { - "id": 438, - "damage": 7 + "id" : 262, + "damage" : 35 }, { - "id": 438, - "damage": 8 + "id" : 262, + "damage" : 36 }, { - "id": 438, - "damage": 9 + "id" : 262, + "damage" : 37 }, { - "id": 438, - "damage": 10 + "id" : 262, + "damage" : 38 }, { - "id": 438, - "damage": 11 + "id" : 262, + "damage" : 39 }, { - "id": 438, - "damage": 12 + "id" : 262, + "damage" : 40 }, { - "id": 438, - "damage": 13 + "id" : 262, + "damage" : 41 }, { - "id": 438, - "damage": 14 + "id" : 262, + "damage" : 42 }, { - "id": 438, - "damage": 15 + "id" : 513 }, { - "id": 438, - "damage": 16 + "id" : 366 }, { - "id": 438, - "damage": 17 + "id" : 320 }, { - "id": 438, - "damage": 18 + "id" : 364 }, { - "id": 438, - "damage": 19 + "id" : 424 }, { - "id": 438, - "damage": 20 + "id" : 412 }, { - "id": 438, - "damage": 21 + "id" : 350 }, { - "id": 438, - "damage": 22 + "id" : 463 }, { - "id": 438, - "damage": 23 + "id" : 297 }, { - "id": 438, - "damage": 24 + "id" : 282 }, { - "id": 438, - "damage": 25 + "id" : 459 }, { - "id": 438, - "damage": 26 + "id" : 413 }, { - "id": 438, - "damage": 27 + "id" : 393 }, { - "id": 438, - "damage": 28 + "id" : 357 }, { - "id": 438, - "damage": 29 + "id" : 400 }, { - "id": 438, - "damage": 30 + "id" : 354 }, { - "id": 438, - "damage": 31 + "id" : 464 }, { - "id": 438, - "damage": 32 + "id" : 346 }, { - "id": 438, - "damage": 33 + "id" : 398 }, { - "id": 438, - "damage": 34 + "id" : 332 }, { - "id": 438, - "damage": 35 + "id" : 359 }, { - "id": 438, - "damage": 36 + "id" : 259 }, { - "id": 438, - "damage": 37 + "id" : 420 }, { - "id": 438, - "damage": 38 + "id" : 347 }, { - "id": 438, - "damage": 39 + "id" : 345 }, { - "id": 438, - "damage": 40 + "id" : 395 }, { - "id": 438, - "damage": 41 + "id" : 395, + "damage" : 2 }, { - "id": 441 + "id" : 329 }, { - "id": 441, - "damage": 1 + "id" : 416 }, { - "id": 441, - "damage": 2 + "id" : 417 }, { - "id": 441, - "damage": 3 + "id" : 418 }, { - "id": 441, - "damage": 4 + "id" : 419 }, { - "id": 441, - "damage": 5 + "id" : 455 }, { - "id": 441, - "damage": 6 + "id" : 469 }, { - "id": 441, - "damage": 7 + "id" : 444 }, { - "id": 441, - "damage": 8 + "id" : 450 }, { - "id": 441, - "damage": 9 + "id" : 374 }, { - "id": 441, - "damage": 10 + "id" : 384 }, { - "id": 441, - "damage": 11 + "id" : 373 }, { - "id": 441, - "damage": 12 + "id" : 373, + "damage" : 1 }, { - "id": 441, - "damage": 13 + "id" : 373, + "damage" : 2 }, { - "id": 441, - "damage": 14 + "id" : 373, + "damage" : 3 }, { - "id": 441, - "damage": 15 + "id" : 373, + "damage" : 4 }, { - "id": 441, - "damage": 16 + "id" : 373, + "damage" : 5 }, { - "id": 441, - "damage": 17 + "id" : 373, + "damage" : 6 }, { - "id": 441, - "damage": 18 + "id" : 373, + "damage" : 7 }, { - "id": 441, - "damage": 19 + "id" : 373, + "damage" : 8 }, { - "id": 441, - "damage": 20 + "id" : 373, + "damage" : 9 }, { - "id": 441, - "damage": 21 + "id" : 373, + "damage" : 10 }, { - "id": 441, - "damage": 22 + "id" : 373, + "damage" : 11 }, { - "id": 441, - "damage": 23 + "id" : 373, + "damage" : 12 }, { - "id": 441, - "damage": 24 + "id" : 373, + "damage" : 13 }, { - "id": 441, - "damage": 25 + "id" : 373, + "damage" : 14 }, { - "id": 441, - "damage": 26 + "id" : 373, + "damage" : 15 }, { - "id": 441, - "damage": 27 + "id" : 373, + "damage" : 16 }, { - "id": 441, - "damage": 28 + "id" : 373, + "damage" : 17 }, { - "id": 441, - "damage": 29 + "id" : 373, + "damage" : 18 }, { - "id": 441, - "damage": 30 + "id" : 373, + "damage" : 19 }, { - "id": 441, - "damage": 31 + "id" : 373, + "damage" : 20 }, { - "id": 441, - "damage": 32 + "id" : 373, + "damage" : 21 }, { - "id": 441, - "damage": 33 + "id" : 373, + "damage" : 22 }, { - "id": 441, - "damage": 34 + "id" : 373, + "damage" : 23 }, { - "id": 441, - "damage": 35 + "id" : 373, + "damage" : 24 }, { - "id": 441, - "damage": 36 + "id" : 373, + "damage" : 25 }, { - "id": 441, - "damage": 37 + "id" : 373, + "damage" : 26 }, { - "id": 441, - "damage": 38 + "id" : 373, + "damage" : 27 }, { - "id": 441, - "damage": 39 + "id" : 373, + "damage" : 28 }, { - "id": 441, - "damage": 40 + "id" : 373, + "damage" : 29 }, { - "id": 441, - "damage": 41 + "id" : 373, + "damage" : 30 }, { - "id": 280 + "id" : 373, + "damage" : 31 }, { - "id": 355 + "id" : 373, + "damage" : 32 }, { - "id": 355, - "damage": 8 + "id" : 373, + "damage" : 33 }, { - "id": 355, - "damage": 7 + "id" : 373, + "damage" : 34 }, { - "id": 355, - "damage": 15 + "id" : 373, + "damage" : 35 }, { - "id": 355, - "damage": 12 + "id" : 373, + "damage" : 36 }, { - "id": 355, - "damage": 14 + "id" : 373, + "damage" : 37 }, { - "id": 355, - "damage": 1 + "id" : 373, + "damage" : 38 }, { - "id": 355, - "damage": 4 + "id" : 373, + "damage" : 39 }, { - "id": 355, - "damage": 5 + "id" : 373, + "damage" : 40 }, { - "id": 355, - "damage": 13 + "id" : 373, + "damage" : 41 }, { - "id": 355, - "damage": 9 + "id" : 438 }, { - "id": 355, - "damage": 3 + "id" : 438, + "damage" : 1 }, { - "id": 355, - "damage": 11 + "id" : 438, + "damage" : 2 }, { - "id": 355, - "damage": 10 + "id" : 438, + "damage" : 3 }, { - "id": 355, - "damage": 2 + "id" : 438, + "damage" : 4 }, { - "id": 355, - "damage": 6 + "id" : 438, + "damage" : 5 }, { - "id": 50 + "id" : 438, + "damage" : 6 }, { - "id": 58 + "id" : 438, + "damage" : 7 }, { - "id": 61 + "id" : 438, + "damage" : 8 }, { - "id": 379 + "id" : 438, + "damage" : 9 }, - { - "id": 145 + "id" : 438, + "damage" : 10 }, { - "id": 145, - "damage": 4 + "id" : 438, + "damage" : 11 }, { - "id": 145, - "damage": 8 + "id" : 438, + "damage" : 12 }, { - "id": 116 + "id" : 438, + "damage" : 13 }, { - "id": 47 + "id" : 438, + "damage" : 14 }, { - "id": 380 + "id" : 438, + "damage" : 15 }, { - "id": 54 + "id" : 438, + "damage" : 16 }, { - "id": 146 + "id" : 438, + "damage" : 17 }, { - "id": 130 + "id" : 438, + "damage" : 18 }, { - "id": 205 + "id" : 438, + "damage" : 19 }, { - "id": 218 + "id" : 438, + "damage" : 20 }, { - "id": 218, - "damage": 8 + "id" : 438, + "damage" : 21 }, { - "id": 218, - "damage": 7 + "id" : 438, + "damage" : 22 }, { - "id": 218, - "damage": 15 + "id" : 438, + "damage" : 23 }, { - "id": 218, - "damage": 12 + "id" : 438, + "damage" : 24 }, { - "id": 218, - "damage": 14 + "id" : 438, + "damage" : 25 }, { - "id": 218, - "damage": 1 + "id" : 438, + "damage" : 26 }, { - "id": 218, - "damage": 4 + "id" : 438, + "damage" : 27 }, { - "id": 218, - "damage": 5 + "id" : 438, + "damage" : 28 }, { - "id": 218, - "damage": 13 + "id" : 438, + "damage" : 29 }, { - "id": 218, - "damage": 9 + "id" : 438, + "damage" : 30 }, { - "id": 218, - "damage": 3 + "id" : 438, + "damage" : 31 }, { - "id": 218, - "damage": 11 + "id" : 438, + "damage" : 32 }, { - "id": 218, - "damage": 10 + "id" : 438, + "damage" : 33 }, { - "id": 218, - "damage": 2 + "id" : 438, + "damage" : 34 }, { - "id": 218, - "damage": 6 + "id" : 438, + "damage" : 35 }, { - "id": 425 + "id" : 438, + "damage" : 36 }, { - "id": 25 + "id" : 438, + "damage" : 37 }, { - "id": 84 + "id" : 438, + "damage" : 38 }, { - "id": 500 + "id" : 438, + "damage" : 39 }, { - "id": 501 + "id" : 438, + "damage" : 40 }, { - "id": 502 + "id" : 438, + "damage" : 41 }, { - "id": 503 + "id" : 441 }, { - "id": 504 + "id" : 441, + "damage" : 1 }, { - "id": 505 + "id" : 441, + "damage" : 2 }, { - "id": 506 + "id" : 441, + "damage" : 3 }, { - "id": 507 + "id" : 441, + "damage" : 4 }, { - "id": 508 + "id" : 441, + "damage" : 5 }, { - "id": 509 + "id" : 441, + "damage" : 6 }, { - "id": 510 + "id" : 441, + "damage" : 7 }, { - "id": 511 + "id" : 441, + "damage" : 8 }, { - "id": 348 + "id" : 441, + "damage" : 9 }, { - "id": 89 + "id" : 441, + "damage" : 10 }, { - "id": 123 + "id" : 441, + "damage" : 11 }, { - "id": 169 + "id" : 441, + "damage" : 12 }, { - "id": 323 + "id" : 441, + "damage" : 13 }, { - "id": 321 + "id" : 441, + "damage" : 14 }, { - "id": 389 + "id" : 441, + "damage" : 15 }, { - "id": 390 + "id" : 441, + "damage" : 16 }, { - "id": 281 + "id" : 441, + "damage" : 17 }, { - "id": 325 + "id" : 441, + "damage" : 18 }, { - "id": 325, - "damage": 1 + "id" : 441, + "damage" : 19 }, { - "id": 325, - "damage": 8 + "id" : 441, + "damage" : 20 }, { - "id": 325, - "damage": 10 + "id" : 441, + "damage" : 21 }, { - "id": 325, - "damage": 2 + "id" : 441, + "damage" : 22 }, { - "id": 325, - "damage": 3 + "id" : 441, + "damage" : 23 }, { - "id": 325, - "damage": 4 + "id" : 441, + "damage" : 24 }, { - "id": 325, - "damage": 5 + "id" : 441, + "damage" : 25 }, { - "id": 397, - "damage": 3 + "id" : 441, + "damage" : 26 }, { - "id": 397, - "damage": 2 + "id" : 441, + "damage" : 27 }, { - "id": 397, - "damage": 4 + "id" : 441, + "damage" : 28 }, { - "id": 397, - "damage": 5 + "id" : 441, + "damage" : 29 }, { - "id": 397 + "id" : 441, + "damage" : 30 }, { - "id": 397, - "damage": 1 + "id" : 441, + "damage" : 31 }, { - "id": 138 + "id" : 441, + "damage" : 32 }, { - "id": 245 + "id" : 441, + "damage" : 33 }, { - "id": 120 + "id" : 441, + "damage" : 34 }, { - "id": 263 + "id" : 441, + "damage" : 35 }, { - "id": 263, - "damage": 1 + "id" : 441, + "damage" : 36 }, { - "id": 264 + "id" : 441, + "damage" : 37 }, { - "id": 452 + "id" : 441, + "damage" : 38 }, { - "id": 265 + "id" : 441, + "damage" : 39 }, { - "id": 371 + "id" : 441, + "damage" : 40 }, { - "id": 266 + "id" : 441, + "damage" : 41 }, { - "id": 388 + "id" : 280 }, { - "id": 406 + "id" : 355 }, { - "id": 337 + "id" : 355, + "damage" : 8 }, { - "id": 336 + "id" : 355, + "damage" : 7 }, { - "id": 405 + "id" : 355, + "damage" : 15 }, { - "id": 409 + "id" : 355, + "damage" : 12 }, { - "id": 422 + "id" : 355, + "damage" : 14 }, { - "id": 465 + "id" : 355, + "damage" : 1 }, { - "id": 467 + "id" : 355, + "damage" : 4 }, { - "id": 468 + "id" : 355, + "damage" : 5 }, { - "id": 470 + "id" : 355, + "damage" : 13 }, { - "id": 287 + "id" : 355, + "damage" : 9 }, { - "id": 288 + "id" : 355, + "damage" : 3 }, { - "id": 318 + "id" : 355, + "damage" : 11 }, { - "id": 289 + "id" : 355, + "damage" : 10 }, { - "id": 334 + "id" : 355, + "damage" : 2 }, { - "id": 415 + "id" : 355, + "damage" : 6 }, { - "id": 414 + "id" : 50 }, { - "id": 385 + "id" : -156 }, { - "id": 369 + "id" : -208 }, { - "id": 377 + "id" : 58 }, - { - "id": 378 + "id" : -200 }, - { - "id": 376 + "id" : -201 }, { - "id": 437 + "id" : -202 }, { - "id": 445 + "id" : 720 }, { - "id": 370 + "id" : 61 }, { - "id": 341 + "id" : -196 }, { - "id": 368 + "id" : -198 }, { - "id": 381 + "id" : 379 }, { - "id": 399 + "id" : 145 }, { - "id": 208 + "id" : 145, + "damage" : 4 }, { - "id": 426 + "id" : 145, + "damage" : 8 }, { - "id": 339 + "id" : -195 }, { - "id": 340 + "id" : 116 }, { - "id": 386 + "id" : 47 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c01000000" + "id" : -194 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c02000000" + "id" : 380 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c03000000" + "id" : -213 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c04000000" + "id" : 54 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c01000000" + "id" : 146 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c02000000" + "id" : 130 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c03000000" + "id" : -203 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c04000000" + "id" : 205 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c01000000" + "id" : 218 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c02000000" + "id" : 218, + "damage" : 8 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c03000000" + "id" : 218, + "damage" : 7 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c04000000" + "id" : 218, + "damage" : 15 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c01000000" + "id" : 218, + "damage" : 12 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c02000000" + "id" : 218, + "damage" : 14 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c03000000" + "id" : 218, + "damage" : 1 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c04000000" + "id" : 218, + "damage" : 4 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c01000000" + "id" : 218, + "damage" : 5 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c02000000" + "id" : 218, + "damage" : 13 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c03000000" + "id" : 218, + "damage" : 9 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c04000000" + "id" : 218, + "damage" : 3 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696405000203006c766c01000000" + "id" : 218, + "damage" : 11 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696405000203006c766c02000000" + "id" : 218, + "damage" : 10 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696405000203006c766c03000000" + "id" : 218, + "damage" : 2 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696406000203006c766c01000000" + "id" : 218, + "damage" : 6 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696406000203006c766c02000000" + "id" : 425 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696406000203006c766c03000000" + "id" : 25 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696407000203006c766c01000000" + "id" : 84 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696407000203006c766c02000000" + "id" : 500 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696407000203006c766c03000000" + "id" : 501 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696408000203006c766c01000000" + "id" : 502 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c01000000" + "id" : 503 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c02000000" + "id" : 504 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c03000000" + "id" : 505 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c04000000" + "id" : 506 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c05000000" + "id" : 507 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c01000000" + "id" : 508 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c02000000" + "id" : 509 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c03000000" + "id" : 510 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c04000000" + "id" : 511 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c05000000" + "id" : 348 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c01000000" + "id" : 89 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c02000000" + "id" : 123 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c03000000" + "id" : 169 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c04000000" + "id" : 323 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c05000000" + "id" : 472 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640c000203006c766c01000000" + "id" : 473 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640c000203006c766c02000000" + "id" : 474 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640d000203006c766c01000000" + "id" : 475 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640d000203006c766c02000000" + "id" : 476 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640e000203006c766c01000000" + "id" : 321 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640e000203006c766c02000000" + "id" : 389 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640e000203006c766c03000000" + "id" : 390 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c01000000" + "id" : 281 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c02000000" + "id" : 325 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c03000000" + "id" : 325, + "damage" : 1 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c04000000" + "id" : 325, + "damage" : 8 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c05000000" + "id" : 325, + "damage" : 10 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696410000203006c766c01000000" + "id" : 325, + "damage" : 2 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696411000203006c766c01000000" + "id" : 325, + "damage" : 3 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696411000203006c766c02000000" + "id" : 325, + "damage" : 4 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696411000203006c766c03000000" + "id" : 325, + "damage" : 5 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696412000203006c766c01000000" + "id" : 397, + "damage" : 3 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696412000203006c766c02000000" + "id" : 397, + "damage" : 2 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696412000203006c766c03000000" + "id" : 397, + "damage" : 4 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c01000000" + "id" : 397, + "damage" : 5 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c02000000" + "id" : 397 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c03000000" + "id" : 397, + "damage" : 1 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c04000000" + "id" : 138 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c05000000" + "id" : -206 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696414000203006c766c01000000" + "id" : -157 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696414000203006c766c02000000" + "id" : -197 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696415000203006c766c01000000" + "id" : 120 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696416000203006c766c01000000" + "id" : 263 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696417000203006c766c01000000" + "id" : 263, + "damage" : 1 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696417000203006c766c02000000" + "id" : 264 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696417000203006c766c03000000" + "id" : 452 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696418000203006c766c01000000" + "id" : 265 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696418000203006c766c02000000" + "id" : 371 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696418000203006c766c03000000" + "id" : 266 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696419000203006c766c01000000" + "id" : 388 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696419000203006c766c02000000" + "id" : 406 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641a000203006c766c01000000" + "id" : 337 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641d000203006c766c01000000" + "id" : 336 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641d000203006c766c02000000" + "id" : 405 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641d000203006c766c03000000" + "id" : 409 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641d000203006c766c04000000" + "id" : 422 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641d000203006c766c05000000" + "id" : 465 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641f000203006c766c01000000" + "id" : 467 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641f000203006c766c02000000" + "id" : 468 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641f000203006c766c03000000" + "id" : 470 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641e000203006c766c01000000" + "id" : 287 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641e000203006c766c02000000" + "id" : 288 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a0100000002020069641e000203006c766c03000000" + "id" : 318 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696420000203006c766c01000000" + "id" : 289 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696421000203006c766c01000000" + "id" : 334 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696422000203006c766c01000000" + "id" : 415 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696422000203006c766c02000000" + "id" : 414 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696422000203006c766c03000000" + "id" : 385 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696422000203006c766c04000000" + "id" : 369 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696423000203006c766c01000000" + "id" : 377 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696423000203006c766c02000000" + "id" : 378 }, { - "id": 403, - "nbt_hex": "0a0000090400656e63680a01000000020200696423000203006c766c03000000" + "id" : 376 }, { - "id": 333 + "id" : 437 }, { - "id": 333, - "damage": 1 + "id" : 445 }, { - "id": 333, - "damage": 2 + "id" : 370 }, { - "id": 333, - "damage": 3 + "id" : 341 }, { - "id": 333, - "damage": 4 + "id" : 368 }, { - "id": 333, - "damage": 5 + "id" : 381 }, { - "id": 66 + "id" : 399 }, { - "id": 27 + "id" : 208 }, { - "id": 28 + "id" : 426 }, { - "id": 126 + "id" : 339 }, { - "id": 328 + "id" : 340 }, { - "id": 342 + "id" : 386 }, { - "id": 408 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAAAAAA=" }, { - "id": 407 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAAAAAA=" }, { - "id": 331 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAAAAAA=" }, { - "id": 152 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAAAAAA=" }, { - "id": 76 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAEAAAA=" }, { - "id": 69 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAEAAAA=" }, { - "id": 143, - "damage": 5 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAEAAAA=" }, { - "id": 77, - "damage": 5 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAEAAAA=" }, { - "id": 131 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAIAAAA=" }, { - "id": 72 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAIAAAA=" }, { - "id": 70 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAIAAAA=" }, { - "id": 147 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAIAAAA=" }, { - "id": 148 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAMAAAA=" }, { - "id": 251 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAMAAAA=" }, { - "id": 151 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAMAAAA=" }, { - "id": 356 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAMAAAA=" }, { - "id": 404 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAQAAAA=" }, { - "id": 410 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAQAAAA=" }, { - "id": 125, - "damage": 3 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAQAAAA=" }, { - "id": 23, - "damage": 3 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAQAAAA=" }, { - "id": 33, - "damage": 1 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAUAAAA=" }, { - "id": 29, - "damage": 1 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAUAAAA=" }, { - "id": 46 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAUAAAA=" }, { - "id": 421 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAYAAAA=" }, { - "id": 446 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAYAAAA=" }, { - "id": 446, - "damage": 8 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAYAAAA=" }, { - "id": 446, - "damage": 7 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAcAAAA=" }, { - "id": 446, - "damage": 15 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAcAAAA=" }, { - "id": 446, - "damage": 12 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAcAAAA=" }, { - "id": 446, - "damage": 14 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAgAAAA=" }, { - "id": 446, - "damage": 1 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAkAAAA=" }, { - "id": 446, - "damage": 4 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAkAAAA=" }, { - "id": 446, - "damage": 5 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAkAAAA=" }, { - "id": 446, - "damage": 13 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAkAAAA=" }, { - "id": 446, - "damage": 9 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZAkAAAA=" }, { - "id": 446, - "damage": 3 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAoAAAA=" }, { - "id": 446, - "damage": 11 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAoAAAA=" }, { - "id": 446, - "damage": 10 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAoAAAA=" }, { - "id": 446, - "damage": 2 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAoAAAA=" }, { - "id": 446, - "damage": 6 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZAoAAAA=" }, { - "id": 446, - "damage": 15, - "nbt_hex": "0a0000030400547970650100000000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAsAAAA=" }, { - "id": 434 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAsAAAA=" }, { - "id": 434, - "damage": 1 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZAsAAAA=" }, { - "id": 434, - "damage": 2 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZAsAAAA=" }, { - "id": 434, - "damage": 3 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZAsAAAA=" }, { - "id": 434, - "damage": 4 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZAwAAAA=" }, { - "id": 434, - "damage": 5 + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZAwAAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730000000000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZA0AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000000070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZA0AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000008070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZA4AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000007070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZA4AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000f070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZA4AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000c070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZA8AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000e070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZA8AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000001070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZA8AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000004070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZA8AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000005070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZA8AAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000d070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBAAAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000009070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBEAAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000003070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBEAAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000b070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBEAAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000a070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBIAAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000002070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBIAAAA=" }, { - "id": 401, - "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000006070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBIAAAA=" }, { - "id": 402, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000000070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72211d1dff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBMAAAA=" }, { - "id": 402, - "damage": 8, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000008070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72524f47ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBMAAAA=" }, { - "id": 402, - "damage": 7, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000007070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72979d9dff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBMAAAA=" }, { - "id": 402, - "damage": 15, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000f070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72f0f0f0ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZBMAAAA=" }, { - "id": 402, - "damage": 12, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000c070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72dab33aff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZBMAAAA=" }, { - "id": 402, - "damage": 14, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000e070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f721d80f9ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBQAAAA=" }, { - "id": 402, - "damage": 1, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000001070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72262eb0ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBQAAAA=" }, { - "id": 402, - "damage": 4, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000004070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72aa443cff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBUAAAA=" }, { - "id": 402, - "damage": 5, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000005070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72b83289ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBYAAAA=" }, { - "id": 402, - "damage": 13, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000d070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72bd4ec7ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBcAAAA=" }, { - "id": 402, - "damage": 9, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000009070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72aa8bf3ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBcAAAA=" }, { - "id": 402, - "damage": 3, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000003070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72325483ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBcAAAA=" }, { - "id": 402, - "damage": 11, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000b070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f723dd8feff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBgAAAA=" }, { - "id": 402, - "damage": 10, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000a070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f721fc780ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBgAAAA=" }, { - "id": 402, - "damage": 2, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000002070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72167c5eff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZBgAAAA=" }, { - "id": 402, - "damage": 6, - "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000006070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f729c9c16ff00" + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBkAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZBkAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZBoAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZB0AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZB0AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZB0AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZB0AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBQACAgBpZB0AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZB4AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZB4AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZB4AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZB8AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZB8AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZB8AAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCAAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCEAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCIAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZCIAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZCIAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsBAACAgBpZCIAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAQACAgBpZCMAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAgACAgBpZCMAAAA=" + }, + { + "id" : 403, + "nbt_b64" : "CgAACQQAZW5jaAoBAAAAAgMAbHZsAwACAgBpZCMAAAA=" + }, + { + "id" : 333 + }, + { + "id" : 333, + "damage" : 1 + }, + { + "id" : 333, + "damage" : 2 + }, + { + "id" : 333, + "damage" : 3 + }, + { + "id" : 333, + "damage" : 4 + }, + { + "id" : 333, + "damage" : 5 + }, + { + "id" : 66 + }, + { + "id" : 27 + }, + { + "id" : 28 + }, + { + "id" : 126 + }, + { + "id" : 328 + }, + { + "id" : 342 + }, + { + "id" : 408 + }, + { + "id" : 407 + }, + { + "id" : 331 + }, + { + "id" : 152 + }, + { + "id" : 76 + }, + { + "id" : 69 + }, + { + "id" : 143 + }, + { + "id" : -144 + }, + { + "id" : -141 + }, + { + "id" : -143 + }, + { + "id" : -140 + }, + { + "id" : -142 + }, + { + "id" : 77 + }, + { + "id" : 131 + }, + { + "id" : 72 + }, + { + "id" : -154 + }, + { + "id" : -151 + }, + { + "id" : -153 + }, + { + "id" : -150 + }, + { + "id" : -152 + }, + { + "id" : 70 + }, + { + "id" : 147 + }, + { + "id" : 148 + }, + { + "id" : 251 + }, + { + "id" : 151 + }, + { + "id" : 356 + }, + { + "id" : 404 + }, + { + "id" : 410 + }, + { + "id" : 125, + "damage" : 3 + }, + { + "id" : 23, + "damage" : 3 + }, + { + "id" : 33, + "damage" : 1 + }, + { + "id" : 29, + "damage" : 1 + }, + { + "id" : 46 + }, + { + "id" : 421 + }, + { + "id" : -204 + }, + { + "id" : 446 + }, + { + "id" : 446, + "damage" : 8 + }, + { + "id" : 446, + "damage" : 7 + }, + { + "id" : 446, + "damage" : 15 + }, + { + "id" : 446, + "damage" : 12 + }, + { + "id" : 446, + "damage" : 14 + }, + { + "id" : 446, + "damage" : 1 + }, + { + "id" : 446, + "damage" : 4 + }, + { + "id" : 446, + "damage" : 5 + }, + { + "id" : 446, + "damage" : 13 + }, + { + "id" : 446, + "damage" : 9 + }, + { + "id" : 446, + "damage" : 3 + }, + { + "id" : 446, + "damage" : 11 + }, + { + "id" : 446, + "damage" : 10 + }, + { + "id" : 446, + "damage" : 2 + }, + { + "id" : 446, + "damage" : 6 + }, + { + "id" : 446, + "damage" : 15, + "nbt_b64" : "CgAAAwQAVHlwZQEAAAAA" + }, + { + "id" : 434 + }, + { + "id" : 434, + "damage" : 1 + }, + { + "id" : 434, + "damage" : 2 + }, + { + "id" : 434, + "damage" : 3 + }, + { + "id" : 434, + "damage" : 4 + }, + { + "id" : 434, + "damage" : 5 + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMAAAAAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAADQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAACgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAAAgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 401, + "nbt_b64" : "CgAACgkARmlyZXdvcmtzAQYARmxpZ2h0AQkKAEV4cGxvc2lvbnMKAQAAAAcNAEZpcmV3b3JrQ29sb3IBAAAABgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAAA" + }, + { + "id" : 402, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IhHR3/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 8, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3JST0f/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 7, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3KXnZ3/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 15, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Lw8PD/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 12, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Laszr/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 14, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IdgPn/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 1, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3ImLrD/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 4, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3KqRDz/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABAEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 5, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3K4Mon/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 13, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3K9Tsf/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAADQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 9, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Kqi/P/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACQEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 3, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IyVIP/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 11, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3I92P7/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACwEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 10, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3Ifx4D/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAACgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 2, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3IWfF7/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAAAgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" + }, + { + "id" : 402, + "damage" : 6, + "nbt_b64" : "CgAAAwsAY3VzdG9tQ29sb3KcnBb/Cg0ARmlyZXdvcmtzSXRlbQcNAEZpcmV3b3JrQ29sb3IBAAAABgEMAEZpcmV3b3JrVHlwZQAHDABGaXJld29ya0ZhZGUAAAAAAQ0ARmlyZXdvcmtUcmFpbAABDwBGaXJld29ya0ZsaWNrZXIAAAA=" } ] -} +} \ No newline at end of file diff --git a/src/main/resources/entity_identifiers.dat b/src/main/resources/entity_identifiers.dat index 1ec483560126de664ed154a0f729fa1d59ee26ba..da80e47e66042e3d497eaa12478e5ddfebac7fc5 100644 GIT binary patch delta 180 zcmdn#aM_WUi-D6ZGbJapxPI3SbrK)Xc0$Ih0y*!iE<@-TeCZCX#ot&-UI5|{7jj4rY@vzxTPF|;zbdpZ??&NinPWI%XB2Xl;4Wh6GpgA0Q z@4fdvjvMOD1LlL^+4E`^x-kRFzG zXVap7HCCfSF@6&9YJl*8$Q+LP%#P7o1sZS0F z(BHYBQ=SYFAb)3yPJQx6i2lwao%&>#0DZAcC*kZcF9#5sECKPOz1!iQ#|h%(9!392 zg8rd<6#1tK^5h;x|5=Lup?ehlHxl#}_b7y}+;d(|YcoWY+;cQ~K3iPO7PIqBy=bMI zM_%csS5MjpJ!;0vS%zy z`8Y!MG0RdKj*vC;%fntq={G|5Da%sUjgWoDvXpEiWZz)f=NSz4oMm5RkbS|jly9xM zn0KCi?9Q_Ra*|&mNBK2CPVy?`D6a;{Nj`-f<Z$ggp zW`LaJOUO~a43LvN2|3D>0dkTbAxHT!Ku+=^Y+cB=13v@?L<3lKe&MQ;1ffbvFkDq~ zAyg?BhO0_Sges-Pa8=2SP^HWmt||!%9`P- zl02bG$unG4awt?OhlZ<4DupVg(r{JDs8FSh8m=md6{?h2!&N1}LY4BXwV>{sSD(7` zs=u1#Q>als^;eTT3N^~3{%Vpxp+@=BUrq8R)F^NIt4Y3u8s$rWHOZ4uqde)aCixL+ zlpp=oBrigZ@}j?*KT- zP@_EOuO|5oYF+tlWuAP>xY0bFYyMe%{`kx*!MWv*>vFoE%0r8qW<9Agk5ylnb7|cE z$8ShO=*MqiTzpy0r{k(F>F3YAem<+Kh5GXsUOzX_TIcG|U%G#8?QWP?m(uRD(X-}K z%%AWu?QHaPIh)UmdR)9aTaAux_xI*ak*BAqp7M7OL_7#onLG)*MU*(|5# z(d1q5-q@C!vbaF4*>|b8+@&QW@h;2^rD~HAx{Eb4sQ_hU-UXbYRFeXrM^W*lGL@0& zj3-sDj8IoRshDMCI^#+8EdV-d#q;QFCvBnMzKsd;yjadw>Kyg`?%!KmgLl8*?$4Lu;lB0nxD~7x zOlPZQxmdNfMZLT*J6g?lDYFf_oUCWnqS+XAG?R69OYt(1;L^~ z=3>e7L$D~ixmfaa5iBZAv8<#1cu{%E#Zux$Gv8b+C0;b+&BapUMWxr5W$IvP|Dy7n zi>1ViN^mZg5-%#lxmZfPs1(PtJo*=v<6JByUNm#f#Zux$GuB)zC0tZ&SI(9sI-uyF7rFMTT)(VW@Hla?< zI^y|ebwv^SG`WrRbL*LBShxRl(WKCXYD#~HCWR$bQ#v{{DI}qq(!-%iJv>xXx;8Ya zYlmt|pN1yI{iAp^yNml5*4JNL&F8a~c?v29Iv#bV{?BlGAVpvN0Dba9ioSRQ`s9ri zeentO$tNlL;u+|ZXHxXVKhP)tSZ@fJZH8C|Ivbr#s(Dq9PHwKOGt)lP?UY`n)5;uc zcUkUpA*9F@y;6^^&8bj}=CHp9+cW&2MF!~AdNQ9~UYUK3^NrFpig&Nv>1{bThqrLc zD73v&59c#;d?8&TJqhR+TFw@)ScsPS`-M!6m(44<=`;`O6*^tb=C8||+e8zcUa5zZ z+d>8eXcNt2dWBAxn|g_Z)*wX_m0qcbx|?Vo(kpawURO8D)zLhoU+Jz>|tLDJcUCx6n<mQWHoqNL=3RuV za{StuyHt82<%kcWe{WOn_}Was>Z-b?MDsb)ok_%ct-MJRPKmGRmar~$`!1rsJ>@HT z32fg+)CIn>Ti_$O6VqyA&T$>@_8qO}#kt&!W=m3YBJ#NT>n`a%+wH*F{nzH%{JL72 ze_QR|7}*E;s|c5JVJvt1?&-EM{ZPu3vCxOjJ30M4N};j5r`D0W{aDJcu~;+wFe{1u zKuWu@z^6M`d15Ic$6`-EwJGZLd;J$mX*!npY*Cv0RF6HclyZ19x1*QJd>y@%vV1Jp z*-I5D<^Ncy)=L#0jqhW59lcb+(rh3WtM*a_NRx(GU`H=iur$Mn#p=COA<|qVme|!x z6)p`3tZI&OnOsD_s=J5=1D*X+9(RI9)iao;bw~w7WiuGm(GwLO)yrU>+65I770O^z zN8eL;R3U?TdZ$xRR2qX(UA;{KQcVm8`V>R>pNe5H(AnE0nSe%BFqo$GHU&iGFBsI( z+Y}zvy4JHBZ&OfI;(}3Ky-fj9ZL{`sZ3nSt1>HVTeLv&- zYErC7!FnFOUf^>kN290ZyUTes-M>chJ~N50ZpIt){g!%rP;YAs>Q@&4O(~NAat{EY z0_X)WyKa21fC^s{K)C{nfwi`JURAGFSLOUd1}OU&ehsJQ6eCGR;wnj91(ePceYv%e>hChtT@pRB44J`V+L8X(1Jn-3Fd+B77J&^|VcO?*el ztkxrm3-hM1nB;hLY@R2YA%=`HzLwhzy&}@>AVOj96_ID#5TO9~ipT?Oh)^VZMdT?p zL?~pvA}9OkK!k>-y&`hLB1CA=*()LkD?)?@lD#5wo+3nOaM&v%$0$OC#(cdZa(W^} zXgt>|B8MhIga&E7B63zDL}>igD5b1{7ttOeo2LKv53Ngpy1N6lKa#lzG9;Ggg#OGoh4NQP$0bQes89ITK2W z74<7?3^BPf!zHWKckYDye0To|l$3i9N$Mwol5&qBN&O*EQtl-r$@hVhGG2fr`8!Zj zMh}prh+CUH%(K_sYU@pVkoQ9V#cFoBXugq&0pHhk2R=?&;tyqtf1I+|0Lo;8IAw7I zl*tWo%3=yAlPThq#T!tj92ci7!%ZktN{dsLF({O23>v2_15+r|z%))-MypV!(Q2Hs z3}K;6L)bWF8P`IY#C%di;AG%Su&ma#IFDL-3dWwW?* zU77R3wR5(=p5$Rtu-dj)(3c#)zFWiE=Cl1Gcynmjx-gquWPq?ANq>P`#HwC5=FAs+ zaB+HR_G`Ak%=isNh=$}|LQ+T|M1{mlNa_ZJsBU-(Ntu8Ul?g8)sR|IHs^BFgMF2un z1iXYK=R=5czL$`scnFc9tu@b!qP{ktyq<~;k4JA^SLXe_O+9qb7^5qegf3Y!MpqmPU2-9rszv8gg({66n&|Q(C?bI2m4ya(&c6dw9rQ}|$gnaD%G zYa$=4FVlJGcTMMm^<`2I{jN!Uu)a*~q2D#N57w6nKJ>dL_`&)z&4)haf2*)GuSl5F z<)&3tOZGn+omJ-a?(L(jqmg;KWxg!3+caK7pMRa+yuo7RHOZypyY)pFHjjDxCx3EsL~JI z1jP$97#O$-iWgo!8JXAq%|79?(ZfYyPPg!Hf|3`^pAPbZ^H5=wsWVSo1l2%eeq3D@x)JvTw|0MDbzx{y+^zlE z&B^dq)RoT_j7+Q_n3Iye$Q|<#J&8B;DBdA@5^LyDtV8r9&d{Schv-R+p+_+e(UbT> zk5)-S^kkhMdbH#bq9^P6(4+N>5ItGPhaN3Pgy_k-J@lx5SVs@toivd8D!~`KJ5M&4 z^en)rXJvy)w*!p2T{f8XNx-O2hQs<^DFBQ*Y&Mt@GwRLRU`ouWt7n5LF{6HFy)S!X zUQ0fFw%z%1!8ddFs0dk!17xZ1M99h=hb;Ay2wAzekfr_*AuFW{vgG^-S*ci%B_~+h z0OzGS%T$IW_A{=t(TnZBwDvie6)W>DSMz%C*nW}so4d^sOKQD?41ID*w7%YcvP-Of z`)y=g02wD5(0U~q0J2al;Q3}b-ZI9m7pp;{%o$61W=I{LqNEwidAW6O=QYvT$%kdW zY98KGXjBzTa`Y5>eiN;)_Y`_K6RYp)DfH+i8sO+D^k5|x(A!h!`AICPr>D?El30#M zPobLfah$!m8^tf{a<$sj<#;(S)}@q~<5AbUY~R9NN|FFF;sAhZNfJQD9sp1=Ndm|y z1OO@~NdOs-06@hg2_PdA0H}r}0c3Iw02PlUfXuc5po)oXGaUd_8j=9An+^b~2iDYOdzkYu zd+ENVyIu_10I4Tv0M(O08zA)t4WN26Xal4kp#fBnd>f!V`{HNfBiDXPG=S>Wpbbzw zK=o|U1}Gk&dN*hT6c126^lgCGvv3a&P`wiM7zP&`2Oe$WOe9-!TFz70_F0M`QNo*2{u+AB9`0~8O?PPsuF zpm>1x$qm{7#RIfU&bI+d9*FV)?U5U_0g4A`huokIP&`2U;|6Vj;sM$n=i2}!4{$B; z$OE)DZqNoO9-y6ZgEm0%0PTw#v;m3-XjhzX1C%@vEK2(o$%0?jtl2(o+(0!=5=2(o?*0@Vj=Ym}UQshzs^t)&2v#D`I0 znqyu%WxkDD##$=hELuXb=k9E#@0FbD7WgyvG13;o8mQHGK5&(*-Sq4;V zy8uvB*3wb-(f2V%Tm2gWQ(FX!{Hh@>vq3bG|%%V=?N9>gg#MvIHhAWms8T3kE@amsno;$k3(Q-X^Y7q>v1)+wUJWr+gf zv_ugtE~^s|r#u`YZqje3&nf9fiz_~-3>z)3_?*&dw7BAP%AL{Tiq9!gMvE&xr}c+u zamDAf^bjqs_?+^&_0htabtdrst=#V*$(nv?y%pJ10vSQ?fBL2FpIR>+Pm0NUgRdy@ z*WJFO$k5V(hm3R>$WUSMkddwd87cuDGSUekL&@GlM(hq5O6wjnVrEzlx+Os8E{fSCK3Q70OcmDw3q2LP@G$MRF8WC`a|HNQ#0ArKo-t z$xu+C4Ark92?{Edp!!uLKS71^Q@@I&C#X<*>Q|BM1Qp6o{VI~2phC&1Uqx~gR46y~ zt4L~s3gs8;xc^UXUX|z7yOLq{1F7#Kd4+y$wc(;%SmU>|(c@;5!FYYUEJw%YbCr9s z?Yl3=_faf%em6Lt_;?tSC|p%tl;)_ z<+2XFvuRPk8mj@J=vv-bZmyTFW>}_ny&CY{+~M_ClUf8d3a-DJ)Fh};Z2i@wHbIT< zn>ArtlQ-MSw~7R;y+Y<)aC2~k{k5OH6~4RI?=R=o&Gtb9@xO=r_tvmy zX+FeV%;m>tqo=#SjEi??tI^?i(Z8o?Qt$TGJbHJ3g;1mL`>VajYIMJ?gGHOi&Aw*; zUhb<6QxwBPk*+vQQA`g-GJTk$7$1ssAFV0*qWslK=S z9{Q`v{ev3aKYum3cTl5y=dULB4Qf;h{MBSo3pFYP{%SI~g&GZR{nccU3pH}cN3la> zvshL)+ok%tT$(TAS&ADS7G;(l0ni4m1cs`mEH=5?0mtVL4SXvX=6T`5;vM&=A$WbqiJY9 zni4mfuEx`j`{PE_+I%!6ZZ!SPM^oZP)8u?KC2myTtR0Yfs@wN@pM*~ll81x*b4>UPiVBX zl#eE%2aQT$KAHp{G%AMqXcB(Vs2qCIcCU$s#*GSMKAI9YDv9}MO5CU@=A$Wbqq68p z+r8r5ikovF1D8-?%tuq=Mx`+yO^F*7$9yy;Zd4vUX-a?dh#M8id^9C)R3h`yl(oLY|G@vHm>Xz38!h6ne7#zelE)HQ$^l+%+wlU^YOX zo1*RDP&JF^?5^s(o=xQ8F0O~_STyI&>uR;Sy}O4F8r89A+S`-nS)JKF-g$LY$)b7h zn_SIS<-89_2DBbAZd@7(RZaPgCEkXV}R7B2nh zE+Q;W9t#&g+uwdxCr^c|pRTT}YAt&UaK|Z})~k)@bu$va-mI?bs=D5N?^ptRJbLSD zW4?Bbe?42T>{0T>9o1BZx#M zGUSmS7&OvNWyr2JFlc<6%8)&8V9=;Gl_5Lgz@V{gDns_mfk7kKREF%P1B1q`sSMd` z2L_E+QyH=o4-6WkrZQxo9vCz-O=ZX~J}{_$SPwaxci&JRn$PF@SC6T?dZZDgc7Q;& zBaI-H0|crZX#}YoAW+>%BS_T%fvQFtL23pFR5Q{DQZYcFijhW;dI18}i!_2%3lOMU zq!FZ6fIzh(jUbf*1gaEi1gR4sP@PC4NR_CsoPKcfq8R$`w3DJ|10==#-aJ_YKzx!rx_gNjPM@_s3HS(Un znm7+?FMvn7W6Td-?{ATUDeLb_1?eXaC z=AZGAz~-rGYYpg=ZrcTmiu)i+?h6(b6GD_s7%VDYgeZA2SX3+tQL<#Ps5ld%R?eBLqn9t(7~cIY=$Ten}bDVqzq9S zDF=(n;25GbI1Uz-@i0VbJRB@4LtuzfKDDy*_Vu{&rTvk-qtVmZV!ZuIZ?g%-{w%9& zKkX}bn$5+M>NQokl)blbUC6+)^|BFTT^+F)27iLB(v82A38L7mQdSqs#5=-ir z)&|e(+etZ}ZLs#gU+!N>AETdJPe5*(cg^RsIiJH-xqolvpxv1sjgJ+h}c!JSkD%h<8@)SvCB={r^U70Dw6pC(&Bkt-ZUB}BQB<* zaWwj1>tBpl^J+bwY+pOm|6S4X@6NaXZr`qrw2M;bQ0+3SDBAV7+C_F4s$FLF)Y&ev zDef0pYp8a0-7lqGl%`YK9L?Dd4m}76;b_Koa42nC4*|-> zbbN4N=H9S3l8#BBHT>NRj4$fq8pB`nSulu?%iD50_;fv*epg^L7|jNgiUt_v|7-0_aV{S*IH`ev+@>0I8wZGyYC=|*z!4xxw?xj$g38p;opiq7ZrW|`vD6s@n z9(qtHqXbhf%u3Km|qlE)GQwR8W-k;!xy31x1-J4n+=BP?Y}SP~<=bMLE#=3d^LLRE3rL_D`Bz zGsPe5PIuv|(pIQaW(!x9c?47`iG{1mgaN9QuliM;vpJ|zY6@3X?ki2(!c~>~O7pXD zRpq`?#_U&h9@mQdO35)?Rk^Q}2g6mB`${PSGg65q)k|v!KvOTj%+yPFseEI#S(#T-r+2l|^wH^T zRn*t`=Tl>^Q{DB))7AIgeC6t?2K(db>Lc(}kNxp<^@lgVX--eSr`qg~r>l>^ zQ=Rt5)72l|e2=TAn(dFLtB=4_{r1Px)%V?e#ragr{qc155qPTW{&>3jzMHRHJ=J)B zJY9VRp6b0d#MrO9=*JBHNGaM~8oLk8r@v?aql8AwF;((eCb+4JJ_o zjCxx(m;?TKCy5;MT4uZ6? z=^gFIxI0=tnnVpW>S_6C5;oANtL39f+(4tg7D4OlY@ktR%SThm(YrbtX3%IEBp*%jB&~YnqbZ)GMUH$l z#gnwAk&mW$l9n*?(G*Woo_vY-S)L#9VD~A!r`CVGr~G)K&?G&Qa`)lFP8OxsNY(HC=1`Fb+pQ(I8cL_Jgz2oR zRRX2ZNWz(^k>yH7P~MD0JeF;7e4}V|63IAeeWMbApz%ng{!{BzI=%|Z`H>X&S2uA* zl<^~3&+J32_>w5qM`9i~Fa0@!LkT{T@nl)gR@X&s4(R6#p}Zamd87HnSBH}+qepUH zHk|I*P)4@ik-V5!H)iMBvckT#qtTPia%zrWYaN~Q6WpmcKu!h;kfYXFuOv;1x~|sv z2#)&sn4jownWvPr3R0BwJ*8w^0x3%Fo>DUCfD~nMPbnEuK#H=pr<4pAAVo>pQ%b&u z04d79o>KA+1W1vutmhua?&cM=vd36I$!#oOEpZmK$XUKxVl8NqwS2Y2ThJnJ`D%%| zphf2L)e?6>i`?a_CH8_A*~?c;`~@xYmo+56sj6vnaQbGww#Lflc!=F!#>=`~t?u@$ z^vvCUvfG4Nf#OsE$*Ea^VrKx!&RK!tc>u}tS%K0q0HltQ6)3$2KpJYs0sB@M0HpC| zR-oci8hK^~DlVmAXjY)&QW}zG1u8D3v1wMI;!?_`)~i>mSDX1<7785M((I{Um;?09 z85ylX>rZhf&p?9N`Orh4Y#B%}Z{_w7C_M%e%*(hv1j>hj1oIAV4}lV3Ai=zP+e4s? z7f3LBZft^N9C(RRTp;1VOA3KFH$lL4c|rYq07uIAb6vJQw#SE7wU zC^6~Ev@r-J9$l$62BE~FE7!UZr{{I`s$BFZsjf5|Ca&Cn+8pa)R=wrQou{p@(c;QI zr`li*lGk-%4r`l+y%7A<++`tBTJATbsUk#5%iV@F6@y4=xz~`UIuI!>a~()i?vIp~ zSq-GgA)kZCsM{JfHXr^nrOFs(yCAeNZwfEx_6IMIM=#_I!qx23Y(cRPjXWe-Qw52J8+k~w;tCRtIP#F> zQ^z3Dz#|VyzJCl7jX&~`1TM=rv#GJCy=`8Qvylq6G&b4DS;&Q38b$2lt7aD z1X5RhN+3yn0;#J$C6J^(fz(x>5=c^?K zq1zzU4sD=nH*_1M4x$ZI2h-YM9>awDfhy$CZBYC`wR7k;D1M--I&>QpKTy3*YlC_4 zci;!J>*29_b{{-Rm3ZhjD1M+CJ#-rsKT!1^x($jSsII5Af%AihAE@FF-3G-EG!_`T z4T>LVlrVG~6hF}T!TPrIkfVp60pb^8cEl_+wuY3KCnL9HZ5*HpiXr?UR`1Xb*kC%>T=(qPW3omUDmLnPPH~(UDmCkPIWY1 zUDm3hPBk)KUDl_ePW3NdUDl+bPPHvwUDlzYPIW6@UDlqVPBkfBUDlhSPW2{UUDlYP zPPHUnUDlPMPIV$)UDlGJPBkE2UDl7GPWj#X+LLUn(a#?IIg-Xn#CqHHQ=6h*zo&jS z;^(>rtiu$pJiqDrWL;f0r$IGq*85kMf1VKNEQC;k8N+lFsM>@Q%!c%C0#%$)g4vMX zO`xh1N-!JJy9rczLJ4L=dN+YGcqqZlS}cN$E8r!{;-Q4LmlOhJ@=!wCOA3Lqc_^Xn zC51p4J(SS)l0u-Y9!h9?Ng+^Tvrf9ZOKk1jcs6=ExtguY`R;s{N7M46*v!}F&&%0- zUex2_-Pvk%bh}SbKi{380#Ro>ZaMW}QK|}Loy^U#5GxN5WvoEZ>FTj@R%3OgY zSESzMNKpz4L^&g+uxKb9$kHRFfM`@52y#S9AyIw~B)KB>9!H82bs)+aDTPHTJCLPE zN&!*k4g@(OrI0B5S=+wvvS0Ey`F?>r0p_DgW(SQ@U_P3x6@W&0Fdt3U4nU(^7(r`Z zW;Z+U>#2ET*nDloe2}_Y*!${1qjZ>$ro@dBVm_J@H_D3nXiD6uzgeq&(`s{WCO3D7 zW~}DLxwVt$+1k9*y_#3M4LfI}$IXAX8-W~G#jAZ{{DtnmmVtD-JNKPo)YUS;PPRvE zGmAP}2G;5Bm|=!dKg$6tCgyvH1;ePDWq_Sci~7}AbqH1IDBO{U7pX{RAUVBARXPLA z7*! zr|z0_IgDoWJ!fcB#tPSd)SURSSj&(G;*_bv#h>gRK%dVyO5-UPkbTylslP~yAQ{oKQemJ=; z%|WCl~0$OdO263NI5cG`=Rb?%8TL3=49vUM!6xB3?rm3>#|g&=^1Ib^s#v! z$w8#Yqv66Q=ksEs^cTw6)-ZRUvoGhxYBgRKi?T0a{}Ok?4%9u!))1y_9VmR5s-aA& zI#5~3(-5aT9VmW~q#;a6I#9Tkp&?5dI#kw9&yc0`94c$)X2?=*4wOAe%n+u;94Oq% z%8;e394LE`k|9hfIZ(Kjk0DF>I8e5ojG;@(I8?WliJ?oGI8fJ4!%(F(9H`pP!O*1~ zY#nwzH}BtWy*o)!F3cZSYM zrC9>Fl)yu{&8`CLPax`*osax~nY)wvBBa?K5b~%mzU)OUZsEV@|U|iQ9hbHy#ZxL@J!aXEPsJMgH_ z<>X29z@uK5lPB>5kNRB{udnBUM?EhmPl+G(y_`HHe$@MN@|5^d|FaIDHOEw6mr~M4 zrVllbCw`^$CHk$EDQ0!G7&nK^POH|jH)qwd`Rt4-|K`KBo8@XYEyq){9NKx<&94$9 zy-1K5DGmUVdT?f>m;gxX#F>%e10bm%hak=TC?b~Bl`|uiSW<7!j8tMt9Xc~oi6zZ8 zLXb+obiOVO`_SwoGg65qtyE@4DzT(B%*;q7mNerEK`Q;yBbGGj%8XQENvoonkxDFS z$uu)ki6zxb>qR|$@KXxz+35ZF>+Y-X?e|*t75P`Y%VQ+rVKek*c`AjG^5%H_+2qxV z0bO@!RTf`TEY@YckUO*&_%(6^NZ@TuC$5Uy-CdO5ACI0)OLMUK#GGhu9&V1@OA^0U z2s8o-BAB&9$Gb=%&|MB9Jn$gU_#%jK>_MR6L=b`EAWLevOEh{2B2XL@0u2^|2owi} zKx2a-0>wcg&`=ILBxS7e-MG?AN(7kcZa6rUi&mvhff*+^7IGN-Q2~%H{+JQXcUTJlK!`F-jR8Vy#UGkfPk+ zBV}(qfD|PGA1QmY0i>w6dq^E@Fn}0!Y!9*4rUFP&5B88c*hl~|>Z~4Ot<3|FqQSL? zRC~h!v}n}qqt)6Z04*90duZ7k1E55sTo0x8W&mhW4zotg`sUgF`0Uq9?jj9q1UadS z`N05fFYp_@1eRtWnc2TPoA9F5Z*&V-FL2(y=4HNcvp_!Hu5Fl|4R<5(-IJ>~?f-N) zDu2A%EOy_j-?#cV36utnxuB0$xAkmsSum0kdoJXO$vrC>yW<)u_vZpWyf9;azCbDs z*?4uel0BukKB^wMc#oDvSuOC^8IY9E!;xly73fvU`?;WQuTp);g>-q9YDg}i!>d$i zvhj4UQZdTKb9N}7G#^MK zJmQ~Io$p5f5yu2{);+vwOo`CUN++`t3SgHYpsTxEHOA&xD z6@e&W$^8(f+#e+@DIda=@}q<$(?ghC@mW}}rvq-svxRv_%`DYh3;11MAN{Rvk7U4z zV}K#YWWb1jfFb{6z=)fGAva~fh_`?tZ)L!U(}1BdMFxxvEP$bbMFxzFE`XuYMFxxv zF@T{VMh1+GGk~FSMh1)wHh`hQMh1+GIDnxMM+S@xJAk2KM+S_HJ%FLHM+S@xK!BkE zNCu3ILV%%qW36GD7hK0HGf`cwtJeFZFPcA^J*eid`t$eS=B_g_@=veK$zRpw{C0UI z%WObUjfp`VPd4V5-rasaNK;*jk-j_Nj@9YbTi@Gh-TL(HzQ5faknB(y1^`MS$_|x5 z0iYDT>`)mZ07^rIcxc}iU_fcWkR7VTl!g!4p-N0?Fp(Xq#FYA}^)Bh%Q;%IPY>vUR z-z0kzf8OTF@#l@+XmxD!I!^1TynT-N9q#U$z}`MU+|>P0>aGdUm)4Fgv$A=4)sLr+ zn}B~|;hQa7{dDTV33U77w>!N0@zj?S@NZsMtJUpzQk|Q#8n<5T=To;%;J@4*my9t) zKb|^x3cl%Ui*kL{JgRK=*Y)FR7LkDO?CO$G(bZ{ck-*lwx&oz%Mgr7!yVlheJWW6n z@I75!q0_u1fo@;CW;O*+^OXdAZ&z3NG?7W*_jPpzPZOLJd{0+b@Kn{U6+G1KEam)n zy32VCRPWsKkrB|Sj>ph!V{5%y;ZgyQ;r4WA1xuAX25Vog){hk~mFyU9ZwFSuRI6ja zeLYvsOZ1iKr~cK=m#wSqU_%$1yb9!&;YBxKxN^xIJA~!BPc| z!P=Lrby-xO=?{e4J%t*OPAgR!1M#@SEkW^zcBV`=~NUE=iNave( zKvHSVj8tMtMKv>0i6zz3%t$4cR7xX|=4Hx|eo2KiGg65qRnyE!C6-i3Gb5E)QV~r= zdh|=GpP7+LEU9v4Mk=wSqL~?~#FFaehq3)*`;C!I_K8WFpepDS@OSnHj0XlB#58q!LRilbMl9EU8XLAiWADVo8NE zGg65qRm#jrC6-hwGb5E)Qmsrxdh|;wmYI=CEU8*%Mk=wSa+w*a#FFae$1tCi(?s*E z!2Vu$#mo+scmhf_Gdolw3Mf_0>`;j*pj0=Lq0Sx(C{@nvP$i~RJF`QTm{Rr34pm}G z^)m|URY4I`s-W4SN=&JSW``;GD)Xfo8JpHd~w4pm}GwKO|ai78dn>`*19 zR8OrEes>yvo@)C0+|@KQQsN0D)zZvJi6)R#Ni!oQmOxS+O+-3-Cy-P{Gb5E)QVq?F zRANaLG&53(CDqReq*wJsEU9{CMk=wS+L;-t#F8p!W~35Js+);OkA6v2Gc!_&CDqK# zNF|n3F*75TSW>3*gx~KjnnQ3Us^C)P9D*yM1((X_5L}5bxKu-j;L1Dl;8GzSf-5i0gG+TLiEA>G zr=O|V48c|WOciGcuHt7ZJ40|4KU3`)f~)wM3eXT-#m|(Vtx446%6tbtTW5plk#vhvo8mONjW$-Q}He3-`q^a zx0HKxGZo)b-p$Qad`meuH&gK~y68<^(KP2LG zG;fUf!SU))r$ij1et6zG0PU|W$;`Z6`kf!yM80q$5@(`z-Zk;tWxhk(`6SD?#Wfn|iePr*5 zKP;)6H0-y$mi5NGXVv}n!S<={<^%T4e?Go4Ycyr&_Xz)pG=Zwez)g@b6PiFJX5c2s z5DHD8GBj`#WJHA~P@VE`f>{VRUoTAe0u{4?o1l1sYTm$2P`p5;ao{E>UZ4u<--O-Q z4_jU^_6W}lRAvWmg5m|L#sfD&@dB0Xft#RsfhxOy6O_CV%tu&L?juQ*YjL6BIAJb$xkrzI6&rMLg@Zo!I zg5rgb+;bBYFMKq<2})jw^1{dNxe1CFK7P+lP`vPodv1c_1zKOQK32DTJAIhtg+Js zEDYNM#Rs&oFl-AHAJDpjcMH6R2_8P6Wrbl|p!k4R6^3nr;saV#7`6q94`@xny9G)< z;30yC4`@kY*cK>0pcRE-TcG%W78HhUf#L&NPw;Mmk`K5pIHwJ$3$&atYzq`0&}zc4 zEl_+wiwVQFK=A>sC3v?$$p=wBprwRiTcG%WRuYD7f#L&NNEo&SiVtWV!Mg=YKH$3G zkq>AYVb~TZKA=^EVOya1fEE#kZGqwgT0`(|fszlRd_YSG!?r;20j(em+XBT0w16;d z3ltyF`hoSl;qLUFVHObnn0oy1vG({5C(37cmYkIH3a`AC`o|_ z3C>9anm~&P12;kO0<9tp+yuo7w2Uxt6BIAdI)Z-_yaoy$UZ919ft#RsfmRX*Zi3f`=DqF=5~)C|;n|gn^r&c!8D^25y4l1zJz=Z-SB+xJEc9 z5U3HfpfGR~6fe+Xyg*9|12;kO0<9_dH$lk@QC^@$g@K!(c!5?G25y4l1zJ`Z zxCx3EXkEd-2})kz8sU)_XklUCCMaH@m4$(upm>3n76xvD;ssh;@Na^W7oxmCiwgrc zLGc2uE)3iR#S65&FmMwTFLbRh+?~!e%=*F~_gY^Vq6y*!G@)yKVTdM(7tn;R^@Smt zAYMQdy4DwZnxNzb9xpiO5oki!`oa)RP`uE!zA!`+6fbnGFAUKH#S2~Q3q4Kn8ZUTw zp=*6%h$bjr=vrSGq6vx@y4DwlXoBK}uJwhUCMbD<#|s`_=vrSGq6vx@y4DwlXoBK} zuJwf>nxJ^0Yki@o2})kz8sVHrphk49FAUKH#S2~Q3qv$P@j}=7!VpbRywJ72(9;AZ zFGP8vYkgseCMaI$T3;BV35plG))$6og5rg)^@W}$D0zWvghyWJT3;BV35plG))$6o zg5rg)^@Smtpm?EceW9lbN?wTaLf87j5KU0L(6zoWL=zM*bgeH8(FDZ{w7y`S(I8(_ z7-ogxPqLh%Hx zK@8pq#S^p&F?b^sPtZCQiYI6-V(>;No}kr;!5g7?g4QEK8lmI~ zt{onEf>tC3Z-n9rT9X*O5sD{hRbuc)D4w8oiI7Gpc_PXav@$VxBNR{2+Qi_EP&`4a z6N5KG@dT|;SZ{ckvsG8C@v>N7(E*ZwQq~qo-~~R{aZ#HSqU5*wmkUmgMsLm5<@I>` zACIq#<@VUf{YjNyE+9H4G7$987eoscfuLhw5G_Uof*$ySXu%;6q{m456=Fn-3V|R! zMhb`)4gx`Xj1&+p4g`Yq7%3p?M^-kNTvhdEHNKcvH)U=0r{m(%9P-=z@4@K7Y&!Z= z?#|+`CT)Wn^$348$$L;E+xx3Y0)rZP+h0u%c!wGp*k4VKZigDh^d02J<)$|O@8J7I zXQQW+tJ$iYkJq=$a`b3gUKE@8+WdJro6n1ST)aD5jgD^j0sqtPfCr+^cHDC6!6G{Z zvQFl+MY-|-ky8Rer>pC#T3>mP$VGu9SESzMNRhRh|**r$Pp=p zL<^;XBv+)~<4DnRXducNDTPI=o`EbqQVNJxHUmM9NGT-Beji5mn^g0vE~n#lRZp(Q z^`#WGqtWB-zuOK1&qnX;{)3BR(tPv{CpHN8%$N~*vZ^-gE51z1kvW-*s@9m4EHg5X*VU4*lJZL^@hHZ%lxi|k9j>K}lM|`C zmU2%pIyY~yfkz}N;<|FKOt)!|zIu}uV0cdh@}CLHN+$^TIW*WJ=+RR^-W;LyPSF-JXX#eLWeUgy%tfOqcC0@@auP{Q|3;YGQ zfc3uWMP04drYW;YDNQ&Y9j?&+MbVudwwf;-)8y74pcMzg<2ZKfBRzj3+Ww5B+Q;1Tf3Ko@RZ-`PU3Ko^sZ-`R# zEhjb6zbw|}O>rv@*!Q=;ETJX^>*33Fad}QG?_iiLup=~WnlVxb^tP8Eir zSSSb@l7=BD7P1bFSkU-03_-C_5H#KlLr^Rf1dT7l5EKgqLFx8`NSUj(`4;GUQK|EBz;v6K2!}w%89;;t&dYfj}oA_p8Y{; z=ux)w*0VoC4LwS2zIq2Ao`xc2GGE2k$EKl2Y0Fpd-~-c8q%7sD*!rk6^k|0btJnUJ zGz4k->Mhv%cr*m*)dXKb`-9Ptqn8YP<=P*Kh9Kp{&p=Xdo(#;(@#VaDuXz?*)^#zd z)@ux_^(}j{-{^0<6Jr|A@#$ALomHJu_8J5VU+rc-352MT%2 z`pWsXvc_BN@3<|N3=xL`LJmuYh`|6MgC#@6Ux1Lmk|AO*K*(On5OEhEfhlFyylg7%>_!WV8$zaT+k>w9kgq=CjMIW<_$7 zkkS6W+iZDA;x~}UZ+S>!IgrS5c}U_qkjQm;NMbyY$ar~3;ysYadsZ&JE$8O-g7_r& z58S>>r-VKCoCO9sE0rO>0;9`U&3ayZ=K4oozKUXqufXW?RTM*f z1xA;zq8Q>UFuHsd#SmYC(dDZshWHAME?+h4y79jH$6mgQVu-K6=<-z*Lwp5Bm#?B2 z;wvz^d=*h72=#u!)-Ns6Wh_e78XC*_#T7ZzXk|E+PK*(Fk5HS}ZWUge0xC@Xj zcWocu$Ghuac)2SMBJKjD%Uy90aTg$6?uvtmy8!8OR~$s#1xS~>;vnKKK*(L6z`9nm z4>&q={Y$sE5)tAoAjnyX2=NsV1q6925h0EOf*fTh#d$GFSkn4e zZcimc#8rThtCAt&D?rFs$q;cCAmprMhViL+yw}^>n-G~Nwt{N<+_yD4ICR` z|FzpjUTO!sU?E3_@sc~-0t-FzhL@hQ{}qzt1TV>h&99K7n0v{!cD_Q4cE0+FHA|)= z6Vm3WFo@B%S3j|4os7k3&#RZ%!G>3~jW)b`$+dR7LX391dWjuub%h*lb@h^K?Q?|~ z?Q``KYj1Le8f|j*Q`?S}IHt72)l1FZ-insd_Es;g_TE;gQI50TC)nq>FRgB-)}eWh zO!sfxnJz%{Am2fh@?C)FVb+5xWxW7ZCHFy?a$kV(K?Z~)13YE$0r4{10iN=F3jW%> zs$9>C`Pi(6Y$l~g?5QFQh*x4y)nh=s5__sN1LBp~Q&md9zg^DDYqLjsYzAGs*Fk-{ zK9#UxTc9|AD&DXyP)tDOao83pKA>uv(1OSF;(S*s`V&7@+#$G1%&965!BrwnwR;Gz z5?`wC*7+5tQkV5jlec#Vv0Cdvz6Y=VJ9i~d1GP?e0)+~8BE>$;2^6Z!X_WT)O~6q7 zO~ka0YyyVrY#PQsr3n!7nRTx4-TC#)d9hfF$My>i|6UwK64nuZ&FPhk-D8#&j%aes z=Kq68(wQj!k_VG{Xp#l}H4o?V5GBX?iz-uj=#s_!bq{9p&?GMkffo4zob2Xhaims`~}<7cSussd}&SJ&DVEoyv)B(xc^bIW7m(D)%3k; zPTBfDxqoae<(`-2x;CF)ke?rqPA|;{pXP|~=6}0q5dT@UC{6on$p8;pl%9RHWY7jJ z%F4c4G7y6nWny0~d4dQn%D}!_@}Lk}lzV-(PC1U0b$zuSu@;%rJ_%!Oj!c_u)@I}? z7TYWQ7jYFSSVQhh^Rk=yq(F6Uj=-K33sU*7ZWXK3?hb!5Yh4qw*hL!u&8=Zw%WQE` zjW^4w8HSU>e|IZb!z)YS^GM=wENio?T^isx zJLJfmGS_^@$c*LAXXaxHM!b0*`M(6xl?yx}=EHP7kgg=*2{B)G>Vb4+4^N2sPE`-2 zE3J4!%vZO1AYJ*!6Jow0)&rpdR22t;tGcxxJ!Z1AS@hm;4TG1qq1}Vb?x|^KA6KgMW4RU9Q>Ubuq^YcmLa+ysVOQASH>? zMR{mSLb1<}Kv3#RL9B|`B_k+tr6A^2F=Yg$tpvpBq^cLCoCkBg$ zA!AtwGhP+-c4w%yWU&|cKjIk@u+F#M3Iwfv{9n)humA#Ka$unF;VKA}$#{Xv z%2Ei#$#Q|>2kRjaCYuEcw-!YpO9S&zS$ky!vNT2ym9>{gAWPLdQ1)Pr1j1CY1BF`) zC6J{W9VmOSS^{CJ%YnkJB@@U}B@UErubV)Zif^cHYw-lSw0IJzYpYH1GfE&F);ZJp`7Vq(7ASKJ%#Y+BT>##Qm^fap9V`o69( zTLe|Z40n0Q9``bzTU2+_#bvvfF1HNW|J43KSGYf!JKde1+$=8Vhy9&++3x76%A@;v z-;T+4Kb7u47nkj3y6j2e%XT|mM7qX%y`kAIPj@HX&U?MMY%sMjP~fuNOBF}AG8<0m zcm&IKD^JxdP4_Y%&o66cODuk|%ZBqDbY|nZvKSdA8&NmRfIL6LWHYF0nBn?^PiMdH z*k#LPE_c%H%tqAvAGtFhPj%;jJayVY_tU30SYH>rsLkF9neRIvWxi#;8go6~{>S6% fV%d74`Q`4}dLYO=aJnDRNCnBml%cQ40*(GZg#${d literal 0 HcmV?d00001 diff --git a/src/main/resources/runtimeid_table.json b/src/main/resources/runtimeid_table.json deleted file mode 100644 index e80cb07485d..00000000000 --- a/src/main/resources/runtimeid_table.json +++ /dev/null @@ -1,16557 +0,0 @@ -[ - { - "name": "minecraft:air", - "id": 0, - "data": 0 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 0 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 1 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 2 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 3 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 4 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 5 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 6 - }, - { - "name": "minecraft:stone", - "id": 1, - "data": 7 - }, - { - "name": "minecraft:grass", - "id": 2, - "data": 0 - }, - { - "name": "minecraft:dirt", - "id": 3, - "data": 0 - }, - { - "name": "minecraft:dirt", - "id": 3, - "data": 1 - }, - { - "name": "minecraft:cobblestone", - "id": 4, - "data": 0 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 0 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 1 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 2 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 3 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 4 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 5 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 6 - }, - { - "name": "minecraft:planks", - "id": 5, - "data": 7 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 0 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 1 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 2 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 3 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 4 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 5 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 6 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 7 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 8 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 9 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 10 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 11 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 12 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 13 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 14 - }, - { - "name": "minecraft:sapling", - "id": 6, - "data": 15 - }, - { - "name": "minecraft:bedrock", - "id": 7, - "data": 0 - }, - { - "name": "minecraft:bedrock", - "id": 7, - "data": 1 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 0 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 1 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 2 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 3 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 4 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 5 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 6 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 7 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 8 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 9 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 10 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 11 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 12 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 13 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 14 - }, - { - "name": "minecraft:flowing_water", - "id": 8, - "data": 15 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 0 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 1 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 2 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 3 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 4 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 5 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 6 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 7 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 8 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 9 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 10 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 11 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 12 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 13 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 14 - }, - { - "name": "minecraft:water", - "id": 9, - "data": 15 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 0 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 1 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 2 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 3 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 4 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 5 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 6 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 7 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 8 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 9 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 10 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 11 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 12 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 13 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 14 - }, - { - "name": "minecraft:flowing_lava", - "id": 10, - "data": 15 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 0 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 1 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 2 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 3 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 4 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 5 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 6 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 7 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 8 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 9 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 10 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 11 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 12 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 13 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 14 - }, - { - "name": "minecraft:lava", - "id": 11, - "data": 15 - }, - { - "name": "minecraft:sand", - "id": 12, - "data": 0 - }, - { - "name": "minecraft:sand", - "id": 12, - "data": 1 - }, - { - "name": "minecraft:gravel", - "id": 13, - "data": 0 - }, - { - "name": "minecraft:gold_ore", - "id": 14, - "data": 0 - }, - { - "name": "minecraft:iron_ore", - "id": 15, - "data": 0 - }, - { - "name": "minecraft:coal_ore", - "id": 16, - "data": 0 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 0 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 1 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 2 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 3 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 4 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 5 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 6 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 7 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 8 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 9 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 10 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 11 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 12 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 13 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 14 - }, - { - "name": "minecraft:log", - "id": 17, - "data": 15 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 0 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 1 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 2 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 3 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 4 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 5 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 6 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 7 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 8 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 9 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 10 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 11 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 12 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 13 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 14 - }, - { - "name": "minecraft:leaves", - "id": 18, - "data": 15 - }, - { - "name": "minecraft:sponge", - "id": 19, - "data": 0 - }, - { - "name": "minecraft:sponge", - "id": 19, - "data": 1 - }, - { - "name": "minecraft:glass", - "id": 20, - "data": 0 - }, - { - "name": "minecraft:lapis_ore", - "id": 21, - "data": 0 - }, - { - "name": "minecraft:lapis_block", - "id": 22, - "data": 0 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 0 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 1 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 2 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 3 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 4 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 5 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 6 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 7 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 8 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 9 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 10 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 11 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 12 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 13 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 14 - }, - { - "name": "minecraft:dispenser", - "id": 23, - "data": 15 - }, - { - "name": "minecraft:sandstone", - "id": 24, - "data": 0 - }, - { - "name": "minecraft:sandstone", - "id": 24, - "data": 1 - }, - { - "name": "minecraft:sandstone", - "id": 24, - "data": 2 - }, - { - "name": "minecraft:sandstone", - "id": 24, - "data": 3 - }, - { - "name": "minecraft:noteblock", - "id": 25, - "data": 0 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 0 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 1 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 2 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 3 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 4 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 5 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 6 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 7 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 8 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 9 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 10 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 11 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 12 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 13 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 14 - }, - { - "name": "minecraft:bed", - "id": 26, - "data": 15 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 0 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 1 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 2 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 3 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 4 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 5 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 6 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 7 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 8 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 9 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 10 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 11 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 12 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 13 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 14 - }, - { - "name": "minecraft:golden_rail", - "id": 27, - "data": 15 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 0 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 1 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 2 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 3 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 4 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 5 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 6 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 7 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 8 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 9 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 10 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 11 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 12 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 13 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 14 - }, - { - "name": "minecraft:detector_rail", - "id": 28, - "data": 15 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 0 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 1 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 2 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 3 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 4 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 5 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 6 - }, - { - "name": "minecraft:sticky_piston", - "id": 29, - "data": 7 - }, - { - "name": "minecraft:web", - "id": 30, - "data": 0 - }, - { - "name": "minecraft:tallgrass", - "id": 31, - "data": 0 - }, - { - "name": "minecraft:tallgrass", - "id": 31, - "data": 1 - }, - { - "name": "minecraft:tallgrass", - "id": 31, - "data": 2 - }, - { - "name": "minecraft:tallgrass", - "id": 31, - "data": 3 - }, - { - "name": "minecraft:deadbush", - "id": 32, - "data": 0 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 0 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 1 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 2 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 3 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 4 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 5 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 6 - }, - { - "name": "minecraft:piston", - "id": 33, - "data": 7 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 0 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 1 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 2 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 3 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 4 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 5 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 6 - }, - { - "name": "minecraft:pistonArmCollision", - "id": 34, - "data": 7 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 0 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 1 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 2 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 3 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 4 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 5 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 6 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 7 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 8 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 9 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 10 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 11 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 12 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 13 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 14 - }, - { - "name": "minecraft:wool", - "id": 35, - "data": 15 - }, - { - "name": "minecraft:element_0", - "id": 36, - "data": 0 - }, - { - "name": "minecraft:yellow_flower", - "id": 37, - "data": 0 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 0 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 1 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 2 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 3 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 4 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 5 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 6 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 7 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 8 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 9 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 10 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 11 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 12 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 13 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 14 - }, - { - "name": "minecraft:red_flower", - "id": 38, - "data": 15 - }, - { - "name": "minecraft:brown_mushroom", - "id": 39, - "data": 0 - }, - { - "name": "minecraft:red_mushroom", - "id": 40, - "data": 0 - }, - { - "name": "minecraft:gold_block", - "id": 41, - "data": 0 - }, - { - "name": "minecraft:iron_block", - "id": 42, - "data": 0 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 0 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 1 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 2 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 3 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 4 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 5 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 6 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 7 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 8 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 9 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 10 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 11 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 12 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 13 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 14 - }, - { - "name": "minecraft:double_stone_slab", - "id": 43, - "data": 15 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 0 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 1 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 2 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 3 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 4 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 5 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 6 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 7 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 8 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 9 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 10 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 11 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 12 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 13 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 14 - }, - { - "name": "minecraft:stone_slab", - "id": 44, - "data": 15 - }, - { - "name": "minecraft:brick_block", - "id": 45, - "data": 0 - }, - { - "name": "minecraft:tnt", - "id": 46, - "data": 0 - }, - { - "name": "minecraft:tnt", - "id": 46, - "data": 1 - }, - { - "name": "minecraft:tnt", - "id": 46, - "data": 2 - }, - { - "name": "minecraft:tnt", - "id": 46, - "data": 3 - }, - { - "name": "minecraft:bookshelf", - "id": 47, - "data": 0 - }, - { - "name": "minecraft:mossy_cobblestone", - "id": 48, - "data": 0 - }, - { - "name": "minecraft:obsidian", - "id": 49, - "data": 0 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 0 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 1 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 2 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 3 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 4 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 5 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 6 - }, - { - "name": "minecraft:torch", - "id": 50, - "data": 7 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 0 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 1 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 2 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 3 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 4 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 5 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 6 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 7 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 8 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 9 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 10 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 11 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 12 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 13 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 14 - }, - { - "name": "minecraft:fire", - "id": 51, - "data": 15 - }, - { - "name": "minecraft:mob_spawner", - "id": 52, - "data": 0 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 0 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 1 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 2 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 3 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 4 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 5 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 6 - }, - { - "name": "minecraft:oak_stairs", - "id": 53, - "data": 7 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 0 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 1 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 2 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 3 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 4 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 5 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 6 - }, - { - "name": "minecraft:chest", - "id": 54, - "data": 7 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 0 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 1 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 2 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 3 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 4 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 5 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 6 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 7 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 8 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 9 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 10 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 11 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 12 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 13 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 14 - }, - { - "name": "minecraft:redstone_wire", - "id": 55, - "data": 15 - }, - { - "name": "minecraft:diamond_ore", - "id": 56, - "data": 0 - }, - { - "name": "minecraft:diamond_block", - "id": 57, - "data": 0 - }, - { - "name": "minecraft:crafting_table", - "id": 58, - "data": 0 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 0 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 1 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 2 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 3 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 4 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 5 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 6 - }, - { - "name": "minecraft:wheat", - "id": 59, - "data": 7 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 0 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 1 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 2 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 3 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 4 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 5 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 6 - }, - { - "name": "minecraft:farmland", - "id": 60, - "data": 7 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 0 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 1 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 2 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 3 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 4 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 5 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 6 - }, - { - "name": "minecraft:furnace", - "id": 61, - "data": 7 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 0 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 1 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 2 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 3 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 4 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 5 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 6 - }, - { - "name": "minecraft:lit_furnace", - "id": 62, - "data": 7 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 0 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 1 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 2 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 3 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 4 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 5 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 6 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 7 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 8 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 9 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 10 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 11 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 12 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 13 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 14 - }, - { - "name": "minecraft:standing_sign", - "id": 63, - "data": 15 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 0 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 1 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 2 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 3 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 4 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 5 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 6 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 7 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 8 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 9 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 10 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 11 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 12 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 13 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 14 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 15 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 16 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 17 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 18 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 19 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 20 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 21 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 22 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 23 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 24 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 25 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 26 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 27 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 28 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 29 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 30 - }, - { - "name": "minecraft:wooden_door", - "id": 64, - "data": 31 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 0 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 1 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 2 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 3 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 4 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 5 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 6 - }, - { - "name": "minecraft:ladder", - "id": 65, - "data": 7 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 0 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 1 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 2 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 3 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 4 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 5 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 6 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 7 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 8 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 9 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 10 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 11 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 12 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 13 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 14 - }, - { - "name": "minecraft:rail", - "id": 66, - "data": 15 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 0 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 1 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 2 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 3 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 4 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 5 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 6 - }, - { - "name": "minecraft:stone_stairs", - "id": 67, - "data": 7 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 0 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 1 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 2 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 3 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 4 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 5 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 6 - }, - { - "name": "minecraft:wall_sign", - "id": 68, - "data": 7 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 0 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 1 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 2 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 3 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 4 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 5 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 6 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 7 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 8 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 9 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 10 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 11 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 12 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 13 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 14 - }, - { - "name": "minecraft:lever", - "id": 69, - "data": 15 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 0 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 1 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 2 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 3 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 4 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 5 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 6 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 7 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 8 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 9 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 10 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 11 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 12 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 13 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 14 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70, - "data": 15 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 0 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 1 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 2 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 3 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 4 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 5 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 6 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 7 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 8 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 9 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 10 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 11 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 12 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 13 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 14 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 15 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 16 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 17 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 18 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 19 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 20 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 21 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 22 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 23 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 24 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 25 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 26 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 27 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 28 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 29 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 30 - }, - { - "name": "minecraft:iron_door", - "id": 71, - "data": 31 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 0 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 1 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 2 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 3 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 4 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 5 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 6 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 7 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 8 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 9 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 10 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 11 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 12 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 13 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 14 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72, - "data": 15 - }, - { - "name": "minecraft:redstone_ore", - "id": 73, - "data": 0 - }, - { - "name": "minecraft:lit_redstone_ore", - "id": 74, - "data": 0 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 0 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 1 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 2 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 3 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 4 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 5 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 6 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75, - "data": 7 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 0 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 1 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 2 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 3 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 4 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 5 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 6 - }, - { - "name": "minecraft:redstone_torch", - "id": 76, - "data": 7 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 0 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 1 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 2 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 3 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 4 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 5 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 6 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 7 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 8 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 9 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 10 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 11 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 12 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 13 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 14 - }, - { - "name": "minecraft:stone_button", - "id": 77, - "data": 15 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 0 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 1 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 2 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 3 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 4 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 5 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 6 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 7 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 8 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 9 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 10 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 11 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 12 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 13 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 14 - }, - { - "name": "minecraft:snow_layer", - "id": 78, - "data": 15 - }, - { - "name": "minecraft:ice", - "id": 79, - "data": 0 - }, - { - "name": "minecraft:snow", - "id": 80, - "data": 0 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 0 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 1 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 2 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 3 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 4 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 5 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 6 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 7 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 8 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 9 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 10 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 11 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 12 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 13 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 14 - }, - { - "name": "minecraft:cactus", - "id": 81, - "data": 15 - }, - { - "name": "minecraft:clay", - "id": 82, - "data": 0 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 0 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 1 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 2 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 3 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 4 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 5 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 6 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 7 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 8 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 9 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 10 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 11 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 12 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 13 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 14 - }, - { - "name": "minecraft:reeds", - "id": 83, - "data": 15 - }, - { - "name": "minecraft:jukebox", - "id": 84, - "data": 0 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 0 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 1 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 2 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 3 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 4 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 5 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 6 - }, - { - "name": "minecraft:fence", - "id": 85, - "data": 7 - }, - { - "name": "minecraft:pumpkin", - "id": 86, - "data": 0 - }, - { - "name": "minecraft:pumpkin", - "id": 86, - "data": 1 - }, - { - "name": "minecraft:pumpkin", - "id": 86, - "data": 2 - }, - { - "name": "minecraft:pumpkin", - "id": 86, - "data": 3 - }, - { - "name": "minecraft:netherrack", - "id": 87, - "data": 0 - }, - { - "name": "minecraft:soul_sand", - "id": 88, - "data": 0 - }, - { - "name": "minecraft:glowstone", - "id": 89, - "data": 0 - }, - { - "name": "minecraft:portal", - "id": 90, - "data": 0 - }, - { - "name": "minecraft:portal", - "id": 90, - "data": 1 - }, - { - "name": "minecraft:portal", - "id": 90, - "data": 2 - }, - { - "name": "minecraft:portal", - "id": 90, - "data": 3 - }, - { - "name": "minecraft:lit_pumpkin", - "id": 91, - "data": 0 - }, - { - "name": "minecraft:lit_pumpkin", - "id": 91, - "data": 1 - }, - { - "name": "minecraft:lit_pumpkin", - "id": 91, - "data": 2 - }, - { - "name": "minecraft:lit_pumpkin", - "id": 91, - "data": 3 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 0 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 1 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 2 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 3 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 4 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 5 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 6 - }, - { - "name": "minecraft:cake", - "id": 92, - "data": 7 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 0 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 1 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 2 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 3 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 4 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 5 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 6 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 7 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 8 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 9 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 10 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 11 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 12 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 13 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 14 - }, - { - "name": "minecraft:unpowered_repeater", - "id": 93, - "data": 15 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 0 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 1 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 2 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 3 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 4 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 5 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 6 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 7 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 8 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 9 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 10 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 11 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 12 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 13 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 14 - }, - { - "name": "minecraft:powered_repeater", - "id": 94, - "data": 15 - }, - { - "name": "minecraft:invisibleBedrock", - "id": 95, - "data": 0 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 0 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 1 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 2 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 3 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 4 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 5 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 6 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 7 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 8 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 9 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 10 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 11 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 12 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 13 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 14 - }, - { - "name": "minecraft:trapdoor", - "id": 96, - "data": 15 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 0 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 1 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 2 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 3 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 4 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 5 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 6 - }, - { - "name": "minecraft:monster_egg", - "id": 97, - "data": 7 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 0 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 1 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 2 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 3 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 4 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 5 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 6 - }, - { - "name": "minecraft:stonebrick", - "id": 98, - "data": 7 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 0 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 1 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 2 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 3 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 4 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 5 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 6 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 7 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 8 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 9 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 10 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 11 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 12 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 13 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 14 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99, - "data": 15 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 0 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 1 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 2 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 3 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 4 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 5 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 6 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 7 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 8 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 9 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 10 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 11 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 12 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 13 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 14 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100, - "data": 15 - }, - { - "name": "minecraft:iron_bars", - "id": 101, - "data": 0 - }, - { - "name": "minecraft:glass_pane", - "id": 102, - "data": 0 - }, - { - "name": "minecraft:melon_block", - "id": 103, - "data": 0 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 0 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 1 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 2 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 3 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 4 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 5 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 6 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104, - "data": 7 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 0 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 1 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 2 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 3 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 4 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 5 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 6 - }, - { - "name": "minecraft:melon_stem", - "id": 105, - "data": 7 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 0 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 1 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 2 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 3 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 4 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 5 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 6 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 7 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 8 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 9 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 10 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 11 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 12 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 13 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 14 - }, - { - "name": "minecraft:vine", - "id": 106, - "data": 15 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 0 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 1 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 2 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 3 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 4 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 5 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 6 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 7 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 8 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 9 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 10 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 11 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 12 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 13 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 14 - }, - { - "name": "minecraft:fence_gate", - "id": 107, - "data": 15 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 0 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 1 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 2 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 3 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 4 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 5 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 6 - }, - { - "name": "minecraft:brick_stairs", - "id": 108, - "data": 7 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 0 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 1 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 2 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 3 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 4 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 5 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 6 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109, - "data": 7 - }, - { - "name": "minecraft:mycelium", - "id": 110, - "data": 0 - }, - { - "name": "minecraft:waterlily", - "id": 111, - "data": 0 - }, - { - "name": "minecraft:nether_brick", - "id": 112, - "data": 0 - }, - { - "name": "minecraft:nether_brick_fence", - "id": 113, - "data": 0 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 0 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 1 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 2 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 3 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 4 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 5 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 6 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114, - "data": 7 - }, - { - "name": "minecraft:nether_wart", - "id": 115, - "data": 0 - }, - { - "name": "minecraft:nether_wart", - "id": 115, - "data": 1 - }, - { - "name": "minecraft:nether_wart", - "id": 115, - "data": 2 - }, - { - "name": "minecraft:nether_wart", - "id": 115, - "data": 3 - }, - { - "name": "minecraft:enchanting_table", - "id": 116, - "data": 0 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 0 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 1 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 2 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 3 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 4 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 5 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 6 - }, - { - "name": "minecraft:brewing_stand", - "id": 117, - "data": 7 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 0 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 1 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 2 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 3 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 4 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 5 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 6 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 7 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 8 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 9 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 10 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 11 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 12 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 13 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 14 - }, - { - "name": "minecraft:cauldron", - "id": 118, - "data": 15 - }, - { - "name": "minecraft:end_portal", - "id": 119, - "data": 0 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 0 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 1 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 2 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 3 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 4 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 5 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 6 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120, - "data": 7 - }, - { - "name": "minecraft:end_stone", - "id": 121, - "data": 0 - }, - { - "name": "minecraft:dragon_egg", - "id": 122, - "data": 0 - }, - { - "name": "minecraft:redstone_lamp", - "id": 123, - "data": 0 - }, - { - "name": "minecraft:lit_redstone_lamp", - "id": 124, - "data": 0 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 0 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 1 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 2 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 3 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 4 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 5 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 6 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 7 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 8 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 9 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 10 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 11 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 12 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 13 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 14 - }, - { - "name": "minecraft:dropper", - "id": 125, - "data": 15 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 0 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 1 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 2 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 3 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 4 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 5 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 6 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 7 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 8 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 9 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 10 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 11 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 12 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 13 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 14 - }, - { - "name": "minecraft:activator_rail", - "id": 126, - "data": 15 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 0 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 1 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 2 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 3 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 4 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 5 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 6 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 7 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 8 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 9 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 10 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 11 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 12 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 13 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 14 - }, - { - "name": "minecraft:cocoa", - "id": 127, - "data": 15 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 0 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 1 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 2 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 3 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 4 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 5 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 6 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128, - "data": 7 - }, - { - "name": "minecraft:emerald_ore", - "id": 129, - "data": 0 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 0 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 1 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 2 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 3 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 4 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 5 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 6 - }, - { - "name": "minecraft:ender_chest", - "id": 130, - "data": 7 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 0 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 1 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 2 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 3 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 4 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 5 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 6 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 7 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 8 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 9 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 10 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 11 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 12 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 13 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 14 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131, - "data": 15 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 0 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 1 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 2 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 3 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 4 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 5 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 6 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 7 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 8 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 9 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 10 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 11 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 12 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 13 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 14 - }, - { - "name": "minecraft:tripWire", - "id": 132, - "data": 15 - }, - { - "name": "minecraft:emerald_block", - "id": 133, - "data": 0 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 0 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 1 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 2 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 3 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 4 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 5 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 6 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134, - "data": 7 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 0 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 1 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 2 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 3 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 4 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 5 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 6 - }, - { - "name": "minecraft:birch_stairs", - "id": 135, - "data": 7 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 0 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 1 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 2 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 3 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 4 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 5 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 6 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136, - "data": 7 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 0 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 1 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 2 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 3 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 4 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 5 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 6 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 7 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 8 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 9 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 10 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 11 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 12 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 13 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 14 - }, - { - "name": "minecraft:command_block", - "id": 137, - "data": 15 - }, - { - "name": "minecraft:beacon", - "id": 138, - "data": 0 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 0 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 1 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 2 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 3 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 4 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 5 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 6 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 7 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 8 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 9 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 10 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 11 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 12 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 13 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 14 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139, - "data": 15 - }, - { - "name": "minecraft:flower_pot", - "id": 140, - "data": 0 - }, - { - "name": "minecraft:flower_pot", - "id": 140, - "data": 1 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 0 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 1 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 2 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 3 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 4 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 5 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 6 - }, - { - "name": "minecraft:carrots", - "id": 141, - "data": 7 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 0 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 1 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 2 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 3 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 4 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 5 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 6 - }, - { - "name": "minecraft:potatoes", - "id": 142, - "data": 7 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 0 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 1 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 2 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 3 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 4 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 5 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 6 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 7 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 8 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 9 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 10 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 11 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 12 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 13 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 14 - }, - { - "name": "minecraft:wooden_button", - "id": 143, - "data": 15 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 0 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 1 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 2 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 3 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 4 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 5 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 6 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 7 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 8 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 9 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 10 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 11 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 12 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 13 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 14 - }, - { - "name": "minecraft:skull", - "id": 144, - "data": 15 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 0 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 1 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 2 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 3 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 4 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 5 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 6 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 7 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 8 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 9 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 10 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 11 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 12 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 13 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 14 - }, - { - "name": "minecraft:anvil", - "id": 145, - "data": 15 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 0 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 1 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 2 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 3 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 4 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 5 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 6 - }, - { - "name": "minecraft:trapped_chest", - "id": 146, - "data": 7 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 0 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 1 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 2 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 3 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 4 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 5 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 6 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 7 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 8 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 9 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 10 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 11 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 12 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 13 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 14 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147, - "data": 15 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 0 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 1 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 2 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 3 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 4 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 5 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 6 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 7 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 8 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 9 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 10 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 11 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 12 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 13 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 14 - }, - { - "name": "minecraft:heavy_weighted_pressure_plate", - "id": 148, - "data": 15 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 0 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 1 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 2 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 3 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 4 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 5 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 6 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 7 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 8 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 9 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 10 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 11 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 12 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 13 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 14 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149, - "data": 15 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 0 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 1 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 2 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 3 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 4 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 5 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 6 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 7 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 8 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 9 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 10 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 11 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 12 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 13 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 14 - }, - { - "name": "minecraft:powered_comparator", - "id": 150, - "data": 15 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 0 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 1 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 2 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 3 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 4 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 5 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 6 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 7 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 8 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 9 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 10 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 11 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 12 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 13 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 14 - }, - { - "name": "minecraft:daylight_detector", - "id": 151, - "data": 15 - }, - { - "name": "minecraft:redstone_block", - "id": 152, - "data": 0 - }, - { - "name": "minecraft:quartz_ore", - "id": 153, - "data": 0 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 0 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 1 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 2 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 3 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 4 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 5 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 6 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 7 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 8 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 9 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 10 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 11 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 12 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 13 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 14 - }, - { - "name": "minecraft:hopper", - "id": 154, - "data": 15 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 0 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 1 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 2 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 3 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 4 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 5 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 6 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 7 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 8 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 9 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 10 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 11 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 12 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 13 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 14 - }, - { - "name": "minecraft:quartz_block", - "id": 155, - "data": 15 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 0 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 1 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 2 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 3 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 4 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 5 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 6 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156, - "data": 7 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 0 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 1 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 2 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 3 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 4 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 5 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 6 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 7 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 8 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 9 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 10 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 11 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 12 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 13 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 14 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157, - "data": 15 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 0 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 1 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 2 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 3 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 4 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 5 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 6 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 7 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 8 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 9 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 10 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 11 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 12 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 13 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 14 - }, - { - "name": "minecraft:wooden_slab", - "id": 158, - "data": 15 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 0 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 1 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 2 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 3 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 4 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 5 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 6 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 7 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 8 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 9 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 10 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 11 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 12 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 13 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 14 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159, - "data": 15 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 0 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 1 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 2 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 3 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 4 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 5 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 6 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 7 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 8 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 9 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 10 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 11 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 12 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 13 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 14 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160, - "data": 15 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 0 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 1 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 2 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 3 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 4 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 5 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 6 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 7 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 8 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 9 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 10 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 11 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 12 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 13 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 14 - }, - { - "name": "minecraft:leaves2", - "id": 161, - "data": 15 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 0 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 1 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 2 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 3 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 4 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 5 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 6 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 7 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 8 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 9 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 10 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 11 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 12 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 13 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 14 - }, - { - "name": "minecraft:log2", - "id": 162, - "data": 15 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 0 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 1 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 2 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 3 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 4 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 5 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 6 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163, - "data": 7 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 0 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 1 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 2 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 3 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 4 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 5 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 6 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164, - "data": 7 - }, - { - "name": "minecraft:slime", - "id": 165, - "data": 0 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 0 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 1 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 2 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 3 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 4 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 5 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 6 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 7 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 8 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 9 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 10 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 11 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 12 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 13 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 14 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167, - "data": 15 - }, - { - "name": "minecraft:prismarine", - "id": 168, - "data": 0 - }, - { - "name": "minecraft:prismarine", - "id": 168, - "data": 1 - }, - { - "name": "minecraft:prismarine", - "id": 168, - "data": 2 - }, - { - "name": "minecraft:prismarine", - "id": 168, - "data": 3 - }, - { - "name": "minecraft:seaLantern", - "id": 169, - "data": 0 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 0 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 1 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 2 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 3 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 4 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 5 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 6 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 7 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 8 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 9 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 10 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 11 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 12 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 13 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 14 - }, - { - "name": "minecraft:hay_block", - "id": 170, - "data": 15 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 0 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 1 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 2 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 3 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 4 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 5 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 6 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 7 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 8 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 9 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 10 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 11 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 12 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 13 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 14 - }, - { - "name": "minecraft:carpet", - "id": 171, - "data": 15 - }, - { - "name": "minecraft:hardened_clay", - "id": 172, - "data": 0 - }, - { - "name": "minecraft:coal_block", - "id": 173, - "data": 0 - }, - { - "name": "minecraft:packed_ice", - "id": 174, - "data": 0 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 0 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 1 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 2 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 3 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 4 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 5 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 6 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 7 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 8 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 9 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 10 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 11 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 12 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 13 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 14 - }, - { - "name": "minecraft:double_plant", - "id": 175, - "data": 15 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 0 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 1 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 2 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 3 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 4 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 5 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 6 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 7 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 8 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 9 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 10 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 11 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 12 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 13 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 14 - }, - { - "name": "minecraft:standing_banner", - "id": 176, - "data": 15 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 0 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 1 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 2 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 3 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 4 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 5 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 6 - }, - { - "name": "minecraft:wall_banner", - "id": 177, - "data": 7 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 0 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 1 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 2 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 3 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 4 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 5 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 6 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 7 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 8 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 9 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 10 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 11 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 12 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 13 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 14 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178, - "data": 15 - }, - { - "name": "minecraft:red_sandstone", - "id": 179, - "data": 0 - }, - { - "name": "minecraft:red_sandstone", - "id": 179, - "data": 1 - }, - { - "name": "minecraft:red_sandstone", - "id": 179, - "data": 2 - }, - { - "name": "minecraft:red_sandstone", - "id": 179, - "data": 3 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 0 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 1 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 2 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 3 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 4 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 5 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 6 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180, - "data": 7 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 0 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 1 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 2 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 3 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 4 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 5 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 6 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 7 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 8 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 9 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 10 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 11 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 12 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 13 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 14 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 181, - "data": 15 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 0 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 1 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 2 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 3 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 4 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 5 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 6 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 7 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 8 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 9 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 10 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 11 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 12 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 13 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 14 - }, - { - "name": "minecraft:stone_slab2", - "id": 182, - "data": 15 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 0 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 1 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 2 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 3 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 4 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 5 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 6 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 7 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 8 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 9 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 10 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 11 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 12 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 13 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 14 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183, - "data": 15 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 0 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 1 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 2 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 3 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 4 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 5 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 6 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 7 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 8 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 9 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 10 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 11 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 12 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 13 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 14 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184, - "data": 15 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 0 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 1 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 2 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 3 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 4 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 5 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 6 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 7 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 8 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 9 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 10 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 11 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 12 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 13 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 14 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185, - "data": 15 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 0 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 1 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 2 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 3 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 4 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 5 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 6 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 7 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 8 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 9 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 10 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 11 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 12 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 13 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 14 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186, - "data": 15 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 0 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 1 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 2 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 3 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 4 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 5 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 6 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 7 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 8 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 9 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 10 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 11 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 12 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 13 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 14 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187, - "data": 15 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 0 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 1 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 2 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 3 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 4 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 5 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 6 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 7 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 8 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 9 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 10 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 11 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 12 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 13 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 14 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188, - "data": 15 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 0 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 1 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 2 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 3 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 4 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 5 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 6 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 7 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 8 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 9 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 10 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 11 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 12 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 13 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 14 - }, - { - "name": "minecraft:chain_command_block", - "id": 189, - "data": 15 - }, - { - "name": "minecraft:hard_glass_pane", - "id": 190, - "data": 0 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 0 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 1 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 2 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 3 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 4 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 5 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 6 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 7 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 8 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 9 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 10 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 11 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 12 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 13 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 14 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191, - "data": 15 - }, - { - "name": "minecraft:chemical_heat", - "id": 192, - "data": 0 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 0 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 1 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 2 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 3 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 4 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 5 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 6 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 7 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 8 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 9 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 10 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 11 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 12 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 13 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 14 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 15 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 16 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 17 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 18 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 19 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 20 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 21 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 22 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 23 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 24 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 25 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 26 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 27 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 28 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 29 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 30 - }, - { - "name": "minecraft:spruce_door", - "id": 193, - "data": 31 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 0 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 1 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 2 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 3 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 4 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 5 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 6 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 7 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 8 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 9 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 10 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 11 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 12 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 13 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 14 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 15 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 16 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 17 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 18 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 19 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 20 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 21 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 22 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 23 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 24 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 25 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 26 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 27 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 28 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 29 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 30 - }, - { - "name": "minecraft:birch_door", - "id": 194, - "data": 31 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 0 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 1 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 2 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 3 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 4 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 5 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 6 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 7 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 8 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 9 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 10 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 11 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 12 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 13 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 14 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 15 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 16 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 17 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 18 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 19 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 20 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 21 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 22 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 23 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 24 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 25 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 26 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 27 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 28 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 29 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 30 - }, - { - "name": "minecraft:jungle_door", - "id": 195, - "data": 31 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 0 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 1 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 2 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 3 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 4 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 5 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 6 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 7 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 8 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 9 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 10 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 11 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 12 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 13 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 14 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 15 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 16 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 17 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 18 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 19 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 20 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 21 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 22 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 23 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 24 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 25 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 26 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 27 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 28 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 29 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 30 - }, - { - "name": "minecraft:acacia_door", - "id": 196, - "data": 31 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 0 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 1 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 2 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 3 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 4 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 5 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 6 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 7 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 8 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 9 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 10 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 11 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 12 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 13 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 14 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 15 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 16 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 17 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 18 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 19 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 20 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 21 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 22 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 23 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 24 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 25 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 26 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 27 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 28 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 29 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 30 - }, - { - "name": "minecraft:dark_oak_door", - "id": 197, - "data": 31 - }, - { - "name": "minecraft:grass_path", - "id": 198, - "data": 0 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 0 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 1 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 2 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 3 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 4 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 5 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 6 - }, - { - "name": "minecraft:frame", - "id": 199, - "data": 7 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 0 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 1 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 2 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 3 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 4 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 5 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 6 - }, - { - "name": "minecraft:chorus_flower", - "id": 200, - "data": 7 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 0 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 1 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 2 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 3 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 4 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 5 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 6 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 7 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 8 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 9 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 10 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 11 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 12 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 13 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 14 - }, - { - "name": "minecraft:purpur_block", - "id": 201, - "data": 15 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 0 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 1 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 2 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 3 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 4 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 5 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 6 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 7 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 8 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 9 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 10 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 11 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 12 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 13 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 14 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202, - "data": 15 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 0 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 1 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 2 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 3 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 4 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 5 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 6 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203, - "data": 7 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 0 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 1 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 2 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 3 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 4 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 5 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 6 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 7 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 8 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 9 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 10 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 11 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 12 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 13 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 14 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204, - "data": 15 - }, - { - "name": "minecraft:undyed_shulker_box", - "id": 205, - "data": 0 - }, - { - "name": "minecraft:end_bricks", - "id": 206, - "data": 0 - }, - { - "name": "minecraft:frosted_ice", - "id": 207, - "data": 0 - }, - { - "name": "minecraft:frosted_ice", - "id": 207, - "data": 1 - }, - { - "name": "minecraft:frosted_ice", - "id": 207, - "data": 2 - }, - { - "name": "minecraft:frosted_ice", - "id": 207, - "data": 3 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 0 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 1 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 2 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 3 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 4 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 5 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 6 - }, - { - "name": "minecraft:end_rod", - "id": 208, - "data": 7 - }, - { - "name": "minecraft:end_gateway", - "id": 209, - "data": 0 - }, - { - "name": "minecraft:magma", - "id": 213, - "data": 0 - }, - { - "name": "minecraft:nether_wart_block", - "id": 214, - "data": 0 - }, - { - "name": "minecraft:red_nether_brick", - "id": 215, - "data": 0 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 0 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 1 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 2 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 3 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 4 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 5 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 6 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 7 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 8 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 9 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 10 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 11 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 12 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 13 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 14 - }, - { - "name": "minecraft:bone_block", - "id": 216, - "data": 15 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 0 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 1 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 2 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 3 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 4 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 5 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 6 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 7 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 8 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 9 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 10 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 11 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 12 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 13 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 14 - }, - { - "name": "minecraft:shulker_box", - "id": 218, - "data": 15 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 0 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 1 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 2 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 3 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 4 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 5 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 6 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219, - "data": 7 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 0 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 1 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 2 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 3 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 4 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 5 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 6 - }, - { - "name": "minecraft:white_glazed_terracotta", - "id": 220, - "data": 7 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 0 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 1 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 2 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 3 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 4 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 5 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 6 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221, - "data": 7 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 0 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 1 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 2 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 3 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 4 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 5 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 6 - }, - { - "name": "minecraft:magenta_glazed_terracotta", - "id": 222, - "data": 7 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 0 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 1 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 2 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 3 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 4 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 5 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 6 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223, - "data": 7 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 0 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 1 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 2 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 3 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 4 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 5 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 6 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224, - "data": 7 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 0 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 1 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 2 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 3 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 4 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 5 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 6 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225, - "data": 7 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 0 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 1 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 2 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 3 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 4 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 5 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 6 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226, - "data": 7 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 0 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 1 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 2 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 3 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 4 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 5 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 6 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227, - "data": 7 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 0 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 1 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 2 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 3 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 4 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 5 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 6 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228, - "data": 7 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 0 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 1 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 2 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 3 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 4 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 5 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 6 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229, - "data": 7 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 0 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 1 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 2 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 3 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 4 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 5 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 6 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231, - "data": 7 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 0 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 1 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 2 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 3 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 4 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 5 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 6 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232, - "data": 7 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 0 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 1 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 2 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 3 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 4 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 5 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 6 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233, - "data": 7 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 0 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 1 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 2 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 3 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 4 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 5 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 6 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234, - "data": 7 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 0 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 1 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 2 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 3 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 4 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 5 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 6 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235, - "data": 7 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 0 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 1 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 2 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 3 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 4 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 5 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 6 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 7 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 8 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 9 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 10 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 11 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 12 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 13 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 14 - }, - { - "name": "minecraft:concrete", - "id": 236, - "data": 15 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 0 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 1 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 2 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 3 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 4 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 5 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 6 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 7 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 8 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 9 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 10 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 11 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 12 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 13 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 14 - }, - { - "name": "minecraft:concretePowder", - "id": 237, - "data": 15 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 0 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 1 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 2 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 3 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 4 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 5 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 6 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 7 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 8 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 9 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 10 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 11 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 12 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 13 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 14 - }, - { - "name": "minecraft:chemistry_table", - "id": 238, - "data": 15 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 0 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 1 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 2 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 3 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 4 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 5 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 6 - }, - { - "name": "minecraft:underwater_torch", - "id": 239, - "data": 7 - }, - { - "name": "minecraft:chorus_plant", - "id": 240, - "data": 0 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 0 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 1 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 2 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 3 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 4 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 5 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 6 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 7 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 8 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 9 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 10 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 11 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 12 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 13 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 14 - }, - { - "name": "minecraft:stained_glass", - "id": 241, - "data": 15 - }, - { - "name": "minecraft:podzol", - "id": 243, - "data": 0 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 0 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 1 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 2 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 3 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 4 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 5 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 6 - }, - { - "name": "minecraft:beetroot", - "id": 244, - "data": 7 - }, - { - "name": "minecraft:stonecutter", - "id": 245, - "data": 0 - }, - { - "name": "minecraft:glowingobsidian", - "id": 246, - "data": 0 - }, - { - "name": "minecraft:netherreactor", - "id": 247, - "data": 0 - }, - { - "name": "minecraft:info_update", - "id": 248, - "data": 0 - }, - { - "name": "minecraft:info_update2", - "id": 249, - "data": 0 - }, - { - "name": "minecraft:movingBlock", - "id": 250, - "data": 0 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 0 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 1 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 2 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 3 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 4 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 5 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 6 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 7 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 8 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 9 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 10 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 11 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 12 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 13 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 14 - }, - { - "name": "minecraft:observer", - "id": 251, - "data": 15 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 0 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 1 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 2 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 3 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 4 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 5 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 6 - }, - { - "name": "minecraft:structure_block", - "id": 252, - "data": 7 - }, - { - "name": "minecraft:hard_glass", - "id": 253, - "data": 0 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 0 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 1 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 2 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 3 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 4 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 5 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 6 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 7 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 8 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 9 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 10 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 11 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 12 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 13 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 14 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254, - "data": 15 - }, - { - "name": "minecraft:reserved6", - "id": 255, - "data": 0 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 0 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 1 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 2 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 3 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 4 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 5 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 6 - }, - { - "name": "minecraft:prismarine_stairs", - "id": 257, - "data": 7 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 0 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 1 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 2 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 3 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 4 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 5 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 6 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": 258, - "data": 7 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 0 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 1 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 2 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 3 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 4 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 5 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 6 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": 259, - "data": 7 - }, - { - "name": "minecraft:stripped_spruce_log", - "id": 260, - "data": 0 - }, - { - "name": "minecraft:stripped_spruce_log", - "id": 260, - "data": 1 - }, - { - "name": "minecraft:stripped_spruce_log", - "id": 260, - "data": 2 - }, - { - "name": "minecraft:stripped_spruce_log", - "id": 260, - "data": 3 - }, - { - "name": "minecraft:stripped_birch_log", - "id": 261, - "data": 0 - }, - { - "name": "minecraft:stripped_birch_log", - "id": 261, - "data": 1 - }, - { - "name": "minecraft:stripped_birch_log", - "id": 261, - "data": 2 - }, - { - "name": "minecraft:stripped_birch_log", - "id": 261, - "data": 3 - }, - { - "name": "minecraft:stripped_jungle_log", - "id": 262, - "data": 0 - }, - { - "name": "minecraft:stripped_jungle_log", - "id": 262, - "data": 1 - }, - { - "name": "minecraft:stripped_jungle_log", - "id": 262, - "data": 2 - }, - { - "name": "minecraft:stripped_jungle_log", - "id": 262, - "data": 3 - }, - { - "name": "minecraft:stripped_acacia_log", - "id": 263, - "data": 0 - }, - { - "name": "minecraft:stripped_acacia_log", - "id": 263, - "data": 1 - }, - { - "name": "minecraft:stripped_acacia_log", - "id": 263, - "data": 2 - }, - { - "name": "minecraft:stripped_acacia_log", - "id": 263, - "data": 3 - }, - { - "name": "minecraft:stripped_dark_oak_log", - "id": 264, - "data": 0 - }, - { - "name": "minecraft:stripped_dark_oak_log", - "id": 264, - "data": 1 - }, - { - "name": "minecraft:stripped_dark_oak_log", - "id": 264, - "data": 2 - }, - { - "name": "minecraft:stripped_dark_oak_log", - "id": 264, - "data": 3 - }, - { - "name": "minecraft:stripped_oak_log", - "id": 265, - "data": 0 - }, - { - "name": "minecraft:stripped_oak_log", - "id": 265, - "data": 1 - }, - { - "name": "minecraft:stripped_oak_log", - "id": 265, - "data": 2 - }, - { - "name": "minecraft:stripped_oak_log", - "id": 265, - "data": 3 - }, - { - "name": "minecraft:blue_ice", - "id": 266, - "data": 0 - }, - { - "name": "minecraft:element_1", - "id": 267, - "data": 0 - }, - { - "name": "minecraft:element_2", - "id": 268, - "data": 0 - }, - { - "name": "minecraft:element_3", - "id": 269, - "data": 0 - }, - { - "name": "minecraft:element_4", - "id": 270, - "data": 0 - }, - { - "name": "minecraft:element_5", - "id": 271, - "data": 0 - }, - { - "name": "minecraft:element_6", - "id": 272, - "data": 0 - }, - { - "name": "minecraft:element_7", - "id": 273, - "data": 0 - }, - { - "name": "minecraft:element_8", - "id": 274, - "data": 0 - }, - { - "name": "minecraft:element_9", - "id": 275, - "data": 0 - }, - { - "name": "minecraft:element_10", - "id": 276, - "data": 0 - }, - { - "name": "minecraft:element_11", - "id": 277, - "data": 0 - }, - { - "name": "minecraft:element_12", - "id": 278, - "data": 0 - }, - { - "name": "minecraft:element_13", - "id": 279, - "data": 0 - }, - { - "name": "minecraft:element_14", - "id": 280, - "data": 0 - }, - { - "name": "minecraft:element_15", - "id": 281, - "data": 0 - }, - { - "name": "minecraft:element_16", - "id": 282, - "data": 0 - }, - { - "name": "minecraft:element_17", - "id": 283, - "data": 0 - }, - { - "name": "minecraft:element_18", - "id": 284, - "data": 0 - }, - { - "name": "minecraft:element_19", - "id": 285, - "data": 0 - }, - { - "name": "minecraft:element_20", - "id": 286, - "data": 0 - }, - { - "name": "minecraft:element_21", - "id": 287, - "data": 0 - }, - { - "name": "minecraft:element_22", - "id": 288, - "data": 0 - }, - { - "name": "minecraft:element_23", - "id": 289, - "data": 0 - }, - { - "name": "minecraft:element_24", - "id": 290, - "data": 0 - }, - { - "name": "minecraft:element_25", - "id": 291, - "data": 0 - }, - { - "name": "minecraft:element_26", - "id": 292, - "data": 0 - }, - { - "name": "minecraft:element_27", - "id": 293, - "data": 0 - }, - { - "name": "minecraft:element_28", - "id": 294, - "data": 0 - }, - { - "name": "minecraft:element_29", - "id": 295, - "data": 0 - }, - { - "name": "minecraft:element_30", - "id": 296, - "data": 0 - }, - { - "name": "minecraft:element_31", - "id": 297, - "data": 0 - }, - { - "name": "minecraft:element_32", - "id": 298, - "data": 0 - }, - { - "name": "minecraft:element_33", - "id": 299, - "data": 0 - }, - { - "name": "minecraft:element_34", - "id": 300, - "data": 0 - }, - { - "name": "minecraft:element_35", - "id": 301, - "data": 0 - }, - { - "name": "minecraft:element_36", - "id": 302, - "data": 0 - }, - { - "name": "minecraft:element_37", - "id": 303, - "data": 0 - }, - { - "name": "minecraft:element_38", - "id": 304, - "data": 0 - }, - { - "name": "minecraft:element_39", - "id": 305, - "data": 0 - }, - { - "name": "minecraft:element_40", - "id": 306, - "data": 0 - }, - { - "name": "minecraft:element_41", - "id": 307, - "data": 0 - }, - { - "name": "minecraft:element_42", - "id": 308, - "data": 0 - }, - { - "name": "minecraft:element_43", - "id": 309, - "data": 0 - }, - { - "name": "minecraft:element_44", - "id": 310, - "data": 0 - }, - { - "name": "minecraft:element_45", - "id": 311, - "data": 0 - }, - { - "name": "minecraft:element_46", - "id": 312, - "data": 0 - }, - { - "name": "minecraft:element_47", - "id": 313, - "data": 0 - }, - { - "name": "minecraft:element_48", - "id": 314, - "data": 0 - }, - { - "name": "minecraft:element_49", - "id": 315, - "data": 0 - }, - { - "name": "minecraft:element_50", - "id": 316, - "data": 0 - }, - { - "name": "minecraft:element_51", - "id": 317, - "data": 0 - }, - { - "name": "minecraft:element_52", - "id": 318, - "data": 0 - }, - { - "name": "minecraft:element_53", - "id": 319, - "data": 0 - }, - { - "name": "minecraft:element_54", - "id": 320, - "data": 0 - }, - { - "name": "minecraft:element_55", - "id": 321, - "data": 0 - }, - { - "name": "minecraft:element_56", - "id": 322, - "data": 0 - }, - { - "name": "minecraft:element_57", - "id": 323, - "data": 0 - }, - { - "name": "minecraft:element_58", - "id": 324, - "data": 0 - }, - { - "name": "minecraft:element_59", - "id": 325, - "data": 0 - }, - { - "name": "minecraft:element_60", - "id": 326, - "data": 0 - }, - { - "name": "minecraft:element_61", - "id": 327, - "data": 0 - }, - { - "name": "minecraft:element_62", - "id": 328, - "data": 0 - }, - { - "name": "minecraft:element_63", - "id": 329, - "data": 0 - }, - { - "name": "minecraft:element_64", - "id": 330, - "data": 0 - }, - { - "name": "minecraft:element_65", - "id": 331, - "data": 0 - }, - { - "name": "minecraft:element_66", - "id": 332, - "data": 0 - }, - { - "name": "minecraft:element_67", - "id": 333, - "data": 0 - }, - { - "name": "minecraft:element_68", - "id": 334, - "data": 0 - }, - { - "name": "minecraft:element_69", - "id": 335, - "data": 0 - }, - { - "name": "minecraft:element_70", - "id": 336, - "data": 0 - }, - { - "name": "minecraft:element_71", - "id": 337, - "data": 0 - }, - { - "name": "minecraft:element_72", - "id": 338, - "data": 0 - }, - { - "name": "minecraft:element_73", - "id": 339, - "data": 0 - }, - { - "name": "minecraft:element_74", - "id": 340, - "data": 0 - }, - { - "name": "minecraft:element_75", - "id": 341, - "data": 0 - }, - { - "name": "minecraft:element_76", - "id": 342, - "data": 0 - }, - { - "name": "minecraft:element_77", - "id": 343, - "data": 0 - }, - { - "name": "minecraft:element_78", - "id": 344, - "data": 0 - }, - { - "name": "minecraft:element_79", - "id": 345, - "data": 0 - }, - { - "name": "minecraft:element_80", - "id": 346, - "data": 0 - }, - { - "name": "minecraft:element_81", - "id": 347, - "data": 0 - }, - { - "name": "minecraft:element_82", - "id": 348, - "data": 0 - }, - { - "name": "minecraft:element_83", - "id": 349, - "data": 0 - }, - { - "name": "minecraft:element_84", - "id": 350, - "data": 0 - }, - { - "name": "minecraft:element_85", - "id": 351, - "data": 0 - }, - { - "name": "minecraft:element_86", - "id": 352, - "data": 0 - }, - { - "name": "minecraft:element_87", - "id": 353, - "data": 0 - }, - { - "name": "minecraft:element_88", - "id": 354, - "data": 0 - }, - { - "name": "minecraft:element_89", - "id": 355, - "data": 0 - }, - { - "name": "minecraft:element_90", - "id": 356, - "data": 0 - }, - { - "name": "minecraft:element_91", - "id": 357, - "data": 0 - }, - { - "name": "minecraft:element_92", - "id": 358, - "data": 0 - }, - { - "name": "minecraft:element_93", - "id": 359, - "data": 0 - }, - { - "name": "minecraft:element_94", - "id": 360, - "data": 0 - }, - { - "name": "minecraft:element_95", - "id": 361, - "data": 0 - }, - { - "name": "minecraft:element_96", - "id": 362, - "data": 0 - }, - { - "name": "minecraft:element_97", - "id": 363, - "data": 0 - }, - { - "name": "minecraft:element_98", - "id": 364, - "data": 0 - }, - { - "name": "minecraft:element_99", - "id": 365, - "data": 0 - }, - { - "name": "minecraft:element_100", - "id": 366, - "data": 0 - }, - { - "name": "minecraft:element_101", - "id": 367, - "data": 0 - }, - { - "name": "minecraft:element_102", - "id": 368, - "data": 0 - }, - { - "name": "minecraft:element_103", - "id": 369, - "data": 0 - }, - { - "name": "minecraft:element_104", - "id": 370, - "data": 0 - }, - { - "name": "minecraft:element_105", - "id": 371, - "data": 0 - }, - { - "name": "minecraft:element_106", - "id": 372, - "data": 0 - }, - { - "name": "minecraft:element_107", - "id": 373, - "data": 0 - }, - { - "name": "minecraft:element_108", - "id": 374, - "data": 0 - }, - { - "name": "minecraft:element_109", - "id": 375, - "data": 0 - }, - { - "name": "minecraft:element_110", - "id": 376, - "data": 0 - }, - { - "name": "minecraft:element_111", - "id": 377, - "data": 0 - }, - { - "name": "minecraft:element_112", - "id": 378, - "data": 0 - }, - { - "name": "minecraft:element_113", - "id": 379, - "data": 0 - }, - { - "name": "minecraft:element_114", - "id": 380, - "data": 0 - }, - { - "name": "minecraft:element_115", - "id": 381, - "data": 0 - }, - { - "name": "minecraft:element_116", - "id": 382, - "data": 0 - }, - { - "name": "minecraft:element_117", - "id": 383, - "data": 0 - }, - { - "name": "minecraft:element_118", - "id": 384, - "data": 0 - }, - { - "name": "minecraft:seagrass", - "id": 385, - "data": 0 - }, - { - "name": "minecraft:seagrass", - "id": 385, - "data": 1 - }, - { - "name": "minecraft:seagrass", - "id": 385, - "data": 2 - }, - { - "name": "minecraft:seagrass", - "id": 385, - "data": 3 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 0 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 1 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 2 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 3 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 4 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 5 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 6 - }, - { - "name": "minecraft:coral", - "id": 386, - "data": 7 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 0 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 1 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 2 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 3 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 4 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 5 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 6 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 7 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 8 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 9 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 10 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 11 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 12 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 13 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 14 - }, - { - "name": "minecraft:coral_block", - "id": 387, - "data": 15 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 0 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 1 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 2 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 3 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 4 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 5 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 6 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 7 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 8 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 9 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 10 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 11 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 12 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 13 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 14 - }, - { - "name": "minecraft:coral_fan", - "id": 388, - "data": 15 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 0 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 1 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 2 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 3 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 4 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 5 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 6 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 7 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 8 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 9 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 10 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 11 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 12 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 13 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 14 - }, - { - "name": "minecraft:coral_fan_dead", - "id": 389, - "data": 15 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 0 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 1 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 2 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 3 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 4 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 5 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 6 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 7 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 8 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 9 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 10 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 11 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 12 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 13 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 14 - }, - { - "name": "minecraft:coral_fan_hang", - "id": 390, - "data": 15 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 0 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 1 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 2 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 3 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 4 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 5 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 6 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 7 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 8 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 9 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 10 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 11 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 12 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 13 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 14 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": 391, - "data": 15 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 0 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 1 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 2 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 3 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 4 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 5 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 6 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 7 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 8 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 9 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 10 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 11 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 12 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 13 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 14 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": 392, - "data": 15 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 0 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 1 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 2 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 3 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 4 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 5 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 6 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 7 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 8 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 9 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 10 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 11 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 12 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 13 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 14 - }, - { - "name": "minecraft:kelp", - "id": 393, - "data": 15 - }, - { - "name": "minecraft:dried_kelp_block", - "id": 394, - "data": 0 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 0 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 1 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 2 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 3 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 4 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 5 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 6 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 7 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 8 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 9 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 10 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 11 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 12 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 13 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 14 - }, - { - "name": "minecraft:acacia_button", - "id": 395, - "data": 15 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 0 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 1 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 2 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 3 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 4 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 5 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 6 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 7 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 8 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 9 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 10 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 11 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 12 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 13 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 14 - }, - { - "name": "minecraft:birch_button", - "id": 396, - "data": 15 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 0 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 1 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 2 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 3 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 4 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 5 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 6 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 7 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 8 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 9 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 10 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 11 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 12 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 13 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 14 - }, - { - "name": "minecraft:dark_oak_button", - "id": 397, - "data": 15 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 0 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 1 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 2 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 3 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 4 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 5 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 6 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 7 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 8 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 9 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 10 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 11 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 12 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 13 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 14 - }, - { - "name": "minecraft:jungle_button", - "id": 398, - "data": 15 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 0 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 1 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 2 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 3 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 4 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 5 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 6 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 7 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 8 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 9 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 10 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 11 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 12 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 13 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 14 - }, - { - "name": "minecraft:spruce_button", - "id": 399, - "data": 15 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 0 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 1 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 2 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 3 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 4 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 5 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 6 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 7 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 8 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 9 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 10 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 11 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 12 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 13 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 14 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": 400, - "data": 15 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 0 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 1 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 2 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 3 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 4 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 5 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 6 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 7 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 8 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 9 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 10 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 11 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 12 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 13 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 14 - }, - { - "name": "minecraft:birch_trapdoor", - "id": 401, - "data": 15 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 0 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 1 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 2 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 3 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 4 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 5 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 6 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 7 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 8 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 9 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 10 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 11 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 12 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 13 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 14 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": 402, - "data": 15 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 0 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 1 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 2 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 3 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 4 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 5 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 6 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 7 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 8 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 9 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 10 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 11 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 12 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 13 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 14 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": 403, - "data": 15 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 0 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 1 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 2 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 3 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 4 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 5 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 6 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 7 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 8 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 9 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 10 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 11 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 12 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 13 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 14 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": 404, - "data": 15 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 0 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 1 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 2 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 3 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 4 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 5 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 6 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 7 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 8 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 9 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 10 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 11 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 12 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 13 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 14 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": 405, - "data": 15 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 0 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 1 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 2 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 3 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 4 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 5 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 6 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 7 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 8 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 9 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 10 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 11 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 12 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 13 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 14 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": 406, - "data": 15 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 0 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 1 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 2 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 3 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 4 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 5 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 6 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 7 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 8 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 9 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 10 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 11 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 12 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 13 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 14 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": 407, - "data": 15 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 0 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 1 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 2 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 3 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 4 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 5 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 6 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 7 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 8 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 9 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 10 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 11 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 12 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 13 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 14 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": 408, - "data": 15 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 0 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 1 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 2 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 3 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 4 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 5 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 6 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 7 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 8 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 9 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 10 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 11 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 12 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 13 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 14 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": 409, - "data": 15 - }, - { - "name": "minecraft:carved_pumpkin", - "id": 410, - "data": 0 - }, - { - "name": "minecraft:carved_pumpkin", - "id": 410, - "data": 1 - }, - { - "name": "minecraft:carved_pumpkin", - "id": 410, - "data": 2 - }, - { - "name": "minecraft:carved_pumpkin", - "id": 410, - "data": 3 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 0 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 1 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 2 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 3 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 4 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 5 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 6 - }, - { - "name": "minecraft:sea_pickle", - "id": 411, - "data": 7 - }, - { - "name": "minecraft:conduit", - "id": 412, - "data": 0 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 0 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 1 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 2 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 3 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 4 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 5 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 6 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 7 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 8 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 9 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 10 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 11 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 12 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 13 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 14 - }, - { - "name": "minecraft:turtle_egg", - "id": 414, - "data": 15 - }, - { - "name": "minecraft:bubble_column", - "id": 415, - "data": 0 - }, - { - "name": "minecraft:bubble_column", - "id": 415, - "data": 1 - }, - { - "name": "minecraft:barrier", - "id": 416, - "data": 0 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 0 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 1 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 2 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 3 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 4 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 5 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 6 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 7 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 8 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 9 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 10 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 11 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 12 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 13 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 14 - }, - { - "name": "minecraft:stone_slab3", - "id": 417, - "data": 15 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 0 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 1 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 2 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 3 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 4 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 5 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 6 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 7 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 8 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 9 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 10 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 11 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 12 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 13 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 14 - }, - { - "name": "minecraft:bamboo", - "id": 418, - "data": 15 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 0 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 1 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 2 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 3 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 4 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 5 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 6 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 7 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 8 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 9 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 10 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 11 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 12 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 13 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 14 - }, - { - "name": "minecraft:bamboo_sapling", - "id": 419, - "data": 15 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 0 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 1 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 2 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 3 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 4 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 5 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 6 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 7 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 8 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 9 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 10 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 11 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 12 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 13 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 14 - }, - { - "name": "minecraft:scaffolding", - "id": 420, - "data": 15 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 0 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 1 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 2 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 3 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 4 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 5 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 6 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 7 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 8 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 9 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 10 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 11 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 12 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 13 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 14 - }, - { - "name": "minecraft:stone_slab4", - "id": 421, - "data": 15 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 0 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 1 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 2 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 3 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 4 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 5 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 6 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 7 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 8 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 9 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 10 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 11 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 12 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 13 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 14 - }, - { - "name": "minecraft:double_stone_slab3", - "id": 422, - "data": 15 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 0 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 1 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 2 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 3 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 4 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 5 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 6 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 7 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 8 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 9 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 10 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 11 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 12 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 13 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 14 - }, - { - "name": "minecraft:double_stone_slab4", - "id": 423, - "data": 15 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 0 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 1 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 2 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 3 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 4 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 5 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 6 - }, - { - "name": "minecraft:granite_stairs", - "id": 424, - "data": 7 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 0 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 1 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 2 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 3 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 4 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 5 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 6 - }, - { - "name": "minecraft:diorite_stairs", - "id": 425, - "data": 7 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 0 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 1 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 2 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 3 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 4 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 5 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 6 - }, - { - "name": "minecraft:andesite_stairs", - "id": 426, - "data": 7 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 0 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 1 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 2 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 3 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 4 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 5 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 6 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": 427, - "data": 7 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 0 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 1 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 2 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 3 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 4 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 5 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 6 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": 428, - "data": 7 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 0 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 1 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 2 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 3 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 4 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 5 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 6 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": 429, - "data": 7 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 0 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 1 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 2 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 3 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 4 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 5 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 6 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": 430, - "data": 7 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 0 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 1 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 2 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 3 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 4 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 5 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 6 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": 431, - "data": 7 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 0 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 1 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 2 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 3 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 4 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 5 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 6 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": 432, - "data": 7 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 0 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 1 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 2 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 3 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 4 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 5 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 6 - }, - { - "name": "minecraft:end_brick_stairs", - "id": 433, - "data": 7 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 0 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 1 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 2 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 3 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 4 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 5 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 6 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": 434, - "data": 7 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 0 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 1 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 2 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 3 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 4 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 5 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 6 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": 435, - "data": 7 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 0 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 1 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 2 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 3 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 4 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 5 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 6 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 7 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 8 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 9 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 10 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 11 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 12 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 13 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 14 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": 436, - "data": 15 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 0 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 1 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 2 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 3 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 4 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 5 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 6 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": 437, - "data": 7 - }, - { - "name": "minecraft:smooth_stone", - "id": 438, - "data": 0 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 0 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 1 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 2 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 3 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 4 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 5 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 6 - }, - { - "name": "minecraft:red_nether_brick_stairs", - "id": 439, - "data": 7 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 0 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 1 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 2 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 3 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 4 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 5 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 6 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": 440, - "data": 7 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 0 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 1 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 2 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 3 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 4 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 5 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 6 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 7 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 8 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 9 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 10 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 11 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 12 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 13 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 14 - }, - { - "name": "minecraft:birch_standing_sign", - "id": 441, - "data": 15 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 0 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 1 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 2 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 3 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 4 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 5 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 6 - }, - { - "name": "minecraft:birch_wall_sign", - "id": 442, - "data": 7 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 0 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 1 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 2 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 3 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 4 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 5 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 6 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 7 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 8 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 9 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 10 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 11 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 12 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 13 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 14 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": 443, - "data": 15 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 0 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 1 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 2 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 3 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 4 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 5 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 6 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": 444, - "data": 7 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 0 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 1 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 2 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 3 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 4 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 5 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 6 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 7 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 8 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 9 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 10 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 11 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 12 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 13 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 14 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": 445, - "data": 15 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 0 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 1 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 2 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 3 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 4 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 5 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 6 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": 446, - "data": 7 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 0 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 1 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 2 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 3 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 4 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 5 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 6 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 7 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 8 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 9 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 10 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 11 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 12 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 13 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 14 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": 447, - "data": 15 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 0 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 1 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 2 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 3 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 4 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 5 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 6 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": 448, - "data": 7 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 0 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 1 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 2 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 3 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 4 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 5 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 6 - }, - { - "name": "minecraft:lectern", - "id": 449, - "data": 7 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 0 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 1 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 2 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 3 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 4 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 5 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 6 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 7 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 8 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 9 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 10 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 11 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 12 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 13 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 14 - }, - { - "name": "minecraft:grindstone", - "id": 450, - "data": 15 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 0 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 1 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 2 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 3 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 4 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 5 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 6 - }, - { - "name": "minecraft:blast_furnace", - "id": 451, - "data": 7 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 0 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 1 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 2 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 3 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 4 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 5 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 6 - }, - { - "name": "minecraft:stonecutter_block", - "id": 452, - "data": 7 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 0 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 1 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 2 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 3 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 4 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 5 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 6 - }, - { - "name": "minecraft:smoker", - "id": 453, - "data": 7 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 0 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 1 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 2 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 3 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 4 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 5 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 6 - }, - { - "name": "minecraft:lit_smoker", - "id": 454, - "data": 7 - }, - { - "name": "minecraft:cartography_table", - "id": 455, - "data": 0 - }, - { - "name": "minecraft:fletching_table", - "id": 456, - "data": 0 - }, - { - "name": "minecraft:smithing_table", - "id": 457, - "data": 0 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 0 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 1 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 2 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 3 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 4 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 5 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 6 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 7 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 8 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 9 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 10 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 11 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 12 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 13 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 14 - }, - { - "name": "minecraft:barrel", - "id": 458, - "data": 15 - }, - { - "name": "minecraft:loom", - "id": 459, - "data": 0 - }, - { - "name": "minecraft:loom", - "id": 459, - "data": 1 - }, - { - "name": "minecraft:loom", - "id": 459, - "data": 2 - }, - { - "name": "minecraft:loom", - "id": 459, - "data": 3 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 0 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 1 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 2 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 3 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 4 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 5 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 6 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 7 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 8 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 9 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 10 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 11 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 12 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 13 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 14 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 15 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 16 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 17 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 18 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 19 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 20 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 21 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 22 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 23 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 24 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 25 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 26 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 27 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 28 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 29 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 30 - }, - { - "name": "minecraft:bell", - "id": 461, - "data": 31 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 0 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 1 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 2 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 3 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 4 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 5 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 6 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": 462, - "data": 7 - }, - { - "name": "minecraft:lantern", - "id": 463, - "data": 0 - }, - { - "name": "minecraft:lantern", - "id": 463, - "data": 1 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 0 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 1 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 2 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 3 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 4 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 5 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 6 - }, - { - "name": "minecraft:campfire", - "id": 464, - "data": 7 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 0 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 1 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 2 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 3 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 4 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 5 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 6 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 7 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 8 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 9 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 10 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 11 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 12 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 13 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 14 - }, - { - "name": "minecraft:lava_cauldron", - "id": 465, - "data": 15 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 0 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 1 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 2 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 3 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 4 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 5 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 6 - }, - { - "name": "minecraft:jigsaw", - "id": 466, - "data": 7 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 0 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 1 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 2 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 3 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 4 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 5 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 6 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 7 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 8 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 9 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 10 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 11 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 12 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 13 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 14 - }, - { - "name": "minecraft:wood", - "id": 467, - "data": 15 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 0 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 1 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 2 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 3 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 4 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 5 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 6 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 7 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 8 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 9 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 10 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 11 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 12 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 13 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 14 - }, - { - "name": "minecraft:composter", - "id": 468, - "data": 15 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 0 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 1 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 2 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 3 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 4 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 5 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 6 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": 469, - "data": 7 - } -] \ No newline at end of file From 4923c519f3595db79091d1d2d694c3887fa4fff3 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 3 Nov 2019 16:36:46 +0100 Subject: [PATCH 27/52] Fix piston retracting --- .../java/cn/nukkit/block/BlockPistonBase.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index ed85db70c9d..8b8245b1716 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -4,7 +4,6 @@ import cn.nukkit.blockentity.BlockEntity; import cn.nukkit.blockentity.BlockEntityMovingBlock; import cn.nukkit.blockentity.BlockEntityPistonArm; -import cn.nukkit.event.block.BlockPistonChangeEvent; import cn.nukkit.event.block.BlockPistonEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; @@ -16,6 +15,7 @@ import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.LevelSoundEventPacket; import cn.nukkit.utils.Faceable; +import cn.nukkit.utils.MainLogger; import com.google.common.collect.Lists; import java.util.ArrayList; @@ -113,7 +113,7 @@ public int onUpdate(int type) { boolean powered = this.isPowered(); if (arm.powered != powered) { - this.level.getServer().getPluginManager().callEvent(new BlockPistonChangeEvent(this, powered ? 0 : 15, powered ? 15 : 0)); + MainLogger.getLogger().info("should change state"); if (checkState(powered)) { arm.powered = powered; @@ -162,6 +162,7 @@ private boolean checkState(Boolean isPowered) { return false; } } else if (!this.doMove(false)) { + MainLogger.getLogger().info("!move"); return false; } @@ -211,6 +212,7 @@ private boolean doMove(boolean extending) { BlocksCalculator calculator = new BlocksCalculator(extending); if (!calculator.canMove()) { + MainLogger.getLogger().info("!move 2"); return false; } else { List attached = Collections.emptyList(); @@ -302,12 +304,14 @@ public class BlocksCalculator { private Vector3 armPos; private final Block blockToMove; private final BlockFace moveDirection; + private final boolean extending; private final List toMove = new ArrayList<>(); private final List toDestroy = new ArrayList<>(); public BlocksCalculator(boolean extending) { this.pistonPos = getLocation(); + this.extending = extending; BlockFace face = getBlockFace(); if (!extending) { @@ -319,11 +323,19 @@ public BlocksCalculator(boolean extending) { this.blockToMove = getSide(face); } else { this.moveDirection = face.getOpposite(); - this.blockToMove = getSide(face, 2); + if (sticky) { + this.blockToMove = getSide(face, 2); + } else { + this.blockToMove = null; + } } } public boolean canMove() { + if (!sticky && !extending) { + return true; + } + this.toMove.clear(); this.toDestroy.clear(); Block block = this.blockToMove; From 3a96364e4edbe8ebb396efe1bca7d2112f99485a Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sat, 23 Nov 2019 21:39:15 +0100 Subject: [PATCH 28/52] ops --- src/main/java/cn/nukkit/item/ItemBucket.java | 2 +- src/main/java/cn/nukkit/nbt/NBTIO.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/cn/nukkit/item/ItemBucket.java b/src/main/java/cn/nukkit/item/ItemBucket.java index 40311ddb04e..e3c3b7dc1bb 100644 --- a/src/main/java/cn/nukkit/item/ItemBucket.java +++ b/src/main/java/cn/nukkit/item/ItemBucket.java @@ -50,7 +50,7 @@ protected static String getName(int meta) { } } - protected int getDamageByTarget(int target) { + public static int getDamageByTarget(int target) { switch (target) { case 2: case 3: diff --git a/src/main/java/cn/nukkit/nbt/NBTIO.java b/src/main/java/cn/nukkit/nbt/NBTIO.java index e3a16011c01..c9b2b5cea8a 100644 --- a/src/main/java/cn/nukkit/nbt/NBTIO.java +++ b/src/main/java/cn/nukkit/nbt/NBTIO.java @@ -28,7 +28,7 @@ public static CompoundTag putItemHelper(Item item) { } public static CompoundTag putItemHelper(Item item, Integer slot) { - CompoundTag tag = new CompoundTag(null) + CompoundTag tag = new CompoundTag((String) null) .putShort("id", item.getId()) .putByte("Count", item.getCount()) .putShort("Damage", item.getDamage()); From d02f4158a54a9b71e577027154569d62aeef8903 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sat, 23 Nov 2019 21:39:43 +0100 Subject: [PATCH 29/52] Static access --- src/main/java/cn/nukkit/item/ItemBucket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/item/ItemBucket.java b/src/main/java/cn/nukkit/item/ItemBucket.java index e3c3b7dc1bb..876337447f4 100644 --- a/src/main/java/cn/nukkit/item/ItemBucket.java +++ b/src/main/java/cn/nukkit/item/ItemBucket.java @@ -83,7 +83,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, if (targetBlock instanceof BlockAir) { if (target instanceof BlockLiquid && target.getDamage() == 0) { - Item result = Item.get(BUCKET, this.getDamageByTarget(target.getId()), 1); + Item result = Item.get(BUCKET, getDamageByTarget(target.getId()), 1); PlayerBucketFillEvent ev; player.getServer().getPluginManager().callEvent(ev = new PlayerBucketFillEvent(player, block, face, this, result)); if (!ev.isCancelled()) { From 78e98f7eabf45a7c5c8d449bf6c3a108429bb7ca Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 24 Nov 2019 16:05:47 +0100 Subject: [PATCH 30/52] Fix palette --- .../java/cn/nukkit/blockentity/BlockEntityHopper.java | 10 ++++------ src/main/java/cn/nukkit/level/GlobalBlockPalette.java | 9 ++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java index 45efebe2235..7866071df10 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java @@ -166,12 +166,10 @@ public boolean onUpdate() { boolean changed = pushItems(); - if (!changed) { - if (blockEntity == null) { - changed = pickupItems(); - } else { - changed = pullItems(); - } + if (blockEntity instanceof InventoryHolder) { + changed = pullItems() || changed; + } else { + changed = pickupItems() || changed; } if (changed) { diff --git a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java index 3a1844fdb9b..c2d9a2eefb9 100644 --- a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java +++ b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java @@ -33,15 +33,14 @@ public class GlobalBlockPalette { if (stream == null) { throw new AssertionError("Unable to locate block state nbt"); } - CompoundTag tag; + ListTag tag; try { - tag = NBTIO.read(stream); + tag = (ListTag) NBTIO.readNetwork(stream); } catch (IOException e) { throw new AssertionError(e); } - ListTag states = tag.getList("Palette", CompoundTag.class); - for (CompoundTag state : states.getAll()) { + for (CompoundTag state : tag.getAll()) { int id = state.getShort("id"); int meta = state.getShort("meta"); String name = state.getCompound("block").getString("name"); @@ -50,7 +49,7 @@ public class GlobalBlockPalette { state.remove("meta"); // No point in sending this since the client doesn't use it. } try { - BLOCK_PALETTE = NBTIO.write(tag.getList("Palette"), ByteOrder.LITTLE_ENDIAN, true); + BLOCK_PALETTE = NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN, true); } catch (IOException e) { throw new AssertionError(e); } From bd8f5bc8ae8a5c69263b3ed5e4205b270aa72997 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sat, 30 Nov 2019 11:56:19 +0100 Subject: [PATCH 31/52] ops --- src/main/java/cn/nukkit/Player.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index 9cb296f0061..63e08b65246 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -2688,7 +2688,17 @@ public void onCompletion(Server server) { this.server.getPluginManager().callEvent(pickEvent); if (!pickEvent.isCancelled()) { - this.inventory.setItemInHand(pickEvent.getItem()); + boolean itemExists = false; + for (int slot = 0; slot != this.inventory.getHotbarSize(); slot++) { + if (this.inventory.getItem(slot).equals(pickEvent.getItem())) { + this.inventory.setHeldItemSlot(slot); + itemExists = true; + break; + } + } + if (!itemExists) { + this.inventory.setItemInHand(pickEvent.getItem()); + } } break; case ProtocolInfo.ANIMATE_PACKET: From b9dd554dc9d77b6262b903aa573bc2ecbd0d3329 Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Sat, 30 Nov 2019 23:53:02 +0000 Subject: [PATCH 32/52] Remove debug message left by @Creeperface01 :) --- src/main/java/cn/nukkit/block/BlockPistonBase.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 8b8245b1716..e1786e0a637 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -112,15 +112,11 @@ public int onUpdate(int type) { BlockEntityPistonArm arm = (BlockEntityPistonArm) blockEntity; boolean powered = this.isPowered(); - if (arm.powered != powered) { - MainLogger.getLogger().info("should change state"); + if (arm.powered != powered && checkState(powered)) { + arm.powered = powered; - if (checkState(powered)) { - arm.powered = powered; - - if (arm.chunk != null) { - arm.chunk.setChanged(); - } + if (arm.chunk != null) { + arm.chunk.setChanged(); } } } From 180fefec5d922399e31b23a142293961546d9aaf Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Mon, 9 Dec 2019 10:31:56 +0100 Subject: [PATCH 33/52] Fix redstone wire placement --- src/main/java/cn/nukkit/block/BlockRedstoneWire.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java index 3747a0a47c3..e752518b5ae 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java @@ -46,10 +46,9 @@ public int getId() { public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { if (target.canBeReplaced()) { block = target; - target = target.down(); } - if (!canBePlacedOn(target)) { + if (!canBePlacedOn(block.down())) { return false; } @@ -235,7 +234,7 @@ public int onUpdate(int type) { } public boolean canBePlacedOn(Block b) { - return b.isSolid() && !b.isTransparent() && b.getId() != Block.GLOWSTONE; + return b.isSolid() && (!b.isTransparent() || b.getId() == Block.GLOWSTONE); } public int getStrongPower(BlockFace side) { From 7bab7c73412f3c504a12db43de138fce6470355c Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Mon, 9 Dec 2019 15:57:54 +0100 Subject: [PATCH 34/52] Fix block names --- src/main/java/cn/nukkit/block/BlockPistonBase.java | 3 --- src/main/java/cn/nukkit/level/GlobalBlockPalette.java | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index e1786e0a637..4f4753b0174 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -15,7 +15,6 @@ import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.LevelSoundEventPacket; import cn.nukkit.utils.Faceable; -import cn.nukkit.utils.MainLogger; import com.google.common.collect.Lists; import java.util.ArrayList; @@ -158,7 +157,6 @@ private boolean checkState(Boolean isPowered) { return false; } } else if (!this.doMove(false)) { - MainLogger.getLogger().info("!move"); return false; } @@ -208,7 +206,6 @@ private boolean doMove(boolean extending) { BlocksCalculator calculator = new BlocksCalculator(extending); if (!calculator.canMove()) { - MainLogger.getLogger().info("!move 2"); return false; } else { List attached = Collections.emptyList(); diff --git a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java index be0ff0853be..319af7b24fd 100644 --- a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java +++ b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java @@ -13,7 +13,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; @@ -85,7 +84,7 @@ public static int getLegacyId(int runtimeId) { } public static String getName(int id, int meta) { - return legacyIdToString.get((id << 4) | meta); + return legacyIdToString.get((id << 6) | meta); } private static int registerMapping(int legacyId, String name) { From a79a8610cf203d5a66413c1bd48872e517aa005d Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Mon, 9 Dec 2019 18:29:07 +0100 Subject: [PATCH 35/52] ops --- .../java/cn/nukkit/block/BlockPistonBase.java | 2 +- .../cn/nukkit/level/GlobalBlockPalette.java | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 4f4753b0174..267bddee068 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -246,7 +246,7 @@ private boolean doMove(boolean extending) { .putInt("id", newBlock.getId()) //only for nukkit purpose .putInt("meta", newBlock.getDamage()) //only for nukkit purpose .putShort("val", newBlock.getDamage()) - .putString("name", GlobalBlockPalette.getName(newBlock.getId(), newBlock.getDamage())) + .putString("name", GlobalBlockPalette.getName(newBlock.getId())) ); if (blockEntity != null && !(blockEntity instanceof BlockEntityMovingBlock)) { diff --git a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java index 319af7b24fd..e2c4f779116 100644 --- a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java +++ b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java @@ -54,12 +54,12 @@ public class GlobalBlockPalette { // Resolve to first legacy id runtimeIdToLegacy.put(runtimeId, id << 6 | meta[0]); - stringToLegacyId.put(name, id << 6 | meta[0]); + stringToLegacyId.put(name, id); + legacyIdToString.put(id, name); for (int val : meta) { int legacyId = id << 6 | val; legacyToRuntimeId.put(legacyId, runtimeId); - legacyIdToString.put(legacyId, name); } state.remove("meta"); // No point in sending this since the client doesn't use it. } @@ -83,16 +83,7 @@ public static int getLegacyId(int runtimeId) { return runtimeIdToLegacy.get(runtimeId); } - public static String getName(int id, int meta) { - return legacyIdToString.get((id << 6) | meta); - } - - private static int registerMapping(int legacyId, String name) { - int runtimeId = runtimeIdAllocator.getAndIncrement(); - runtimeIdToLegacy.put(runtimeId, legacyId); - legacyIdToString.put(legacyId, name); - stringToLegacyId.put(name, legacyId); - legacyToRuntimeId.put(legacyId, runtimeId); - return runtimeId; + public static String getName(int id) { + return legacyIdToString.get(id); } } From 294d397eb51e0dead3203c0e36707718528d9ba4 Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Tue, 10 Dec 2019 16:47:32 +0000 Subject: [PATCH 36/52] Bump protocol version to 389 This works but we need to update the block palette. --- src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java index af9e121aca3..2aea64ce0d1 100644 --- a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java +++ b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java @@ -14,12 +14,12 @@ public interface ProtocolInfo { * Actual Minecraft: PE protocol version */ @SuppressWarnings("UnnecessaryBoxing") - int CURRENT_PROTOCOL = Integer.valueOf("388"); // DO NOT REMOVE BOXING + int CURRENT_PROTOCOL = Integer.valueOf("389"); // DO NOT REMOVE BOXING List SUPPORTED_PROTOCOLS = Ints.asList(CURRENT_PROTOCOL); - String MINECRAFT_VERSION = "v1.13.0"; - String MINECRAFT_VERSION_NETWORK = "1.13.0"; + String MINECRAFT_VERSION = "v1.14.0"; + String MINECRAFT_VERSION_NETWORK = "1.14.0"; byte LOGIN_PACKET = 0x01; byte PLAY_STATUS_PACKET = 0x02; From bb3aad2d025b05898bebee5ea209406f40d322ab Mon Sep 17 00:00:00 2001 From: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Date: Tue, 10 Dec 2019 20:24:22 +0000 Subject: [PATCH 37/52] Support 1.13 and 1.14 They are the same apart from the block palette which does not cause issues because we don't use any new blocks. --- src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java index 2aea64ce0d1..43f36e4c4eb 100644 --- a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java +++ b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java @@ -16,7 +16,7 @@ public interface ProtocolInfo { @SuppressWarnings("UnnecessaryBoxing") int CURRENT_PROTOCOL = Integer.valueOf("389"); // DO NOT REMOVE BOXING - List SUPPORTED_PROTOCOLS = Ints.asList(CURRENT_PROTOCOL); + List SUPPORTED_PROTOCOLS = Ints.asList(CURRENT_PROTOCOL, 388); String MINECRAFT_VERSION = "v1.14.0"; String MINECRAFT_VERSION_NETWORK = "1.14.0"; From 288724c434212e135b088b9077470d261ec6bcf8 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 15 Dec 2019 11:48:56 +0100 Subject: [PATCH 38/52] Fix comparator immediate update --- .../cn/nukkit/block/BlockRedstoneComparator.java | 1 - .../java/cn/nukkit/block/BlockRedstoneDiode.java | 14 +++++++------- .../nukkit/dispenser/DispenseBehaviorRegister.java | 2 +- .../cn/nukkit/scheduler/BlockUpdateScheduler.java | 7 ++++++- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java b/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java index c1fb51c94c6..a4fd3ee4d9e 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java @@ -69,7 +69,6 @@ public void updateState() { this.level.scheduleUpdate(this, this, 2, 0); }*/ - //System.out.println("schedule update 0"); this.level.scheduleUpdate(this, this, 2); } } diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java index 795c6c6f0d1..8b98a9d5e1b 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java @@ -75,22 +75,22 @@ public int onUpdate(int type) { this.level.updateAroundRedstone(this.getLocation().getSide(getFacing().getOpposite()), null); if (!shouldBePowered) { -// System.out.println("schedule update 2"); level.scheduleUpdate(getPowered(), this, this.getDelay()); } } } } else if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) { - // Redstone event - RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this); - getLevel().getServer().getPluginManager().callEvent(ev); - if (ev.isCancelled()) { - return 0; - } if (type == Level.BLOCK_UPDATE_NORMAL && this.getSide(BlockFace.DOWN).isTransparent()) { this.level.useBreakOn(this); return Level.BLOCK_UPDATE_NORMAL; } else if (this.level.getServer().isRedstoneEnabled()) { + // Redstone event + RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this); + getLevel().getServer().getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + return 0; + } + this.updateState(); return Level.BLOCK_UPDATE_NORMAL; } diff --git a/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java b/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java index 049264c3013..1209b7cced7 100644 --- a/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java +++ b/src/main/java/cn/nukkit/dispenser/DispenseBehaviorRegister.java @@ -38,7 +38,7 @@ public static void init() { registerBehavior(ItemID.ARROW, new ProjectileDispenseBehavior("Arrow") { @Override protected double getMotion() { - return super.getMotion() * 2; + return super.getMotion() * 1.5; } }); //TODO: tipped arrow diff --git a/src/main/java/cn/nukkit/scheduler/BlockUpdateScheduler.java b/src/main/java/cn/nukkit/scheduler/BlockUpdateScheduler.java index bb1a26a24b6..150f3043251 100644 --- a/src/main/java/cn/nukkit/scheduler/BlockUpdateScheduler.java +++ b/src/main/java/cn/nukkit/scheduler/BlockUpdateScheduler.java @@ -48,11 +48,16 @@ private void perform(long tick) { lastTick = tick; Set updates = pendingUpdates = queuedUpdates.remove(tick); if (updates != null) { - for (BlockUpdateEntry entry : updates) { + Iterator updateIterator = updates.iterator(); + + while (updateIterator.hasNext()) { + BlockUpdateEntry entry = updateIterator.next(); + Vector3 pos = entry.pos; if (level.isChunkLoaded(NukkitMath.floorDouble(pos.x) >> 4, NukkitMath.floorDouble(pos.z) >> 4)) { Block block = level.getBlock(entry.pos); + updateIterator.remove(); if (Block.equals(block, entry.block, false)) { block.onUpdate(Level.BLOCK_UPDATE_SCHEDULED); } From 8e8d63b01ca1b89184e95f829860692479ca20fb Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 15 Dec 2019 18:50:36 +0100 Subject: [PATCH 39/52] Fix block updates --- src/main/java/cn/nukkit/level/Level.java | 54 ++++++------------------ 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index 5f7346b4e3f..52cbbba5f1f 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -186,7 +186,7 @@ public int size() { private final BlockUpdateScheduler updateQueue; - private final Queue normalUpdateQueue = new ConcurrentLinkedDeque<>(); + private final Queue normalUpdateQueue = new ConcurrentLinkedDeque<>(); // private final TreeSet updateQueue = new TreeSet<>(); // private final List nextTickUpdates = Lists.newArrayList(); //private final Map updateQueueIndex = new HashMap<>(); @@ -767,9 +767,14 @@ public void doTick(int currentTick) { this.updateQueue.tick(this.getCurrentTick()); this.timings.doTickPending.stopTiming(); - Block block; - while ((block = this.normalUpdateQueue.poll()) != null) { - block.onUpdate(BLOCK_UPDATE_NORMAL); + while (!this.normalUpdateQueue.isEmpty()) { + Block block = getBlock(this.normalUpdateQueue.poll()); + BlockUpdateEvent event = new BlockUpdateEvent(block); + this.server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + block.onUpdate(BLOCK_UPDATE_NORMAL); + } } TimingsHistory.entityTicks += this.updateEntities.size(); @@ -1196,46 +1201,13 @@ public void updateComparatorOutputLevel(Vector3 v) { } public void updateAround(Vector3 pos) { - updateAround((int) pos.x, (int) pos.y, (int) pos.z); + for (BlockFace face : BlockFace.values()) { + normalUpdateQueue.add(pos.getSide(face)); + } } public void updateAround(int x, int y, int z) { - BlockUpdateEvent ev; - this.server.getPluginManager().callEvent( - ev = new BlockUpdateEvent(this.getBlock(x, y - 1, z))); - if (!ev.isCancelled()) { - normalUpdateQueue.add(ev.getBlock()); - } - - this.server.getPluginManager().callEvent( - ev = new BlockUpdateEvent(this.getBlock(x, y + 1, z))); - if (!ev.isCancelled()) { - normalUpdateQueue.add(ev.getBlock()); - } - - this.server.getPluginManager().callEvent( - ev = new BlockUpdateEvent(this.getBlock(x - 1, y, z))); - if (!ev.isCancelled()) { - normalUpdateQueue.add(ev.getBlock()); - } - - this.server.getPluginManager().callEvent( - ev = new BlockUpdateEvent(this.getBlock(x + 1, y, z))); - if (!ev.isCancelled()) { - normalUpdateQueue.add(ev.getBlock()); - } - - this.server.getPluginManager().callEvent( - ev = new BlockUpdateEvent(this.getBlock(x, y, z - 1))); - if (!ev.isCancelled()) { - normalUpdateQueue.add(ev.getBlock()); - } - - this.server.getPluginManager().callEvent( - ev = new BlockUpdateEvent(this.getBlock(x, y, z + 1))); - if (!ev.isCancelled()) { - normalUpdateQueue.add(ev.getBlock()); - } + updateAround(new Vector3(x, y, z)); } public void scheduleUpdate(Block pos, int delay) { From 65d96c6f46556f71fba5f60130f8ae4dfe16b60f Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 15 Dec 2019 19:12:26 +0100 Subject: [PATCH 40/52] don't push extended pistons --- src/main/java/cn/nukkit/blockentity/BlockEntity.java | 2 +- src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java index c29e0aa034c..6178640767e 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java @@ -55,7 +55,7 @@ public abstract class BlockEntity extends Position { public String name; public long id; - public boolean movable = true; + public boolean movable; public boolean closed = false; public CompoundTag namedTag; diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index 4479b5940b4..64345656111 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -182,11 +182,11 @@ public boolean onUpdate() { if (!extending && this.level.getBlock(getSide(facing)).getId() == BlockID.PISTON_HEAD) { this.level.setBlock(getSide(facing), new BlockAir()); + this.movable = true; } this.level.scheduleUpdate(this.getLevelBlock(), 1); this.attachedBlocks.clear(); - this.movable = true; hasUpdate = false; } From 7f9086d9e8b7e38d1ba6f9e8d596f2fd73537861 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sat, 21 Dec 2019 09:49:44 +0100 Subject: [PATCH 41/52] Fix pistons behavior --- src/main/java/cn/nukkit/block/Block.java | 12 ++ .../java/cn/nukkit/block/BlockBanner.java | 5 + .../java/cn/nukkit/block/BlockBeacon.java | 5 + src/main/java/cn/nukkit/block/BlockBed.java | 10 + .../java/cn/nukkit/block/BlockBedrock.java | 5 + .../nukkit/block/BlockBedrockInvisible.java | 5 + .../java/cn/nukkit/block/BlockCactus.java | 14 +- src/main/java/cn/nukkit/block/BlockCake.java | 10 + .../cn/nukkit/block/BlockChorusFlower.java | 10 + .../cn/nukkit/block/BlockChorusPlant.java | 10 + src/main/java/cn/nukkit/block/BlockCocoa.java | 10 + src/main/java/cn/nukkit/block/BlockDoor.java | 14 +- .../java/cn/nukkit/block/BlockDragonEgg.java | 11 ++ .../java/cn/nukkit/block/BlockEndPortal.java | 5 - .../cn/nukkit/block/BlockEndPortalFrame.java | 5 + .../java/cn/nukkit/block/BlockEndRod.java | 5 - .../java/cn/nukkit/block/BlockEnderChest.java | 5 + .../java/cn/nukkit/block/BlockFlowable.java | 10 + .../java/cn/nukkit/block/BlockItemFrame.java | 10 + .../java/cn/nukkit/block/BlockLadder.java | 12 +- .../java/cn/nukkit/block/BlockLeaves.java | 10 + .../java/cn/nukkit/block/BlockLiquid.java | 10 + .../java/cn/nukkit/block/BlockMobSpawner.java | 5 + .../java/cn/nukkit/block/BlockMoving.java | 12 +- .../cn/nukkit/block/BlockNetherPortal.java | 5 - .../java/cn/nukkit/block/BlockObsidian.java | 5 + .../cn/nukkit/block/BlockObsidianGlowing.java | 5 + .../java/cn/nukkit/block/BlockPistonBase.java | 186 ++++++++---------- .../java/cn/nukkit/block/BlockPistonHead.java | 10 + .../java/cn/nukkit/block/BlockPumpkin.java | 7 +- src/main/java/cn/nukkit/block/BlockRail.java | 10 + .../java/cn/nukkit/block/BlockSignPost.java | 5 + src/main/java/cn/nukkit/block/BlockSkull.java | 9 + .../java/cn/nukkit/block/BlockTripWire.java | 2 +- .../cn/nukkit/block/BlockTripWireHook.java | 2 +- .../nukkit/block/BlockUndyedShulkerBox.java | 10 + src/main/java/cn/nukkit/block/BlockVine.java | 10 + src/main/java/cn/nukkit/level/Level.java | 2 +- 38 files changed, 354 insertions(+), 124 deletions(-) diff --git a/src/main/java/cn/nukkit/block/Block.java b/src/main/java/cn/nukkit/block/Block.java index 3e171f936a6..96a8636a4e8 100644 --- a/src/main/java/cn/nukkit/block/Block.java +++ b/src/main/java/cn/nukkit/block/Block.java @@ -487,6 +487,18 @@ public boolean canBePushed() { return true; } + public boolean canBePulled() { + return true; + } + + public boolean breaksWhenMoved() { + return false; + } + + public boolean sticksToPiston() { + return true; + } + public boolean hasComparatorInputOverride() { return false; } diff --git a/src/main/java/cn/nukkit/block/BlockBanner.java b/src/main/java/cn/nukkit/block/BlockBanner.java index 18a7d026aa4..c5b44163394 100644 --- a/src/main/java/cn/nukkit/block/BlockBanner.java +++ b/src/main/java/cn/nukkit/block/BlockBanner.java @@ -121,4 +121,9 @@ public Item toItem() { public BlockFace getBlockFace() { return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7); } + + @Override + public boolean breaksWhenMoved() { + return true; + } } diff --git a/src/main/java/cn/nukkit/block/BlockBeacon.java b/src/main/java/cn/nukkit/block/BlockBeacon.java index 2a05e2aa59a..7bc4dfc715a 100644 --- a/src/main/java/cn/nukkit/block/BlockBeacon.java +++ b/src/main/java/cn/nukkit/block/BlockBeacon.java @@ -94,4 +94,9 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl public boolean canBePushed() { return false; } + + @Override + public boolean canBePulled() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockBed.java b/src/main/java/cn/nukkit/block/BlockBed.java index e7859058070..1f05cd48919 100644 --- a/src/main/java/cn/nukkit/block/BlockBed.java +++ b/src/main/java/cn/nukkit/block/BlockBed.java @@ -197,4 +197,14 @@ public DyeColor getDyeColor() { public BlockFace getBlockFace() { return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7); } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockBedrock.java b/src/main/java/cn/nukkit/block/BlockBedrock.java index 73e75f6ab2e..1f9392b5c87 100644 --- a/src/main/java/cn/nukkit/block/BlockBedrock.java +++ b/src/main/java/cn/nukkit/block/BlockBedrock.java @@ -41,6 +41,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java b/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java index c952d198d85..12d43cb4763 100644 --- a/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java +++ b/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java @@ -47,6 +47,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + @Override public Item toItem() { return new ItemBlock(new BlockAir()); diff --git a/src/main/java/cn/nukkit/block/BlockCactus.java b/src/main/java/cn/nukkit/block/BlockCactus.java index 1112a0b55b0..561543fb9dd 100644 --- a/src/main/java/cn/nukkit/block/BlockCactus.java +++ b/src/main/java/cn/nukkit/block/BlockCactus.java @@ -152,11 +152,21 @@ public String getName() { public BlockColor getColor() { return BlockColor.FOLIAGE_BLOCK_COLOR; } - + @Override public Item[] getDrops(Item item) { return new Item[]{ - Item.get(Item.CACTUS, 0, 1) + Item.get(Item.CACTUS, 0, 1) }; } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockCake.java b/src/main/java/cn/nukkit/block/BlockCake.java index 762a64cd8f2..d68ead94955 100644 --- a/src/main/java/cn/nukkit/block/BlockCake.java +++ b/src/main/java/cn/nukkit/block/BlockCake.java @@ -136,4 +136,14 @@ public int getComparatorInputOverride() { public boolean hasComparatorInputOverride() { return true; } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockChorusFlower.java b/src/main/java/cn/nukkit/block/BlockChorusFlower.java index d1008d947d4..5d941ead53f 100644 --- a/src/main/java/cn/nukkit/block/BlockChorusFlower.java +++ b/src/main/java/cn/nukkit/block/BlockChorusFlower.java @@ -37,4 +37,14 @@ public int getToolType() { public Item[] getDrops(Item item) { return new Item[0]; } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockChorusPlant.java b/src/main/java/cn/nukkit/block/BlockChorusPlant.java index 39d15fcfcd6..11e806fb7ac 100644 --- a/src/main/java/cn/nukkit/block/BlockChorusPlant.java +++ b/src/main/java/cn/nukkit/block/BlockChorusPlant.java @@ -37,4 +37,14 @@ public int getToolType() { public Item[] getDrops(Item item) { return new Item[0]; } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockCocoa.java b/src/main/java/cn/nukkit/block/BlockCocoa.java index 616297b31f4..e6da6f6621d 100644 --- a/src/main/java/cn/nukkit/block/BlockCocoa.java +++ b/src/main/java/cn/nukkit/block/BlockCocoa.java @@ -244,4 +244,14 @@ public Item[] getDrops(Item item) { public BlockFace getBlockFace() { return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07); } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockDoor.java b/src/main/java/cn/nukkit/block/BlockDoor.java index a3294199fb9..85cffbb8a5e 100644 --- a/src/main/java/cn/nukkit/block/BlockDoor.java +++ b/src/main/java/cn/nukkit/block/BlockDoor.java @@ -334,13 +334,23 @@ public boolean isTop(int meta) { public boolean isRightHinged() { if (isTop()) { - return (this.getDamage() & DOOR_HINGE_BIT ) >0; + return (this.getDamage() & DOOR_HINGE_BIT) > 0; } - return (this.up().getDamage() & DOOR_HINGE_BIT) >0; + return (this.up().getDamage() & DOOR_HINGE_BIT) > 0; } @Override public BlockFace getBlockFace() { return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07); } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockDragonEgg.java b/src/main/java/cn/nukkit/block/BlockDragonEgg.java index a8cb7376ad0..9c9a0f30f38 100644 --- a/src/main/java/cn/nukkit/block/BlockDragonEgg.java +++ b/src/main/java/cn/nukkit/block/BlockDragonEgg.java @@ -3,6 +3,7 @@ import cn.nukkit.level.Level; import cn.nukkit.network.protocol.LevelEventPacket; import cn.nukkit.utils.BlockColor; + import java.util.concurrent.ThreadLocalRandom; public class BlockDragonEgg extends BlockFallable { @@ -73,4 +74,14 @@ public void teleport() { } } } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockEndPortal.java b/src/main/java/cn/nukkit/block/BlockEndPortal.java index e342a7c5744..907ba0b8c4b 100644 --- a/src/main/java/cn/nukkit/block/BlockEndPortal.java +++ b/src/main/java/cn/nukkit/block/BlockEndPortal.java @@ -59,11 +59,6 @@ public BlockColor getColor() { return BlockColor.AIR_BLOCK_COLOR; } - @Override - public boolean canBePushed() { - return false; - } - @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java b/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java index f6e2ac9aaf0..615bd39fd67 100644 --- a/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java +++ b/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java @@ -60,6 +60,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + public boolean hasComparatorInputOverride() { return true; } diff --git a/src/main/java/cn/nukkit/block/BlockEndRod.java b/src/main/java/cn/nukkit/block/BlockEndRod.java index 2c4da5a0106..111e7f7dd6d 100644 --- a/src/main/java/cn/nukkit/block/BlockEndRod.java +++ b/src/main/java/cn/nukkit/block/BlockEndRod.java @@ -47,11 +47,6 @@ public int getLightLevel() { return 14; } - @Override - public boolean canBePushed() { - return true; - } - @Override public int getToolType() { return ItemTool.TYPE_PICKAXE; diff --git a/src/main/java/cn/nukkit/block/BlockEnderChest.java b/src/main/java/cn/nukkit/block/BlockEnderChest.java index 9c5c1558602..c35166a11eb 100644 --- a/src/main/java/cn/nukkit/block/BlockEnderChest.java +++ b/src/main/java/cn/nukkit/block/BlockEnderChest.java @@ -175,6 +175,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockFlowable.java b/src/main/java/cn/nukkit/block/BlockFlowable.java index a0f2d7dd5b8..ea80adc1907 100644 --- a/src/main/java/cn/nukkit/block/BlockFlowable.java +++ b/src/main/java/cn/nukkit/block/BlockFlowable.java @@ -37,6 +37,16 @@ public boolean isSolid() { return false; } + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } + @Override protected AxisAlignedBB recalculateBoundingBox() { return null; diff --git a/src/main/java/cn/nukkit/block/BlockItemFrame.java b/src/main/java/cn/nukkit/block/BlockItemFrame.java index 28ad574eacc..94e32c2d8ae 100644 --- a/src/main/java/cn/nukkit/block/BlockItemFrame.java +++ b/src/main/java/cn/nukkit/block/BlockItemFrame.java @@ -180,4 +180,14 @@ public BlockFace getFacing() { public double getHardness() { return 0.25; } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockLadder.java b/src/main/java/cn/nukkit/block/BlockLadder.java index 014ac90b437..b0e2206e3f4 100644 --- a/src/main/java/cn/nukkit/block/BlockLadder.java +++ b/src/main/java/cn/nukkit/block/BlockLadder.java @@ -176,7 +176,7 @@ public BlockColor getColor() { @Override public Item[] getDrops(Item item) { return new Item[]{ - Item.get(Item.LADDER, 0, 1) + Item.get(Item.LADDER, 0, 1) }; } @@ -184,4 +184,14 @@ public Item[] getDrops(Item item) { public BlockFace getBlockFace() { return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07); } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockLeaves.java b/src/main/java/cn/nukkit/block/BlockLeaves.java index ab1dc1b1f86..87fdd0a65fc 100644 --- a/src/main/java/cn/nukkit/block/BlockLeaves.java +++ b/src/main/java/cn/nukkit/block/BlockLeaves.java @@ -231,4 +231,14 @@ protected boolean canDropApple() { protected Item getSapling() { return Item.get(BlockID.SAPLING, this.getDamage() & 0x03); } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockLiquid.java b/src/main/java/cn/nukkit/block/BlockLiquid.java index bb84a612d32..809e603b396 100644 --- a/src/main/java/cn/nukkit/block/BlockLiquid.java +++ b/src/main/java/cn/nukkit/block/BlockLiquid.java @@ -445,4 +445,14 @@ protected boolean canFlowInto(Block block) { public Item toItem() { return new ItemBlock(new BlockAir()); } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockMobSpawner.java b/src/main/java/cn/nukkit/block/BlockMobSpawner.java index 766b662146b..79ecb657346 100644 --- a/src/main/java/cn/nukkit/block/BlockMobSpawner.java +++ b/src/main/java/cn/nukkit/block/BlockMobSpawner.java @@ -46,6 +46,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockMoving.java b/src/main/java/cn/nukkit/block/BlockMoving.java index 8b385b6a91c..637e37dafec 100644 --- a/src/main/java/cn/nukkit/block/BlockMoving.java +++ b/src/main/java/cn/nukkit/block/BlockMoving.java @@ -2,7 +2,7 @@ import cn.nukkit.item.Item; -public class BlockMoving extends Block { +public class BlockMoving extends BlockTransparent { public BlockMoving() { this(0); @@ -27,6 +27,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + @Override public boolean isBreakable(Item item) { return false; @@ -36,4 +41,9 @@ public boolean isBreakable(Item item) { public boolean canPassThrough() { return true; } + + @Override + public boolean isSolid() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockNetherPortal.java b/src/main/java/cn/nukkit/block/BlockNetherPortal.java index 0d1bb57a229..421e4f2ac1e 100644 --- a/src/main/java/cn/nukkit/block/BlockNetherPortal.java +++ b/src/main/java/cn/nukkit/block/BlockNetherPortal.java @@ -83,11 +83,6 @@ public BlockColor getColor() { return BlockColor.AIR_BLOCK_COLOR; } - @Override - public boolean canBePushed() { - return false; - } - @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockObsidian.java b/src/main/java/cn/nukkit/block/BlockObsidian.java index 7ac74925ae3..bda2b6ec862 100644 --- a/src/main/java/cn/nukkit/block/BlockObsidian.java +++ b/src/main/java/cn/nukkit/block/BlockObsidian.java @@ -75,6 +75,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java b/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java index 70b5098266b..8bedb955c2c 100644 --- a/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java +++ b/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java @@ -64,6 +64,11 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + @Override public boolean canHarvestWithHand() { return false; diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 267bddee068..13722fa2576 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -129,34 +129,19 @@ private boolean checkState(Boolean isPowered) { return false; } - BlockFace facing = getBlockFace(); if (isPowered == null) { isPowered = this.isPowered(); } if (isPowered && !isExtended()) { - BlocksCalculator calculator = new BlocksCalculator(true); - if (calculator.canMove()) { - if (!this.doMove(true)) { - return false; - } - - this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_PISTON_OUT); - return true; + if (!this.doMove(true)) { + return false; } - } else if (!isPowered && isExtended()) { - if (this.sticky) { - Vector3 pos = this.add(0).getSide(facing, 2); - Block block = this.level.getBlock(pos); - if (canPush(block, facing.getOpposite(), false) && (!(block instanceof BlockFlowable) || block.getId() == PISTON || block.getId() == STICKY_PISTON)) { - if (!this.doMove(false)) { - return false; - } - } else { - return false; - } - } else if (!this.doMove(false)) { + this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_PISTON_OUT); + return true; + } else if (!isPowered && isExtended()) { + if (!this.doMove(false)) { return false; } @@ -186,102 +171,97 @@ private boolean isPowered() { } } - if (this.level.isSidePowered(this, BlockFace.DOWN)) { - return true; - } else { - Vector3 pos = this.getLocation().up(); - - for (BlockFace side : BlockFace.values()) { - if (side != BlockFace.DOWN && this.level.isSidePowered(pos.getSide(side), side)) { - return true; - } - } - - return false; - } + return false; } private boolean doMove(boolean extending) { BlockFace direction = getBlockFace(); BlocksCalculator calculator = new BlocksCalculator(extending); - if (!calculator.canMove()) { + boolean canMove = calculator.canMove(); + + if (!canMove && extending) { return false; - } else { - List attached = Collections.emptyList(); + } - BlockPistonEvent event = new BlockPistonEvent(this, direction, calculator.getBlocksToMove(), calculator.getBlocksToDestroy(), extending); - this.level.getServer().getPluginManager().callEvent(event); + List attached = Collections.emptyList(); - if (event.isCancelled()) { - return false; - } + BlockPistonEvent event = new BlockPistonEvent(this, direction, calculator.getBlocksToMove(), calculator.getBlocksToDestroy(), extending); + this.level.getServer().getPluginManager().callEvent(event); - if (this.sticky || extending) { - List destroyBlocks = calculator.getBlocksToDestroy(); - for (int i = destroyBlocks.size() - 1; i >= 0; --i) { - Block block = destroyBlocks.get(i); - this.level.useBreakOn(block, null, null, false); - } - - List newBlocks = calculator.getBlocksToMove(); + if (event.isCancelled()) { + return false; + } - attached = newBlocks.stream().map(Vector3::asBlockVector3).collect(Collectors.toList()); + if (canMove && (this.sticky || extending)) { + List destroyBlocks = calculator.getBlocksToDestroy(); + for (int i = destroyBlocks.size() - 1; i >= 0; --i) { + Block block = destroyBlocks.get(i); + this.level.useBreakOn(block, null, null, false); + } - BlockFace side = extending ? direction : direction.getOpposite(); + List newBlocks = calculator.getBlocksToMove(); - for (Block newBlock : newBlocks) { - Vector3 oldPos = newBlock.add(0); - newBlock.position(newBlock.add(0).getSide(side)); + attached = newBlocks.stream().map(Vector3::asBlockVector3).collect(Collectors.toList()); - BlockEntity blockEntity = this.level.getBlockEntity(oldPos); + BlockFace side = extending ? direction : direction.getOpposite(); - this.level.setBlock(newBlock, Block.get(BlockID.MOVING_BLOCK), true); + for (Block newBlock : newBlocks) { + Vector3 oldPos = newBlock.add(0); + newBlock.position(newBlock.add(0).getSide(side)); - CompoundTag nbt = BlockEntity.getDefaultCompound(newBlock, BlockEntity.MOVING_BLOCK) - .putInt("pistonPosX", this.getFloorX()) - .putInt("pistonPosY", this.getFloorY()) - .putInt("pistonPosZ", this.getFloorZ()) - .putCompound("movingBlock", new CompoundTag() - .putInt("id", newBlock.getId()) //only for nukkit purpose - .putInt("meta", newBlock.getDamage()) //only for nukkit purpose - .putShort("val", newBlock.getDamage()) - .putString("name", GlobalBlockPalette.getName(newBlock.getId())) - ); + BlockEntity blockEntity = this.level.getBlockEntity(oldPos); - if (blockEntity != null && !(blockEntity instanceof BlockEntityMovingBlock)) { - blockEntity.saveNBT(); + this.level.setBlock(newBlock, Block.get(BlockID.MOVING_BLOCK), true); - CompoundTag t = new CompoundTag(blockEntity.namedTag.getTags()); + CompoundTag nbt = BlockEntity.getDefaultCompound(newBlock, BlockEntity.MOVING_BLOCK) + .putInt("pistonPosX", this.getFloorX()) + .putInt("pistonPosY", this.getFloorY()) + .putInt("pistonPosZ", this.getFloorZ()) + .putCompound("movingBlock", new CompoundTag() + .putInt("id", newBlock.getId()) //only for nukkit purpose + .putInt("meta", newBlock.getDamage()) //only for nukkit purpose + .putShort("val", newBlock.getDamage()) + .putString("name", GlobalBlockPalette.getName(newBlock.getId())) + ); - nbt.putCompound("movingEntity", t); - blockEntity.close(); - } + if (blockEntity != null && !(blockEntity instanceof BlockEntityMovingBlock)) { + blockEntity.saveNBT(); - new BlockEntityMovingBlock(this.level.getChunk(newBlock.getChunkX(), newBlock.getChunkZ()), nbt); + CompoundTag t = new CompoundTag(blockEntity.namedTag.getTags()); - if (this.level.getBlockIdAt(oldPos.getFloorX(), oldPos.getFloorY(), oldPos.getFloorZ()) != BlockID.MOVING_BLOCK) { - this.level.setBlock(oldPos, Block.get(BlockID.AIR)); - } + nbt.putCompound("movingEntity", t); + blockEntity.close(); } - } - if (extending) { - this.level.setBlock(this.getSide(direction), new BlockPistonHead(this.getDamage())); + new BlockEntityMovingBlock(this.level.getChunk(newBlock.getChunkX(), newBlock.getChunkZ()), nbt); + + if (this.level.getBlockIdAt(oldPos.getFloorX(), oldPos.getFloorY(), oldPos.getFloorZ()) != BlockID.MOVING_BLOCK) { + this.level.setBlock(oldPos, Block.get(BlockID.AIR)); + } } + } - BlockEntityPistonArm blockEntity = (BlockEntityPistonArm) this.level.getBlockEntity(this); - blockEntity.move(extending, attached); - return true; + if (extending) { + this.level.setBlock(this.getSide(direction), new BlockPistonHead(this.getDamage())); } + + BlockEntityPistonArm blockEntity = (BlockEntityPistonArm) this.level.getBlockEntity(this); + blockEntity.move(extending, attached); + return true; } - public static boolean canPush(Block block, BlockFace face, boolean destroyBlocks) { - if (block.canBePushed() && block.getY() >= 0 && (face != BlockFace.DOWN || block.getY() != 0) && - block.getY() <= 255 && (face != BlockFace.UP || block.getY() != 255)) { + public static boolean canPush(Block block, BlockFace face, boolean destroyBlocks, boolean extending) { + if ( + block.getY() >= 0 && (face != BlockFace.DOWN || block.getY() != 0) && + block.getY() <= 255 && (face != BlockFace.UP || block.getY() != 255) + ) { + if (extending && !block.canBePushed() || !extending && !block.canBePulled()) { + return false; + } - if (block instanceof BlockFlowable) { - return destroyBlocks; + if (block.breaksWhenMoved()) { + return destroyBlocks || block.sticksToPiston(); } BlockEntity be = block.level.getBlockEntity(block); @@ -333,16 +313,19 @@ public boolean canMove() { this.toDestroy.clear(); Block block = this.blockToMove; - if (!canPush(block, this.moveDirection, false)) { - if (block instanceof BlockFlowable) { + if (!canPush(block, this.moveDirection, true, extending)) { + return false; + } + + if (block.breaksWhenMoved()) { + if (extending || block.sticksToPiston()) { this.toDestroy.add(this.blockToMove); - return true; } - return false; + return true; } - if (!this.addBlockLine(this.blockToMove)) { + if (!this.addBlockLine(this.blockToMove, this.moveDirection)) { return false; } @@ -357,14 +340,14 @@ public boolean canMove() { return true; } - private boolean addBlockLine(Block origin) { + private boolean addBlockLine(Block origin, BlockFace from) { Block block = origin.clone(); if (block.getId() == AIR) { return true; } - if (!canPush(origin, this.moveDirection, false)) { + if (!canPush(origin, this.moveDirection, false, extending)) { return true; } @@ -388,7 +371,12 @@ private boolean addBlockLine(Block origin) { while (block.getId() == SLIME_BLOCK) { block = origin.getSide(this.moveDirection.getOpposite(), count); - if (block.getId() == AIR || !canPush(block, this.moveDirection, false) || block.equals(this.pistonPos)) { + if (block.getId() == AIR || !canPush(block, this.moveDirection, false, extending) || block.equals(this.pistonPos)) { + break; + } + + if (block.breaksWhenMoved() && block.sticksToPiston()) { + this.toDestroy.add(block); break; } @@ -429,11 +417,11 @@ private boolean addBlockLine(Block origin) { return true; } - if (!canPush(nextBlock, this.moveDirection, true) || nextBlock.equals(this.pistonPos)) { + if (!canPush(nextBlock, this.moveDirection, true, extending) || nextBlock.equals(this.pistonPos)) { return false; } - if (nextBlock instanceof BlockFlowable && !nextBlock.canBePushed()) { + if (nextBlock.breaksWhenMoved()) { this.toDestroy.add(nextBlock); return true; } @@ -460,7 +448,7 @@ private void reorderListAtCollision(int count, int index) { private boolean addBranchingBlocks(Block block) { for (BlockFace face : BlockFace.values()) { - if (face.getAxis() != this.moveDirection.getAxis() && !this.addBlockLine(block.getSide(face))) { + if (face.getAxis() != this.moveDirection.getAxis() && !this.addBlockLine(block.getSide(face), face)) { return false; } } diff --git a/src/main/java/cn/nukkit/block/BlockPistonHead.java b/src/main/java/cn/nukkit/block/BlockPistonHead.java index 064d9d3cfdd..12e0a68c12d 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonHead.java +++ b/src/main/java/cn/nukkit/block/BlockPistonHead.java @@ -66,6 +66,16 @@ public boolean canBePushed() { return false; } + @Override + public boolean canBePulled() { + return false; + } + + @Override + public boolean isSolid() { + return false; + } + @Override public Item toItem() { return new ItemBlock(new BlockAir()); diff --git a/src/main/java/cn/nukkit/block/BlockPumpkin.java b/src/main/java/cn/nukkit/block/BlockPumpkin.java index 66568dc647b..28dd1ea4959 100644 --- a/src/main/java/cn/nukkit/block/BlockPumpkin.java +++ b/src/main/java/cn/nukkit/block/BlockPumpkin.java @@ -64,7 +64,12 @@ public BlockColor getColor() { } @Override - public boolean canBePushed() { + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { return false; } diff --git a/src/main/java/cn/nukkit/block/BlockRail.java b/src/main/java/cn/nukkit/block/BlockRail.java index b2aa2baa35a..c5112c68883 100644 --- a/src/main/java/cn/nukkit/block/BlockRail.java +++ b/src/main/java/cn/nukkit/block/BlockRail.java @@ -272,4 +272,14 @@ public Item[] getDrops(Item item) { public BlockFace getBlockFace() { return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07); } + + @Override + public boolean canBePushed() { + return true; + } + + @Override + public boolean canBePulled() { + return true; + } } diff --git a/src/main/java/cn/nukkit/block/BlockSignPost.java b/src/main/java/cn/nukkit/block/BlockSignPost.java index 0f6f3b9aee5..ac9681fa5ce 100644 --- a/src/main/java/cn/nukkit/block/BlockSignPost.java +++ b/src/main/java/cn/nukkit/block/BlockSignPost.java @@ -128,4 +128,9 @@ public BlockColor getColor() { public BlockFace getBlockFace() { return BlockFace.fromIndex(this.getDamage() & 0x07); } + + @Override + public boolean breaksWhenMoved() { + return true; + } } diff --git a/src/main/java/cn/nukkit/block/BlockSkull.java b/src/main/java/cn/nukkit/block/BlockSkull.java index 700015a48be..14df442740c 100644 --- a/src/main/java/cn/nukkit/block/BlockSkull.java +++ b/src/main/java/cn/nukkit/block/BlockSkull.java @@ -107,4 +107,13 @@ public int getToolType() { return ItemTool.TYPE_PICKAXE; } + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/block/BlockTripWire.java b/src/main/java/cn/nukkit/block/BlockTripWire.java index 0de4c1295d1..7e118076c0d 100644 --- a/src/main/java/cn/nukkit/block/BlockTripWire.java +++ b/src/main/java/cn/nukkit/block/BlockTripWire.java @@ -11,7 +11,7 @@ /** * @author CreeperFace */ -public class BlockTripWire extends BlockFlowable { +public class BlockTripWire extends BlockTransparentMeta { public BlockTripWire(int meta) { super(meta); diff --git a/src/main/java/cn/nukkit/block/BlockTripWireHook.java b/src/main/java/cn/nukkit/block/BlockTripWireHook.java index 0b1ab9c571f..f320a5bb1e6 100644 --- a/src/main/java/cn/nukkit/block/BlockTripWireHook.java +++ b/src/main/java/cn/nukkit/block/BlockTripWireHook.java @@ -12,7 +12,7 @@ /** * @author CreeperFace */ -public class BlockTripWireHook extends BlockFlowable { +public class BlockTripWireHook extends BlockTransparentMeta { public BlockTripWireHook() { this(0); diff --git a/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java b/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java index cd88e611db3..bd58d13eab5 100644 --- a/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java +++ b/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java @@ -177,4 +177,14 @@ public int getComparatorInputOverride() { public BlockColor getColor() { return BlockColor.PURPLE_BLOCK_COLOR; } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockVine.java b/src/main/java/cn/nukkit/block/BlockVine.java index 339771d3e4a..9508f8b0287 100644 --- a/src/main/java/cn/nukkit/block/BlockVine.java +++ b/src/main/java/cn/nukkit/block/BlockVine.java @@ -202,4 +202,14 @@ private int getMetaFromFace(BlockFace face) { public int getToolType() { return ItemTool.TYPE_SHEARS; } + + @Override + public boolean breaksWhenMoved() { + return true; + } + + @Override + public boolean sticksToPiston() { + return false; + } } diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index 52cbbba5f1f..6bc9a238df7 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -1039,7 +1039,7 @@ private void tickChunks() { int chunksPerLoader = Math.min(200, Math.max(1, (int) (((double) (this.chunksPerTicks - this.loaders.size()) / this.loaders.size() + 0.5)))); int randRange = 3 + chunksPerLoader / 30; - randRange = randRange > this.chunkTickRadius ? this.chunkTickRadius : randRange; + randRange = Math.min(randRange, this.chunkTickRadius); ThreadLocalRandom random = ThreadLocalRandom.current(); if (!this.loaders.isEmpty()) { From a6f99d580537f432039a8e6bbdec276c8a6b7087 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Tue, 24 Dec 2019 08:33:27 +0100 Subject: [PATCH 42/52] Drop painting when pushed --- .../blockentity/BlockEntityPistonArm.java | 17 +++-- src/main/java/cn/nukkit/entity/Entity.java | 5 ++ .../cn/nukkit/entity/item/EntityPainting.java | 65 ++++++++++++++++++- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index 64345656111..7e2db2ab9ef 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -120,12 +120,17 @@ void moveEntity(Entity entity, BlockFace moveDirection) { return; } - float diff = this.progress - this.lastProgress; - entity.move( - diff * moveDirection.getXOffset(), - diff * moveDirection.getYOffset(), - diff * moveDirection.getZOffset() - ); + entity.onPushByPiston(this); + + if (!entity.closed) { + float diff = Math.abs(this.progress - this.lastProgress); + + entity.move( + diff * moveDirection.getXOffset(), + diff * moveDirection.getYOffset(), + diff * moveDirection.getZOffset() + ); + } } public void move(boolean extending, List attachedBlocks) { diff --git a/src/main/java/cn/nukkit/entity/Entity.java b/src/main/java/cn/nukkit/entity/Entity.java index 9dbfef79e7f..85cf6b7dd12 100644 --- a/src/main/java/cn/nukkit/entity/Entity.java +++ b/src/main/java/cn/nukkit/entity/Entity.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.Server; import cn.nukkit.block.*; +import cn.nukkit.blockentity.BlockEntityPistonArm; import cn.nukkit.entity.data.*; import cn.nukkit.event.Event; import cn.nukkit.event.entity.*; @@ -1650,6 +1651,10 @@ public void onStruckByLightning(Entity entity) { } } + public void onPushByPiston(BlockEntityPistonArm piston) { + + } + public boolean onInteract(Player player, Item item, Vector3 clickedPos) { return onInteract(player, item); } diff --git a/src/main/java/cn/nukkit/entity/item/EntityPainting.java b/src/main/java/cn/nukkit/entity/item/EntityPainting.java index f20602f61b3..63344a6da46 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityPainting.java +++ b/src/main/java/cn/nukkit/entity/item/EntityPainting.java @@ -1,6 +1,7 @@ package cn.nukkit.entity.item; import cn.nukkit.Player; +import cn.nukkit.blockentity.BlockEntityPistonArm; import cn.nukkit.entity.Entity; import cn.nukkit.entity.EntityHanging; import cn.nukkit.event.entity.EntityDamageByEntityEvent; @@ -8,6 +9,10 @@ import cn.nukkit.item.ItemPainting; import cn.nukkit.level.GameRule; import cn.nukkit.level.format.FullChunk; +import cn.nukkit.math.BlockFace; +import cn.nukkit.math.BlockFace.Axis; +import cn.nukkit.math.SimpleAxisAlignedBB; +import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.AddPaintingPacket; import cn.nukkit.network.protocol.DataPacket; @@ -26,6 +31,10 @@ public class EntityPainting extends EntityHanging { public final static Motive[] motives = Motive.values(); private Motive motive; + private float width; + private float length; + private float height; + public EntityPainting(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); } @@ -34,6 +43,21 @@ public static Motive getMotive(String name) { return Motive.BY_NAME.getOrDefault(name, Motive.KEBAB); } + @Override + public float getWidth() { + return width; + } + + @Override + public float getLength() { + return length; + } + + @Override + public float getHeight() { + return height; + } + @Override public int getNetworkId() { return NETWORK_ID; @@ -41,8 +65,38 @@ public int getNetworkId() { @Override protected void initEntity() { - super.initEntity(); this.motive = getMotive(this.namedTag.getString("Motive")); + + if (this.motive != null) { + BlockFace face = getHorizontalFacing(); + + Vector3 size = new Vector3(this.motive.width, this.motive.height, this.motive.width).multiply(0.5); + + if (face.getAxis() == Axis.Z) { + size.z = 0.5; + } else { + size.x = 0.5; + } + + this.width = (float) size.x; + this.length = (float) size.z; + this.height = (float) size.y; + + this.boundingBox = new SimpleAxisAlignedBB( + this.x - size.x, + this.y - size.y, + this.z - size.z, + this.x + size.x, + this.y + size.y, + this.z + size.z + ); + } else { + this.width = 0; + this.height = 0; + this.length = 0; + } + + super.initEntity(); } @Override @@ -80,6 +134,15 @@ public void saveNBT() { this.namedTag.putString("Motive", this.motive.title); } + @Override + public void onPushByPiston(BlockEntityPistonArm piston) { + if (this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) { + this.level.dropItem(this, new ItemPainting()); + } + + this.close(); + } + public Motive getArt() { return getMotive(); } From 5be10a953aebd2ef03ec690b853cc9273cd58d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Bedn=C3=A1=C5=99?= Date: Mon, 30 Dec 2019 11:47:21 +0100 Subject: [PATCH 43/52] Don't create new BlockEntityPiston instance directly --- src/main/java/cn/nukkit/block/BlockPistonBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 13722fa2576..d42ae91f39f 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -72,7 +72,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl .putInt("facing", this.getBlockFace().getIndex()) .putBoolean("Sticky", this.sticky); - new BlockEntityPistonArm(this.level.getChunk(getChunkX(), getChunkZ()), nbt); + BlockEntity.createBlockEntity(BlockEntity.PISTON_ARM, this.level.getChunk(getChunkX(), getChunkZ()), nbt); this.checkState(null); return true; From bd3efa12d4cf0f970028f2ca6ed267807c9113db Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Thu, 2 Jan 2020 12:50:53 +0100 Subject: [PATCH 44/52] Fix buttons --- src/main/java/cn/nukkit/block/BlockButton.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockButton.java b/src/main/java/cn/nukkit/block/BlockButton.java index 9150302436c..eaf08ddce3c 100644 --- a/src/main/java/cn/nukkit/block/BlockButton.java +++ b/src/main/java/cn/nukkit/block/BlockButton.java @@ -56,6 +56,9 @@ public boolean onActivate(Item item, Player player) { this.level.scheduleUpdate(this, 30); + this.setDamage(this.getDamage() ^ 0x08); + this.level.setBlock(this, this, true, false); + if (this.level.getServer().isRedstoneEnabled()) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 0, 15)); @@ -65,8 +68,6 @@ public boolean onActivate(Item item, Player player) { level.updateAroundRedstone(pos.getSide(getFacing().getOpposite()), null); } - this.setDamage(this.getDamage() ^ 0x08); - this.level.setBlock(this, this, true, false); this.level.addSound(this.add(0.5, 0.5, 0.5), Sound.RANDOM_CLICK); return true; } @@ -84,7 +85,7 @@ public int onUpdate(int type) { this.level.setBlock(this, this, true, false); this.level.addSound(this.add(0.5, 0.5, 0.5), Sound.RANDOM_CLICK); - if (!this.level.getServer().isRedstoneEnabled()) { + if (this.level.getServer().isRedstoneEnabled()) { this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 15, 0)); Vector3 pos = getLocation(); From d7de1ef55e432959c4d675cac36d2adbdcd2c447 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Thu, 2 Jan 2020 16:33:22 +0100 Subject: [PATCH 45/52] Fixed piston bug --- src/main/java/cn/nukkit/block/BlockPistonBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index d42ae91f39f..87023dc88cc 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -111,7 +111,7 @@ public int onUpdate(int type) { BlockEntityPistonArm arm = (BlockEntityPistonArm) blockEntity; boolean powered = this.isPowered(); - if (arm.powered != powered && checkState(powered)) { + if (arm.state % 2 == 0 && arm.powered != powered && checkState(powered)) { arm.powered = powered; if (arm.chunk != null) { From 8a3c56e49bdf235d69ac983b01d3e260f384d3b3 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Thu, 2 Jan 2020 23:37:03 +0100 Subject: [PATCH 46/52] Fix piston placement --- src/main/java/cn/nukkit/block/BlockPistonBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 87023dc88cc..92171e139d6 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -49,7 +49,7 @@ public double getHardness() { @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { - if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) { + if (Math.abs(player.getFloorX() - this.x) <= 1 && Math.abs(player.getFloorZ() - this.z) <= 1) { double y = player.y + player.getEyeHeight(); if (y - this.y > 2) { From d46a4d7a85000a15be925af024827c8774590ee3 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sat, 4 Jan 2020 14:26:12 +0100 Subject: [PATCH 47/52] Don't use player instance in async event --- .../cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java b/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java index ba7b49ed705..e483f49c13e 100644 --- a/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java +++ b/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java @@ -1,5 +1,6 @@ package cn.nukkit.event.player; +import cn.nukkit.Player; import cn.nukkit.Server; import cn.nukkit.event.HandlerList; @@ -38,6 +39,11 @@ public PlayerAsyncPreLoginEvent(String name, UUID uuid, String address, int port this.port = port; } + @Override + public Player getPlayer() { + throw new UnsupportedOperationException("Could not get player instance in an async event"); + } + public String getName() { return name; } From 5931d91b8ff7d8eed5b82bb7f0f6c80baa757225 Mon Sep 17 00:00:00 2001 From: CreeperFace Date: Sun, 12 Jan 2020 19:19:58 +0100 Subject: [PATCH 48/52] Fix redstone torch and piston update --- .../java/cn/nukkit/block/BlockPistonBase.java | 5 ++-- .../cn/nukkit/block/BlockRedstoneTorch.java | 30 ++++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 92171e139d6..5e6fd716cee 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -72,9 +72,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl .putInt("facing", this.getBlockFace().getIndex()) .putBoolean("Sticky", this.sticky); - BlockEntity.createBlockEntity(BlockEntity.PISTON_ARM, this.level.getChunk(getChunkX(), getChunkZ()), nbt); + BlockEntityPistonArm piston = (BlockEntityPistonArm) BlockEntity.createBlockEntity(BlockEntity.PISTON_ARM, this.level.getChunk(getChunkX(), getChunkZ()), nbt); + piston.powered = isPowered(); - this.checkState(null); + this.checkState(piston.powered); return true; } diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java b/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java index 3249931bb41..34c517f403e 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java @@ -43,20 +43,22 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl return false; } -// if (!checkState()) { -// BlockFace facing = getFacing().getOpposite(); -// Vector3 pos = getLocation(); -// -// for (BlockFace side : BlockFace.values()) { -// if (facing == side) { -// continue; -// } -// -// this.level.updateAround(pos.getSide(side)); -// } -// } - - checkState(); + if (this.level.getServer().isRedstoneEnabled()) { + if (!checkState()) { + BlockFace facing = getBlockFace().getOpposite(); + Vector3 pos = getLocation(); + + for (BlockFace side : BlockFace.values()) { + if (facing == side) { + continue; + } + + this.level.updateAroundRedstone(pos.getSide(side), null); + } + } + + checkState(); + } return true; } From 8344d9f4419c7981e86d9876bbcd7f75f5137e7b Mon Sep 17 00:00:00 2001 From: AlicanCopur <37244649+AlicanCopur@users.noreply.github.com> Date: Sun, 10 May 2020 11:26:55 +0300 Subject: [PATCH 49/52] [Redstone] Updates from master branch (#1419) * Added call BlockGrowEvent (#1403) Added call BlockGrowEvent in Sugarcane * Added PlayerJumpEvent (#1405) * Create PlayerJumpEvent.java * Call PlayerJumpEvent * Removed Cancellable * Fix wrong flags in MoveEntityAbsolutePacket + set onGround (#1410) Co-authored-by: PetteriM1 --- src/main/java/cn/nukkit/Player.java | 3 +++ .../java/cn/nukkit/block/BlockSugarcane.java | 7 ++++++- .../cn/nukkit/event/player/PlayerJumpEvent.java | 16 ++++++++++++++++ src/main/java/cn/nukkit/level/Level.java | 1 + .../protocol/MoveEntityAbsolutePacket.java | 4 ++-- 5 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/main/java/cn/nukkit/event/player/PlayerJumpEvent.java diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index 73272b528e7..f400fa49711 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -27,6 +27,7 @@ import cn.nukkit.event.player.PlayerAsyncPreLoginEvent.LoginResult; import cn.nukkit.event.player.PlayerInteractEvent.Action; import cn.nukkit.event.player.PlayerTeleportEvent.TeleportCause; +import cn.nukkit.event.player.PlayerJumpEvent; import cn.nukkit.event.server.DataPacketReceiveEvent; import cn.nukkit.event.server.DataPacketSendEvent; import cn.nukkit.form.window.FormWindow; @@ -2485,6 +2486,8 @@ public void onCompletion(Server server) { this.respawn(); break; case PlayerActionPacket.ACTION_JUMP: + PlayerJumpEvent playerJumpEvent = new PlayerJumpEvent(this); + this.server.getPluginManager().callEvent(playerJumpEvent); break packetswitch; case PlayerActionPacket.ACTION_START_SPRINT: PlayerToggleSprintEvent playerToggleSprintEvent = new PlayerToggleSprintEvent(this, true); diff --git a/src/main/java/cn/nukkit/block/BlockSugarcane.java b/src/main/java/cn/nukkit/block/BlockSugarcane.java index 529700e6163..4948a709b43 100644 --- a/src/main/java/cn/nukkit/block/BlockSugarcane.java +++ b/src/main/java/cn/nukkit/block/BlockSugarcane.java @@ -104,7 +104,12 @@ public int onUpdate(int type) { for (int y = 1; y < 3; ++y) { Block b = this.getLevel().getBlock(new Vector3(this.x, this.y + y, this.z)); if (b.getId() == AIR) { - this.getLevel().setBlock(b, Block.get(BlockID.SUGARCANE_BLOCK), false); + BlockGrowEvent ev = new BlockGrowEvent(b, Block.get(BlockID.SUGARCANE_BLOCK)); + Server.getInstance().getPluginManager().callEvent(ev); + + if (!ev.isCancelled()) { + this.getLevel().setBlock(b, Block.get(BlockID.SUGARCANE_BLOCK), false); + } break; } } diff --git a/src/main/java/cn/nukkit/event/player/PlayerJumpEvent.java b/src/main/java/cn/nukkit/event/player/PlayerJumpEvent.java new file mode 100644 index 00000000000..b915ca1d8cb --- /dev/null +++ b/src/main/java/cn/nukkit/event/player/PlayerJumpEvent.java @@ -0,0 +1,16 @@ +package cn.nukkit.event.player; + +import cn.nukkit.Player; +import cn.nukkit.event.HandlerList; + +public class PlayerJumpEvent extends PlayerEvent { + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + public PlayerJumpEvent(Player player){ + this.player = player; + } +} diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index f9e5d51b87f..ed0085adbb1 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -3216,6 +3216,7 @@ public void addEntityMovement(Entity entity, double x, double y, double z, doubl pk.yaw = (float) yaw; pk.headYaw = (float) headYaw; pk.pitch = (float) pitch; + pk.onGround = entity.onGround; Server.broadcastPacket(entity.getViewers().values(), pk); } diff --git a/src/main/java/cn/nukkit/network/protocol/MoveEntityAbsolutePacket.java b/src/main/java/cn/nukkit/network/protocol/MoveEntityAbsolutePacket.java index f79138a8413..b4615cae370 100644 --- a/src/main/java/cn/nukkit/network/protocol/MoveEntityAbsolutePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MoveEntityAbsolutePacket.java @@ -46,10 +46,10 @@ public void encode() { this.reset(); this.putEntityRuntimeId(this.eid); byte flags = 0; - if (teleport) { + if (onGround) { flags |= 0x01; } - if (onGround) { + if (teleport) { flags |= 0x02; } this.putByte(flags); From 15da21fb07162c28685328ae2c903231dc0b450f Mon Sep 17 00:00:00 2001 From: Sleepybear Date: Fri, 25 Sep 2020 17:41:16 -0700 Subject: [PATCH 50/52] merge master into redstone - fix build --- src/main/java/cn/nukkit/block/BlockPistonBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 3f3201fb6fc..a616de58967 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -272,7 +272,7 @@ public static boolean canPush(Block block, BlockFace face, boolean destroyBlocks return false; } - public static class BlocksCalculator { + public class BlocksCalculator { private final Vector3 pistonPos; private Vector3 armPos; From 9e6612d8278d3e01807a48892e5df5d0f294ffb5 Mon Sep 17 00:00:00 2001 From: FlamingKnight <56657837+FlamingKnight@users.noreply.github.com> Date: Sun, 28 Mar 2021 12:04:07 -0700 Subject: [PATCH 51/52] Redstone 1.16.210 Support (#1817) * Number of entries needs to be unsigned (#1623) * TwistedAsylumMC * Fix transfer (#1622) * Fix transfer * Update Player.java * Update Player.java * Bump RakNet to 1.6.21 Fixes memory leaks that can occur randomly. * Downgrade RakNet to 1.6.20 This reverts commit 26417517 * Max stack size in BE is 64 (#1627) * Potted plants can now be removed from the pot by clicking on it (#1629) * Update fuels (#1631) * Event for dragon egg teleporting (#1632) * Implement multiple snow layers (#1633) * Fix beacons giving too high effect levels (#1638) * Remove unnecessary packet broadcasts (#1639) * Implement netherite items (#1641) * Improve sponge placement (#1640) * Bucket fixes and improvements (#1644) * Implement vine growth (#1645) * Make getSafeSpawn() account for non-generated chunks (#1634) * Make getSafeSpawn() account for non-generated chunks * move to Level.initLevel() * Totem doesn't save from suicide (#1642) * Vines can be harvested with an axe that has the Silk Touch enchantment (#1646) * No weather in nether and end (#1647) * Cleanup command params (#1637) * Cleanup command params * Remove deprecated constructor * deprecate old constructors * CommandEnum(String, String...) * Fix block break time (#1649) * Fix block break time * Update Block.java * parse difficulty from string (#1652) * Fix dropped items falling thru grass path & farmland (#1653) * Implement Curse of Vanishing (#1654) * Limit the spawn height of nether portal (#1655) * Fix incorrect meta in BlockButton's toItem() (#1656) * Better constructor for DefaultPlayerDataSerializer (#1657) * Update DefaultPlayerDataSerializer.java Make this class more flexible, so youre able to use it for e.g. on other directories and hold no server instance. * Update DefaultPlayerDataSerializer.java * change initialization of DefaultPlayerData serialized to prevent null datapath (#1662) * Set new distribution URLs * Update to raknet 1.6.22 (#1665) * Update resources (#1658) * Implement StructureGrowEvent (#1669) * Implement StructureGrowEvent * Fix replacing saplings * Fix finding saplings and fix cocoa age * Add XUID to PlayerAsyncPreLoginEvent (#1675) * Fix end portal creation (#1667) * Add trident pickup event (#1679) * Update Particle.java (#1676) * Update Particle.java Add utility methods * Update Particle.java add missing import * Update Particle.java Fix typo * Update Particle.java next time work the whole time with the IDE to prevent such stupid small mistakes lol * Artifactory Integration (#1680) * Test artifactory publishing * Missed pom field * Remove maven tool * Have another shot at this * Include javadoc jar * Fix level loading (#1681) * Make snowballs to damage blazes (#1682) * Add TimeSinceRest (#1670) * implement that Haybales reduce fall damage (#1690) * Lava doesn't turn concrete powder into concrete (#1712) * Fix typo in ShowParticles (#1725) * NetherPortal block can't be destroyed by liquid flow (#1726) * 1.16.200 (#1708) * Initial 1.16.100 support CreativeContentPacket is corrupting for some odd reason and crashing the client. * Many performance changes & fix crashing * Remove remapping and send pre-mapped values * Fixes for skins, commands and inventories (#1685) - Fixed client crashing on Windows 10 (when you use persona-skin). - Added new parameter for SkinAnimation - "AnimationExpression". - Fixed commands enabling. - Fixed opening / closing inventories on client-side. Co-authored-by: xBeeegsone * fix for skin + geometry, for entityhumans (#1688) * Item translations using item palette (#1694) * Move batching code down to RakNetInterface * Fix ContainerClosePacket (#1696) * Update Sound enum to 1.16.100 (#1693) * Do not remove session before it gets ticked * initial 1.16.200 support (#1707) * fix for skin + geometry, for entityhumans * initial 1.16.200 support * change comment * Don't batch batched packets * Remove extra data from chunk encoding * Refactor packet batching code * More refactoring of RakNetInterface - Should improve performance further as we are now decompressing packets on the network threads unlike before. - May fix the disconnect issues but I haven't been able to reproduce it so can't be certain it does. - PlayerCreationEvent was being called async which could have caused further issues. It is now queued onto the main thread. - Broken packets from the client will now cause them to be force disconnected from the server. - Bumped RakNet to 1.6.25 * Don't batch broadcasted packets * Catch and log errors whilst handling packets * Don't encode packets on netty threads * Remove RakNet testing version Co-authored-by: Alex <21226684+xBeeegsone@users.noreply.github.com> Co-authored-by: xBeeegsone Co-authored-by: Erik Miller Co-authored-by: Alemiz <45739737+Alemiz112@users.noreply.github.com> Co-authored-by: Wodor <17339354+wode490390@users.noreply.github.com> * Support for arm64, k8s, helm (#1552) * Initial helm chart, docker for arm64. * Add missing slash to local helm values file in gitignore. * Add in support to provide loadBalancerIP by helm value. * Ports should not be set on the service if using default ClusterIP svc type. * Revert svc condition. * Remove condition on service ports. * Add in nodePort key as a value configurable. * Provide a default nodePort value. * Make nodePort conditional. * Fix a typo (#1731) * Fix floating vines (#1727) * Fix floating vines * Use BlockFace * Pass LoginChainData and Skin to PlayerAsyncPreLoginEvent (#1717) * Fix resource packs for 1.16.200 (Fixes #1718) (#1736) * Make undead mobs to take damage from healing potions and heal from damage potions (#1743) * No smoke particles on boat/minecart death (#1745) * No smoke particles on boat/minecart death * Remove unused imports * Fixed BlockDispenser and made BlockPistonBase not show white moving entity * Fix boat variants (#1748) * Tridents don't despawn on Bedrock Edition (#1747) * Fixes & improvements (#1756) Fixes for spectator and adventure game modes by XKazzuKX * Use player display name in updatePlayerListData (#1760) * Implement bStats metrics (#1761) * Implement bStats metrics * Hardcode server name * Add a check to make sure metrics isnt started twice * Actully initialize metrics... (#1763) * Add server codename to metrics (#1766) I see PowerNukkit has already merged in metrics...so here we go * Implement ItemNameTag (#1765) * Add ItemNameTag class * Uncomment nametag in item list * Use custom name + add check * Move nametag behaviour into EntityCreature * Humans and Dragons cannot be name-tagged. * Added gamemode check * Use !isCreative() instead * Remove removeItem duplication * Implement Fatal Poison (#1758) * Allow "flying" with Slow Falling effect (#1762) * Fix anvil (#1616) * Fix anvil * Implement anvil damage * AnvilDamagePacket cannot be trusted * Add support for hardcoded MultiRecipe * Implement cost calculation * Add support for netherite items * Fix #703 (#1769) * Fix #703 * Change inbound queue to SPSC * Register new effects (#1771) * Allow anvil items to be renamed (#1772) This is untested with other PRs, but it allows the client to recognize that items can be renamed. * Update WhitelistCommand.java (#1778) * Allow equipping equipping armor with something on already (#1783) * Update runtime_item_ids.json (#1781) * Make EnchantmentTridentImpaling actually do something (#1775) * Fire aspect can ignite tnt (#1774) * Add basic ServerStopEvent (#834) * Make netherite items fireproof (#1788) * Update command param types (#1792) * Full vanilla turtle shell (#1767) * 1.16.210 (#1795) * Start working on .210 changes * Save and load PlayFabID * Update block palette * Update entity flags * Don't allow empty sub-motd * Update command param types https://discord.com/channels/393465748535640064/703967014481297448/819182033602478131 * Fix crafting in 1.16.210 (#1799) Technical explanation: Bedrock behaves much better when each recipe in the CraftingDataPacket is assigned a network ID, even when using client-authoritative inventories. * Update entity data ids (#1801) * Fix entity data ids by removing unused data values * Adding entity data with correct ids * Update BlockDirt (#1803) * Update Sound enum to 1.16.210 (#1802) * Fix the flow speed of lava in nether (#1800) * use EntityRuntimeId (#1773) * Add getter for LoginChainData in PlayerAsyncPreLoginEvent (#1780) * Update PlayerAsyncPreLoginEvent.java * API_VERSION to 1.0.12 * Bump GSON version (#1711) * Allow sugarcane to be placed on podzol (#1807) * Ice melts in nether + supports silk touch (#1809) * Ice melts in nether + supports silk touch * oops * Fixed normal pistons, working on sticky ones * Fixed sticky pistons mostly * Sticky pistons do not duplicate anymore Co-authored-by: Twisted Co-authored-by: Wodor <17339354+wode490390@users.noreply.github.com> Co-authored-by: SupremeMortal <6178101+SupremeMortal@users.noreply.github.com> Co-authored-by: Erkam Kahriman Co-authored-by: PetteriM1 <26197131+PetteriM1@users.noreply.github.com> Co-authored-by: Sleepybear Co-authored-by: Serhat G Co-authored-by: Kevims Co-authored-by: Luke <32024335+lukeeey@users.noreply.github.com> Co-authored-by: VixikHD <40556088+VixikHD@users.noreply.github.com> Co-authored-by: good777LUCKY <43497313+good777LUCKY@users.noreply.github.com> Co-authored-by: Erik Miller Co-authored-by: Kazuk <46299532+XKazzuKX@users.noreply.github.com> Co-authored-by: Alex <21226684+xBeeegsone@users.noreply.github.com> Co-authored-by: xBeeegsone Co-authored-by: Alemiz <45739737+Alemiz112@users.noreply.github.com> Co-authored-by: Chris Fordham Co-authored-by: DenielW Co-authored-by: Will <16578982+CloudG360@users.noreply.github.com> Co-authored-by: Hancho1577 Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> Co-authored-by: William Qi <20137336+WillQi@users.noreply.github.com> Co-authored-by: pffsac2 <79375297+pffsac2@users.noreply.github.com> Co-authored-by: Dima Uzyanov <55317472+kk4444w@users.noreply.github.com> Co-authored-by: LT_Name <45508179+lt-name@users.noreply.github.com> Co-authored-by: blohnungde <35181949+belohnung@users.noreply.github.com> --- .gitignore | 4 +- Dockerfile.arm64 | 33 + Jenkinsfile | 27 +- Makefile | 85 + README.md | 47 +- charts/nukkit/.helmignore | 23 + charts/nukkit/Chart.yaml | 6 + charts/nukkit/templates/NOTES.txt | 15 + charts/nukkit/templates/_helpers.tpl | 63 + charts/nukkit/templates/configmap.yaml | 14 + charts/nukkit/templates/deployment.yaml | 70 + charts/nukkit/templates/hpa.yaml | 28 + charts/nukkit/templates/service.yaml | 21 + charts/nukkit/templates/serviceaccount.yaml | 12 + .../templates/tests/test-connection.yaml | 15 + charts/nukkit/values.yaml | 74 + nukkit.yml.default | 4 + pom.xml | 25 +- .../java/cn/nukkit/AdventureSettings.java | 2 +- src/main/java/cn/nukkit/Nukkit.java | 2 +- src/main/java/cn/nukkit/Player.java | 233 +- src/main/java/cn/nukkit/Server.java | 62 +- src/main/java/cn/nukkit/block/Block.java | 32 +- .../java/cn/nukkit/block/BlockButton.java | 2 +- .../java/cn/nukkit/block/BlockCobweb.java | 6 +- .../cn/nukkit/block/BlockConcretePowder.java | 4 +- src/main/java/cn/nukkit/block/BlockDirt.java | 15 +- .../java/cn/nukkit/block/BlockDispenser.java | 2 +- .../java/cn/nukkit/block/BlockDragonEgg.java | 19 +- .../cn/nukkit/block/BlockEndPortalFrame.java | 17 +- .../java/cn/nukkit/block/BlockFarmland.java | 2 +- .../java/cn/nukkit/block/BlockFlowerPot.java | 21 +- .../java/cn/nukkit/block/BlockGrassPath.java | 2 +- .../java/cn/nukkit/block/BlockHayBale.java | 6 + src/main/java/cn/nukkit/block/BlockIce.java | 18 +- src/main/java/cn/nukkit/block/BlockLava.java | 3 + .../java/cn/nukkit/block/BlockLeaves.java | 2 +- .../java/cn/nukkit/block/BlockLiquid.java | 2 +- .../java/cn/nukkit/block/BlockMushroom.java | 13 +- .../cn/nukkit/block/BlockNetherPortal.java | 5 + .../java/cn/nukkit/block/BlockPistonBase.java | 13 +- .../java/cn/nukkit/block/BlockSapling.java | 102 +- src/main/java/cn/nukkit/block/BlockSnow.java | 18 +- .../java/cn/nukkit/block/BlockSnowLayer.java | 79 +- .../java/cn/nukkit/block/BlockSponge.java | 57 +- .../java/cn/nukkit/block/BlockSugarcane.java | 2 +- src/main/java/cn/nukkit/block/BlockTNT.java | 10 +- src/main/java/cn/nukkit/block/BlockVine.java | 181 +- .../nukkit/blockentity/BlockEntityBeacon.java | 4 +- .../blockentity/BlockEntityShulkerBox.java | 4 - src/main/java/cn/nukkit/command/Command.java | 2 +- .../cn/nukkit/command/SimpleCommandMap.java | 2 +- .../cn/nukkit/command/data/CommandEnum.java | 31 + .../nukkit/command/data/CommandParameter.java | 100 +- .../nukkit/command/defaults/BanCommand.java | 4 +- .../nukkit/command/defaults/BanIpCommand.java | 8 +- .../command/defaults/BanListCommand.java | 3 +- .../command/defaults/DebugPasteCommand.java | 1 + .../defaults/DefaultGamemodeCommand.java | 6 +- .../nukkit/command/defaults/DeopCommand.java | 2 +- .../command/defaults/DifficultyCommand.java | 6 +- .../command/defaults/EffectCommand.java | 28 +- .../command/defaults/EnchantCommand.java | 26 +- .../command/defaults/GamemodeCommand.java | 10 +- .../command/defaults/GameruleCommand.java | 58 +- .../nukkit/command/defaults/GiveCommand.java | 26 +- .../nukkit/command/defaults/HelpCommand.java | 2 +- .../nukkit/command/defaults/KickCommand.java | 4 +- .../nukkit/command/defaults/KillCommand.java | 2 +- .../cn/nukkit/command/defaults/MeCommand.java | 2 +- .../cn/nukkit/command/defaults/OpCommand.java | 2 +- .../command/defaults/PardonCommand.java | 2 +- .../command/defaults/PardonIpCommand.java | 3 +- .../command/defaults/ParticleCommand.java | 12 +- .../nukkit/command/defaults/SayCommand.java | 3 +- .../defaults/SetWorldSpawnCommand.java | 2 +- .../command/defaults/SpawnpointCommand.java | 3 +- .../command/defaults/TeleportCommand.java | 16 +- .../nukkit/command/defaults/TellCommand.java | 4 +- .../nukkit/command/defaults/TimeCommand.java | 19 +- .../command/defaults/TimingsCommand.java | 4 +- .../nukkit/command/defaults/TitleCommand.java | 37 +- .../command/defaults/VersionCommand.java | 5 + .../command/defaults/WeatherCommand.java | 7 +- .../command/defaults/WhitelistCommand.java | 10 +- .../cn/nukkit/command/defaults/XpCommand.java | 8 +- src/main/java/cn/nukkit/entity/Entity.java | 136 +- .../java/cn/nukkit/entity/EntityCreature.java | 30 + .../java/cn/nukkit/entity/EntityHuman.java | 17 +- .../cn/nukkit/entity/EntityHumanType.java | 5 + .../java/cn/nukkit/entity/EntityLiving.java | 21 +- src/main/java/cn/nukkit/entity/data/Skin.java | 9 + .../cn/nukkit/entity/item/EntityBoat.java | 20 +- .../entity/item/EntityFallingBlock.java | 35 +- .../nukkit/entity/item/EntityFishingHook.java | 11 +- .../cn/nukkit/entity/item/EntityItem.java | 9 +- .../entity/item/EntityMinecartAbstract.java | 6 +- .../cn/nukkit/entity/item/EntityPainting.java | 2 +- .../nukkit/entity/mob/EntityEnderDragon.java | 8 + .../java/cn/nukkit/entity/mob/EntityMob.java | 12 - .../nukkit/entity/passive/EntityAnimal.java | 12 - .../entity/projectile/EntityProjectile.java | 3 +- .../projectile/EntityThrownTrident.java | 8 - .../nukkit/event/block/AnvilDamageEvent.java | 53 + .../InventoryPickupTridentEvent.java | 26 + .../event/inventory/RepairItemEvent.java | 55 + .../event/level/StructureGrowEvent.java | 44 + .../player/PlayerAsyncPreLoginEvent.java | 34 +- .../nukkit/event/server/ServerStopEvent.java | 17 + .../cn/nukkit/inventory/AnvilInventory.java | 130 +- .../nukkit/inventory/ContainerInventory.java | 1 + .../cn/nukkit/inventory/CraftingManager.java | 26 +- .../inventory/FakeBlockUIComponent.java | 1 + src/main/java/cn/nukkit/inventory/Fuel.java | 4 +- .../java/cn/nukkit/inventory/MultiRecipe.java | 33 + .../inventory/PlayerEnderChestInventory.java | 1 + .../cn/nukkit/inventory/PlayerInventory.java | 8 +- .../inventory/PlayerOffhandInventory.java | 3 +- .../nukkit/inventory/PlayerUIComponent.java | 3 + .../transaction/RepairItemTransaction.java | 396 ++ .../transaction/action/RepairItemAction.java | 38 + src/main/java/cn/nukkit/item/Item.java | 48 +- src/main/java/cn/nukkit/item/ItemArmor.java | 21 +- .../java/cn/nukkit/item/ItemAxeNetherite.java | 36 + src/main/java/cn/nukkit/item/ItemBoat.java | 2 +- .../cn/nukkit/item/ItemBootsNetherite.java | 41 + src/main/java/cn/nukkit/item/ItemBow.java | 4 +- src/main/java/cn/nukkit/item/ItemBucket.java | 46 +- src/main/java/cn/nukkit/item/ItemCake.java | 2 +- .../nukkit/item/ItemChestplateNetherite.java | 41 + .../java/cn/nukkit/item/ItemCrossbow.java | 26 + src/main/java/cn/nukkit/item/ItemEdible.java | 2 +- .../java/cn/nukkit/item/ItemEndCrystal.java | 2 +- .../java/cn/nukkit/item/ItemFireCharge.java | 4 + .../java/cn/nukkit/item/ItemFirework.java | 4 + .../java/cn/nukkit/item/ItemFlintSteel.java | 4 + .../cn/nukkit/item/ItemHelmetNetherite.java | 41 + .../java/cn/nukkit/item/ItemHoeNetherite.java | 36 + src/main/java/cn/nukkit/item/ItemID.java | 12 + .../cn/nukkit/item/ItemIngotNetherite.java | 16 + .../cn/nukkit/item/ItemLeggingsNetherite.java | 41 + src/main/java/cn/nukkit/item/ItemMap.java | 2 +- .../java/cn/nukkit/item/ItemMinecart.java | 2 +- .../cn/nukkit/item/ItemMinecartChest.java | 2 +- .../cn/nukkit/item/ItemMinecartHopper.java | 2 +- .../java/cn/nukkit/item/ItemMinecartTNT.java | 2 +- src/main/java/cn/nukkit/item/ItemNameTag.java | 17 + .../java/cn/nukkit/item/ItemPainting.java | 4 + .../cn/nukkit/item/ItemPickaxeNetherite.java | 36 + src/main/java/cn/nukkit/item/ItemPotion.java | 2 +- .../cn/nukkit/item/ItemScrapNetherite.java | 16 + .../cn/nukkit/item/ItemShovelNetherite.java | 36 + .../java/cn/nukkit/item/ItemSpawnEgg.java | 4 + .../cn/nukkit/item/ItemSwordNetherite.java | 36 + src/main/java/cn/nukkit/item/ItemTool.java | 9 +- .../cn/nukkit/item/RuntimeItemMapping.java | 43 + .../java/cn/nukkit/item/RuntimeItems.java | 91 + .../nukkit/item/enchantment/Enchantment.java | 63 +- .../enchantment/EnchantmentBindingCurse.java | 9 +- .../enchantment/EnchantmentDurability.java | 8 +- .../enchantment/EnchantmentEfficiency.java | 5 +- .../enchantment/EnchantmentFireAspect.java | 4 +- .../enchantment/EnchantmentFrostWalker.java | 7 +- .../enchantment/EnchantmentKnockback.java | 4 +- .../item/enchantment/EnchantmentLure.java | 10 +- .../item/enchantment/EnchantmentMending.java | 12 +- .../enchantment/EnchantmentSilkTouch.java | 13 +- .../enchantment/EnchantmentSoulSpeed.java | 18 + .../item/enchantment/EnchantmentThorns.java | 4 +- .../item/enchantment/EnchantmentType.java | 14 +- .../EnchantmentVanishingCurse.java | 18 +- .../enchantment/EnchantmentWaterBreath.java | 6 +- .../enchantment/EnchantmentWaterWalker.java | 4 +- .../enchantment/EnchantmentWaterWorker.java | 7 +- .../item/enchantment/bow/EnchantmentBow.java | 5 +- .../enchantment/bow/EnchantmentBowFlame.java | 7 +- .../bow/EnchantmentBowInfinity.java | 7 +- .../bow/EnchantmentBowKnockback.java | 4 +- .../enchantment/bow/EnchantmentBowPower.java | 4 +- .../crossbow/EnchantmentCrossbow.java | 11 + .../EnchantmentCrossbowMultishot.java | 30 + .../crossbow/EnchantmentCrossbowPiercing.java | 30 + .../EnchantmentCrossbowQuickCharge.java | 25 + .../enchantment/damage/EnchantmentDamage.java | 6 +- .../damage/EnchantmentDamageAll.java | 2 +- .../damage/EnchantmentDamageArthropods.java | 2 +- .../damage/EnchantmentDamageSmite.java | 2 +- .../enchantment/loot/EnchantmentLoot.java | 8 +- .../loot/EnchantmentLootDigging.java | 2 +- .../loot/EnchantmentLootFishing.java | 2 +- .../loot/EnchantmentLootWeapon.java | 2 +- .../protection/EnchantmentProtection.java | 8 +- .../protection/EnchantmentProtectionAll.java | 2 +- .../EnchantmentProtectionExplosion.java | 2 +- .../protection/EnchantmentProtectionFall.java | 2 +- .../protection/EnchantmentProtectionFire.java | 2 +- .../EnchantmentProtectionProjectile.java | 2 +- .../trident/EnchantmentTrident.java | 5 +- .../trident/EnchantmentTridentChanneling.java | 17 +- .../trident/EnchantmentTridentImpaling.java | 16 +- .../trident/EnchantmentTridentLoyalty.java | 9 +- .../trident/EnchantmentTridentRiptide.java | 9 +- src/main/java/cn/nukkit/level/EnumLevel.java | 5 +- .../cn/nukkit/level/GlobalBlockPalette.java | 8 - src/main/java/cn/nukkit/level/Level.java | 11 +- .../cn/nukkit/level/ListChunkManager.java | 85 + src/main/java/cn/nukkit/level/Sound.java | 23 +- .../impl/mushroom/MushroomIslandBiome.java | 1 - .../nukkit/level/biome/type/SandyBiome.java | 2 - .../cn/nukkit/level/format/anvil/Anvil.java | 26 +- .../generator/object/tree/NewJungleTree.java | 4 +- .../populator/impl/PopulatorGroundCover.java | 2 - .../populator/impl/PopulatorOre.java | 1 - .../level/particle/ItemBreakParticle.java | 24 +- .../cn/nukkit/level/particle/Particle.java | 23 + .../level/util/PalettedBlockStorage.java | 3 +- src/main/java/cn/nukkit/metrics/Metrics.java | 533 +++ .../java/cn/nukkit/metrics/NukkitMetrics.java | 170 + src/main/java/cn/nukkit/network/Network.java | 156 +- .../cn/nukkit/network/RakNetInterface.java | 198 +- .../network/protocol/AnvilDamagePacket.java | 32 + .../protocol/AvailableCommandsPacket.java | 31 +- .../protocol/ContainerClosePacket.java | 3 + .../network/protocol/CraftingDataPacket.java | 14 +- .../protocol/CreativeContentPacket.java | 2 +- .../nukkit/network/protocol/DataPacket.java | 12 +- .../network/protocol/EmoteListPacket.java | 4 +- .../nukkit/network/protocol/EmotePacket.java | 4 +- .../network/protocol/FilterTextPacket.java | 30 + .../protocol/InventoryTransactionPacket.java | 1 + .../nukkit/network/protocol/LoginPacket.java | 6 +- .../protocol/MoveEntityDeltaPacket.java | 26 +- .../network/protocol/MovePlayerPacket.java | 3 + .../network/protocol/PlayerListPacket.java | 4 +- .../nukkit/network/protocol/ProtocolInfo.java | 14 +- .../protocol/ResourcePackStackPacket.java | 4 +- .../protocol/ResourcePacksInfoPacket.java | 3 +- .../network/protocol/SetEntityDataPacket.java | 2 + .../network/protocol/StartGamePacket.java | 69 +- .../protocol/UpdateAttributesPacket.java | 2 + .../types/NetworkInventoryAction.java | 83 +- src/main/java/cn/nukkit/potion/Effect.java | 20 +- src/main/java/cn/nukkit/potion/Potion.java | 13 +- src/main/java/cn/nukkit/raknet/RakNet.java | 0 .../cn/nukkit/raknet/protocol/Packet.java | 0 .../nukkit/raknet/server/SessionManager.java | 0 src/main/java/cn/nukkit/utils/Binary.java | 23 +- .../java/cn/nukkit/utils/BinaryStream.java | 64 +- .../utils/DefaultPlayerDataSerializer.java | 16 +- .../java/cn/nukkit/utils/SkinAnimation.java | 4 +- src/main/resources/biome_definitions.dat | Bin 37626 -> 37626 bytes src/main/resources/creativeitems.json | 14 +- src/main/resources/entity_identifiers.dat | Bin 8775 -> 7193 bytes src/main/resources/recipes.json | 23 +- src/main/resources/runtime_block_states.dat | Bin 1264335 -> 1214196 bytes src/main/resources/runtime_item_ids.json | 3231 +---------------- 256 files changed, 4870 insertions(+), 4507 deletions(-) create mode 100644 Dockerfile.arm64 create mode 100644 Makefile create mode 100644 charts/nukkit/.helmignore create mode 100644 charts/nukkit/Chart.yaml create mode 100644 charts/nukkit/templates/NOTES.txt create mode 100644 charts/nukkit/templates/_helpers.tpl create mode 100644 charts/nukkit/templates/configmap.yaml create mode 100644 charts/nukkit/templates/deployment.yaml create mode 100644 charts/nukkit/templates/hpa.yaml create mode 100644 charts/nukkit/templates/service.yaml create mode 100644 charts/nukkit/templates/serviceaccount.yaml create mode 100644 charts/nukkit/templates/tests/test-connection.yaml create mode 100644 charts/nukkit/values.yaml create mode 100644 nukkit.yml.default create mode 100644 src/main/java/cn/nukkit/event/block/AnvilDamageEvent.java create mode 100644 src/main/java/cn/nukkit/event/inventory/InventoryPickupTridentEvent.java create mode 100644 src/main/java/cn/nukkit/event/inventory/RepairItemEvent.java create mode 100644 src/main/java/cn/nukkit/event/level/StructureGrowEvent.java create mode 100644 src/main/java/cn/nukkit/event/server/ServerStopEvent.java create mode 100644 src/main/java/cn/nukkit/inventory/MultiRecipe.java create mode 100644 src/main/java/cn/nukkit/inventory/transaction/RepairItemTransaction.java create mode 100644 src/main/java/cn/nukkit/inventory/transaction/action/RepairItemAction.java create mode 100644 src/main/java/cn/nukkit/item/ItemAxeNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemBootsNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemChestplateNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemCrossbow.java create mode 100644 src/main/java/cn/nukkit/item/ItemHelmetNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemHoeNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemIngotNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemLeggingsNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemNameTag.java create mode 100644 src/main/java/cn/nukkit/item/ItemPickaxeNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemScrapNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemShovelNetherite.java create mode 100644 src/main/java/cn/nukkit/item/ItemSwordNetherite.java create mode 100644 src/main/java/cn/nukkit/item/RuntimeItemMapping.java create mode 100644 src/main/java/cn/nukkit/item/RuntimeItems.java create mode 100644 src/main/java/cn/nukkit/item/enchantment/EnchantmentSoulSpeed.java create mode 100644 src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbow.java create mode 100644 src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowMultishot.java create mode 100644 src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowPiercing.java create mode 100644 src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowQuickCharge.java create mode 100644 src/main/java/cn/nukkit/level/ListChunkManager.java create mode 100644 src/main/java/cn/nukkit/metrics/Metrics.java create mode 100644 src/main/java/cn/nukkit/metrics/NukkitMetrics.java create mode 100644 src/main/java/cn/nukkit/network/protocol/AnvilDamagePacket.java create mode 100644 src/main/java/cn/nukkit/network/protocol/FilterTextPacket.java delete mode 100644 src/main/java/cn/nukkit/raknet/RakNet.java delete mode 100644 src/main/java/cn/nukkit/raknet/protocol/Packet.java delete mode 100644 src/main/java/cn/nukkit/raknet/server/SessionManager.java diff --git a/.gitignore b/.gitignore index 6bb05c99ec6..543cd13b686 100644 --- a/.gitignore +++ b/.gitignore @@ -232,4 +232,6 @@ creativeitems.json recipes.json data/ data/* -/src/main/resources/rebel.xml + +# a file that can be used for your helm chart values +/helm-values.local.yaml diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 new file mode 100644 index 00000000000..045f840c8e0 --- /dev/null +++ b/Dockerfile.arm64 @@ -0,0 +1,33 @@ +FROM arm64v8/openjdk:8-jdk-slim AS build +WORKDIR /usr/local/src/nukkit +COPY src /usr/local/src/nukkit/src +COPY mvn* pom.xml /usr/local/src/nukkit/ +COPY .git /usr/local/src/nukkit/.git +COPY .mvn /usr/local/src/nukkit/.mvn +COPY .gitmodules /usr/local/src/nukkit/.gitmodules +RUN apt-get -y update && \ + apt-get install -y build-essential git maven && \ + git submodule update --init && \ + mvn clean package + +FROM arm64v8/openjdk:8-jre-slim AS run +LABEL maintainer="Chris Fordham " +COPY --from=build /usr/local/src/nukkit/target/nukkit-1.0-SNAPSHOT.jar /opt/nukkit/nukkit.jar +COPY nukkit.yml.default /etc/opt/nukkit/nukkit.yml +RUN useradd --user-group \ + --no-create-home \ + --home-dir /var/opt/nukkit \ + --shell /usr/sbin/nologin \ + minecraft && \ + mkdir -p /var/opt/nukkit && \ + chown -R minecraft /opt/nukkit /var/opt/nukkit /etc/opt/nukkit/nukkit.yml && \ + ln -sfv /etc/opt/nukkit/nukkit.yml /var/opt/nukkit/nukkit.yml && \ + apt-get -y update && \ + apt-get -y install lsof && \ + rm -rf /var/lib/apt/lists/* +USER minecraft +VOLUME /etc/opt/nukkit /var/opt/nukkit /opt/nukkit +EXPOSE 19132 +WORKDIR /var/opt/nukkit +ENTRYPOINT ["java"] +CMD [ "-jar", "/opt/nukkit/nukkit.jar" ] diff --git a/Jenkinsfile b/Jenkinsfile index 03b8192a3ba..9ea4f8740e0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,11 +24,30 @@ pipeline { when { branch "master" } + steps { - sh 'mvn javadoc:javadoc javadoc:jar source:jar deploy -DskipTests' - step([$class: 'JavadocArchiver', - javadocDir: 'target/site/apidocs', - keepAll: false]) + rtMavenDeployer ( + id: "maven-deployer", + serverId: "opencollab-artifactory", + releaseRepo: "maven-releases", + snapshotRepo: "maven-snapshots" + ) + rtMavenResolver ( + id: "maven-resolver", + serverId: "opencollab-artifactory", + releaseRepo: "release", + snapshotRepo: "snapshot" + ) + rtMavenRun ( + pom: 'pom.xml', + goals: 'javadoc:javadoc javadoc:jar source:jar install -DskipTests', + deployerId: "maven-deployer", + resolverId: "maven-resolver" + ) + rtPublishBuildInfo ( + serverId: "opencollab-artifactory" + ) + step([$class: 'JavadocArchiver', javadocDir: 'target/site/apidocs', keepAll: false]) } } } diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..2bf78eea82f --- /dev/null +++ b/Makefile @@ -0,0 +1,85 @@ +DOCKER_REGISTRY = index.docker.io +IMAGE_NAME = nukkit +IMAGE_VERSION = latest +IMAGE_ORG = flaccid +IMAGE_TAG = $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):$(IMAGE_VERSION) +export DOCKER_BUILDKIT = 1 + +WORKING_DIR := $(shell pwd) + +.DEFAULT_GOAL := docker-build + +.PHONY: build run + +# TODO: finish +#build:: ## builds nukkit + +# TODO: finish +#run:: ## runs the binary + +docker-release:: docker-build docker-push ## builds and pushes the docker image to the registry +docker-release-arm64:: docker-build-arm64 docker-push-arm64 ## builds and pushes the arm64 docker image to the registry + +docker-push:: ## pushes the docker image to the registry + @docker push $(IMAGE_TAG) + +docker-push-arm64:: ## pushes the arm64 docker image to the registry + @docker push $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):arm64 + +docker-build:: ## builds the docker image locally + @echo http_proxy=$(HTTP_PROXY) http_proxy=$(HTTPS_PROXY) + @echo building $(IMAGE_TAG) + @docker build --pull \ + --build-arg=http_proxy=$(HTTP_PROXY) \ + --build-arg=https_proxy=$(HTTPS_PROXY) \ + -t $(IMAGE_TAG) $(WORKING_DIR) + +docker-build-arm64:: ## builds the arm64 docker image locally + @echo http_proxy=$(HTTP_PROXY) http_proxy=$(HTTPS_PROXY) + @echo building $(IMAGE_TAG) + @docker build --pull \ + --build-arg=http_proxy=$(HTTP_PROXY) \ + --build-arg=https_proxy=$(HTTPS_PROXY) \ + -f Dockerfile.arm64 \ + -t $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):arm64 $(WORKING_DIR) + +docker-run:: ## runs the docker image locally + @docker run \ + -it \ + -p 19132:19132 \ + $(DOCKER_REGISTRY)/$(IMAGE_ORG)/$(IMAGE_NAME):$(IMAGE_VERSION) + +helm-install:: ## installs using helm from chart in repo + @helm install --name nukkit ./charts/nukkit + +helm-upgrade:: ## upgrades deployed helm release + @helm upgrade nukkit ./charts/nukkit + +helm-purge:: ## deletes and purges deployed helm release + @helm delete --purge nukkit + +helm-render:: ## prints out the rendered chart + @helm install --dry-run --debug charts/nukkit + +helm-validate:: ## runs a lint on the helm chart + @helm lint charts/nukkit + +install-ghr:: ## installs ghr + @cd /tmp + @wget https://github.com/tcnksm/ghr/releases/download/v0.12.2/ghr_v0.12.2_linux_amd64.tar.gz + @tar zxvf ghr_v0.12.2_linux_amd64.tar.gz + @sudo mv ghr_v0.12.2_linux_amd64/ghr /usr/local/bin/ + +# a help target including self-documenting targets (see the awk statement) +define HELP_TEXT +Usage: make [TARGET]... [MAKEVAR1=SOMETHING]... + +Available targets: +endef +export HELP_TEXT +help: ## this help target + @cat .banner + @echo + @echo "$$HELP_TEXT" + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / \ + {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) diff --git a/README.md b/README.md index 6080b4c5e47..15784d085df 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ It has a few key advantages over other server software: * Written in Java, Nukkit is faster and more stable. * Having a friendly structure, it's easy to contribute to Nukkit's development and rewrite plugins from other platforms into Nukkit plugins. -Nukkit is **under improvement** yet, we welcome contributions. +Nukkit is **under improvement** yet, we welcome contributions. Links -------------------- @@ -67,6 +67,51 @@ Use [docker-compose](https://docs.docker.com/compose/overview/) to start server docker-compose up -d ``` +Kubernetes & Helm +------------- + +Validate the chart: + +`helm lint charts/nukkit` + +Dry run and print out rendered YAML: + +`helm install --dry-run --debug nukkit charts/nukkit` + +Install the chart: + +`helm install nukkit charts/nukkit` + +Or, with some different values: + +``` +helm install nukkit \ + --set image.tag="arm64" \ + --set service.type="LoadBalancer" \ + charts/nukkit +``` + +Or, the same but with a custom values from a file: + +``` +helm install nukkit \ + -f helm-values.local.yaml \ + charts/nukkit +``` + +Upgrade the chart: + +`helm upgrade nukkit charts/nukkit` + +Testing after deployment: + +`helm test nukkit` + +Completely remove the chart: + +`helm uninstall nukkit` + + Contributing ------------ Please read the [CONTRIBUTING](.github/CONTRIBUTING.md) guide before submitting any issue. Issues with insufficient information or in the wrong format will be closed and will not be reviewed. diff --git a/charts/nukkit/.helmignore b/charts/nukkit/.helmignore new file mode 100644 index 00000000000..0e8a0eb36f4 --- /dev/null +++ b/charts/nukkit/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/nukkit/Chart.yaml b/charts/nukkit/Chart.yaml new file mode 100644 index 00000000000..18fb6920fba --- /dev/null +++ b/charts/nukkit/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: nukkit +description: A Helm chart for Nukkit +type: application +version: 0.1.0 +appVersion: 0.1.0 diff --git a/charts/nukkit/templates/NOTES.txt b/charts/nukkit/templates/NOTES.txt new file mode 100644 index 00000000000..996aa14e3e1 --- /dev/null +++ b/charts/nukkit/templates/NOTES.txt @@ -0,0 +1,15 @@ +1. Get the application URL by running these commands: +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "nukkit.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "nukkit.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "nukkit.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "nukkit.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/charts/nukkit/templates/_helpers.tpl b/charts/nukkit/templates/_helpers.tpl new file mode 100644 index 00000000000..09bfbe78fd2 --- /dev/null +++ b/charts/nukkit/templates/_helpers.tpl @@ -0,0 +1,63 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "nukkit.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "nukkit.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "nukkit.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "nukkit.labels" -}} +helm.sh/chart: {{ include "nukkit.chart" . }} +{{ include "nukkit.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "nukkit.selectorLabels" -}} +app.kubernetes.io/name: {{ include "nukkit.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "nukkit.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "nukkit.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/nukkit/templates/configmap.yaml b/charts/nukkit/templates/configmap.yaml new file mode 100644 index 00000000000..00cbcee8771 --- /dev/null +++ b/charts/nukkit/templates/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "nukkit.name" . }}-conf + labels: + app: {{ template "nukkit.name" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +data: + nukkit.yml: |- +{{- if .Values.config }} +{{ .Values.config | indent 4 }} +{{- end -}} diff --git a/charts/nukkit/templates/deployment.yaml b/charts/nukkit/templates/deployment.yaml new file mode 100644 index 00000000000..5937c9d08fe --- /dev/null +++ b/charts/nukkit/templates/deployment.yaml @@ -0,0 +1,70 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "nukkit.fullname" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} +spec: +{{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} +{{- end }} + selector: + matchLabels: + {{- include "nukkit.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "nukkit.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "nukkit.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: {{ template "nukkit.name" . }}-conf + subPath: nukkit.yml + mountPath: /var/opt/nukkit/nukkit.yml + ports: + - name: udp + containerPort: 19132 + protocol: UDP + # TODO: fix/re-test + # readinessProbe: + # exec: + # command: + # - lsof + # - -i + # - :19132 + # initialDelaySeconds: 3 + # periodSeconds: 5 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumes: + - name: {{ template "nukkit.name" . }}-conf + configMap: + name: {{ template "nukkit.name" . }}-conf + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/nukkit/templates/hpa.yaml b/charts/nukkit/templates/hpa.yaml new file mode 100644 index 00000000000..205cc5415fa --- /dev/null +++ b/charts/nukkit/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "nukkit.fullname" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "nukkit.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/nukkit/templates/service.yaml b/charts/nukkit/templates/service.yaml new file mode 100644 index 00000000000..58dfb2f8303 --- /dev/null +++ b/charts/nukkit/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "nukkit.fullname" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: udp + protocol: UDP + name: udp +{{- if eq .Values.service.port "NodePort" }} + nodePort: {{ .Values.service.nodePort }} +{{- end }} + selector: + {{- include "nukkit.selectorLabels" . | nindent 4 }} +{{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} +{{- end }} diff --git a/charts/nukkit/templates/serviceaccount.yaml b/charts/nukkit/templates/serviceaccount.yaml new file mode 100644 index 00000000000..dc56a543579 --- /dev/null +++ b/charts/nukkit/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "nukkit.serviceAccountName" . }} + labels: + {{- include "nukkit.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/nukkit/templates/tests/test-connection.yaml b/charts/nukkit/templates/tests/test-connection.yaml new file mode 100644 index 00000000000..3c0330acc4b --- /dev/null +++ b/charts/nukkit/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "nukkit.fullname" . }}-test-connection" + labels: + {{- include "nukkit.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "nukkit.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/charts/nukkit/values.yaml b/charts/nukkit/values.yaml new file mode 100644 index 00000000000..8f6721ece63 --- /dev/null +++ b/charts/nukkit/values.yaml @@ -0,0 +1,74 @@ +# Default values for nukkit. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: flaccid/nukkit + pullPolicy: Always + # Overrides the image tag whose default is the chart appVersion. + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 19132 + loadBalancerIP: "" + nodePort: 39132 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +config: | + settings: + # Multi-language setting + # Available: eng, chs, cht, jpn, rus, spa, pol, bra, kor, ukr, deu + language: "eng" diff --git a/nukkit.yml.default b/nukkit.yml.default new file mode 100644 index 00000000000..3d4d2aae20f --- /dev/null +++ b/nukkit.yml.default @@ -0,0 +1,4 @@ +settings: + # Multi-language setting + # Available: eng, chs, cht, jpn, rus, spa, pol, bra, kor, ukr, deu + language: "eng" diff --git a/pom.xml b/pom.xml index 103ca385de2..dafe13a28ac 100644 --- a/pom.xml +++ b/pom.xml @@ -19,8 +19,8 @@ - nukkitx-repo-release - https://repo.nukkitx.com/maven-releases/ + opencollab-repo-release + https://repo.opencollab.dev/maven-releases/ true @@ -29,8 +29,8 @@ - nukkitx-repo-snapshot - https://repo.nukkitx.com/maven-snapshots/ + opencollab-repo-snapshot + https://repo.opencollab.dev/maven-snapshots/ false @@ -40,24 +40,11 @@ - - - releases - nukkitx-releases - https://repo.nukkitx.com/release - - - snapshots - nukkitx-snapshots - https://repo.nukkitx.com/snapshot - - - com.nukkitx.network raknet - 1.6.20 + 1.6.25 compile @@ -75,7 +62,7 @@ com.google.code.gson gson - 2.4 + 2.8.6 compile diff --git a/src/main/java/cn/nukkit/AdventureSettings.java b/src/main/java/cn/nukkit/AdventureSettings.java index d49304b5389..bceac5c162d 100644 --- a/src/main/java/cn/nukkit/AdventureSettings.java +++ b/src/main/java/cn/nukkit/AdventureSettings.java @@ -53,7 +53,7 @@ public void update() { } pk.commandPermission = (player.isOp() ? AdventureSettingsPacket.PERMISSION_OPERATOR : AdventureSettingsPacket.PERMISSION_NORMAL); - pk.playerPermission = (player.isOp() ? Player.PERMISSION_OPERATOR : Player.PERMISSION_MEMBER); + pk.playerPermission = (player.isOp() && !player.isSpectator() ? Player.PERMISSION_OPERATOR : Player.PERMISSION_MEMBER); pk.entityUniqueId = player.getId(); Server.broadcastPacket(player.getViewers().values(), pk); diff --git a/src/main/java/cn/nukkit/Nukkit.java b/src/main/java/cn/nukkit/Nukkit.java index 783fb0b45be..259f6aa3453 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.11"; + public final static String API_VERSION = "1.0.12"; 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 50b27294cb8..28c2b77343e 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -23,6 +23,7 @@ import cn.nukkit.event.inventory.InventoryCloseEvent; import cn.nukkit.event.inventory.InventoryPickupArrowEvent; import cn.nukkit.event.inventory.InventoryPickupItemEvent; +import cn.nukkit.event.inventory.InventoryPickupTridentEvent; import cn.nukkit.event.player.*; import cn.nukkit.event.player.PlayerAsyncPreLoginEvent.LoginResult; import cn.nukkit.event.player.PlayerInteractEvent.Action; @@ -35,6 +36,7 @@ import cn.nukkit.inventory.transaction.CraftingTransaction; import cn.nukkit.inventory.transaction.EnchantTransaction; import cn.nukkit.inventory.transaction.InventoryTransaction; +import cn.nukkit.inventory.transaction.RepairItemTransaction; import cn.nukkit.inventory.transaction.action.InventoryAction; import cn.nukkit.inventory.transaction.data.ReleaseItemData; import cn.nukkit.inventory.transaction.data.UseItemData; @@ -140,6 +142,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 closingWindowId = Integer.MIN_VALUE; protected int messageCounter = 2; @@ -155,6 +158,7 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde protected CraftingGrid craftingGrid; protected CraftingTransaction craftingTransaction; protected EnchantTransaction enchantTransaction; + protected RepairItemTransaction repairItemTransaction; public long creationTime = 0; @@ -205,8 +209,6 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde protected boolean checkMovement = true; - private final Map> batchedPackets = new TreeMap<>(); - private PermissibleBase perm = null; private int exp = 0; @@ -251,6 +253,8 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde protected double lastRightClickTime = 0.0; protected Vector3 lastRightClickPos = null; + private int timeSinceRest; + public int getStartActionTick() { return startAction; } @@ -777,7 +781,7 @@ public void sendChunk(int x, int z, int subChunkCount, byte[] payload) { pk.subChunkCount = subChunkCount; pk.data = payload; - this.batchDataPacket(pk); + this.dataPacket(pk); if (this.spawned) { for (Entity entity : this.level.getChunkEntities(x, z).values()) { @@ -839,16 +843,10 @@ protected void sendNextChunk() { protected void doFirstSpawn() { this.spawned = true; - this.setEnableClientCommand(true); - - this.getAdventureSettings().update(); - this.sendAttributes(); - - this.sendPotionEffects(this); - this.sendData(this); this.inventory.sendContents(this); this.inventory.sendArmorContents(this); this.offhandInventory.sendContents(this); + this.setEnableClientCommand(true); SetTimePacket setTimePacket = new SetTimePacket(); setTimePacket.time = this.level.getTime(); @@ -879,7 +877,6 @@ protected void doFirstSpawn() { this.noDamageTicks = 60; this.getServer().sendRecipeList(this); - inventory.sendCreativeContents(); for (long index : this.usedChunks.keySet()) { @@ -911,9 +908,6 @@ protected void doFirstSpawn() { //todo Updater //Weather - if (this.level.isRaining() || this.level.isThundering()) { - this.getLevel().sendWeather(this); - } this.getLevel().sendWeather(this); //FoodLevel @@ -1017,25 +1011,9 @@ protected boolean orderChunks() { return true; } + @Deprecated public boolean batchDataPacket(DataPacket packet) { - if (!this.connected) { - return false; - } - - try (Timing timing = Timings.getSendDataPacketTiming(packet)) { - DataPacketSendEvent event = new DataPacketSendEvent(this, packet); - this.server.getPluginManager().callEvent(event); - if (event.isCancelled()) { - return false; - } - - if (!this.batchedPackets.containsKey(packet.getChannel())) { - this.batchedPackets.put(packet.getChannel(), new ArrayList<>()); - } - - this.batchedPackets.get(packet.getChannel()).add(packet.clone()); - } - return true; + return this.dataPacket(packet); } /** @@ -1049,7 +1027,8 @@ public boolean dataPacket(DataPacket packet) { if (!this.connected) { return false; } - try (Timing timing = Timings.getSendDataPacketTiming(packet)) { + + try (Timing ignored = Timings.getSendDataPacketTiming(packet)) { DataPacketSendEvent ev = new DataPacketSendEvent(this, packet); this.server.getPluginManager().callEvent(ev); if (ev.isCancelled()) { @@ -1060,14 +1039,14 @@ public boolean dataPacket(DataPacket packet) { log.trace("Outbound {}: {}", this.getName(), packet); } - this.interfaz.putPacket(this, packet, false, false); + this.interfaz.putPacket(this, packet, false, true); } return true; } @Deprecated public int dataPacket(DataPacket packet, boolean needACK) { - return this.dataPacket(packet) ? 0 : -1; + return dataPacket(packet) ? 1 : 0; } /** @@ -1077,26 +1056,14 @@ public int dataPacket(DataPacket packet, boolean needACK) { * @param packet packet to send * @return packet successfully sent */ + @Deprecated public boolean directDataPacket(DataPacket packet) { - if (!this.connected) { - return false; - } - - try (Timing timing = Timings.getSendDataPacketTiming(packet)) { - DataPacketSendEvent ev = new DataPacketSendEvent(this, packet); - this.server.getPluginManager().callEvent(ev); - if (ev.isCancelled()) { - return false; - } - - this.interfaz.putPacket(this, packet, false, true); - } - return true; + return this.dataPacket(packet); } @Deprecated public int directDataPacket(DataPacket packet, boolean needACK) { - return this.directDataPacket(packet) ? 0 : -1; + return this.dataPacket(packet) ? 1 : 0; } public int getPing() { @@ -1132,6 +1099,8 @@ public boolean sleepOn(Vector3 pos) { this.level.sleepTicks = 60; + this.timeSinceRest = 0; + return true; } @@ -1464,7 +1433,7 @@ protected void processMovement(int tickDiff) { if (this.checkMovement && !server.getAllowFlight() && (this.isSurvival() || this.isAdventure())) { // Some say: I cant move my head when riding because the server // blocked my movement - if (!this.isSleeping() && this.riding == null && !this.hasEffect(Effect.LEVITATION)) { + if (!this.isSleeping() && this.riding == null && !this.hasEffect(Effect.LEVITATION) && !this.hasEffect(Effect.SLOW_FALLING)) { double diffHorizontalSqr = (diffX * diffX + diffZ * diffZ) / ((double) (tickDiff * tickDiff)); if (diffHorizontalSqr > 0.5) { PlayerInvalidMoveEvent ev; @@ -1701,7 +1670,7 @@ public boolean onUpdate(int currentTick) { this.inAirTicks = 0; this.highestPosition = this.y; } else { - if (this.checkMovement && !this.isGliding() && !server.getAllowFlight() && !this.getAdventureSettings().get(Type.ALLOW_FLIGHT) && this.inAirTicks > 20 && !this.isSleeping() && !this.isImmobile() && !this.isSwimming() && this.riding == null && !this.hasEffect(Effect.LEVITATION)) { + if (this.checkMovement && !this.isGliding() && !server.getAllowFlight() && !this.getAdventureSettings().get(Type.ALLOW_FLIGHT) && this.inAirTicks > 20 && !this.isSleeping() && !this.isImmobile() && !this.isSwimming() && this.riding == null && !this.hasEffect(Effect.LEVITATION) && !this.hasEffect(Effect.SLOW_FALLING)) { double expectedVelocity = (-this.getGravity()) / ((double) this.getDrag()) - ((-this.getGravity()) / ((double) this.getDrag())) * Math.exp(-((double) this.getDrag()) * ((double) (this.inAirTicks - this.startAirTicks))); double diff = (this.speed.y - expectedVelocity) * (this.speed.y - expectedVelocity); @@ -1734,6 +1703,10 @@ public boolean onUpdate(int currentTick) { if (this.getFoodData() != null) this.getFoodData().update(tickDiff); } } + + if (!this.isSleeping()) { + this.timeSinceRest++; + } } this.checkTeleportPosition(); @@ -1817,17 +1790,6 @@ public void checkNetwork() { return; } - if (!this.batchedPackets.isEmpty()) { - Player[] pArr = new Player[]{this}; - for (Entry> entry : this.batchedPackets.entrySet()) { - List packets = entry.getValue(); - DataPacket[] arr = packets.toArray(new DataPacket[0]); - packets.clear(); - this.server.batchPackets(pArr, arr, false); - } - this.batchedPackets.clear(); - } - if (this.nextChunkOrderRun-- <= 0 || this.chunk == null) { this.orderChunks(); } @@ -1835,7 +1797,6 @@ public void checkNetwork() { if (!this.loadQueue.isEmpty() || !this.spawned) { this.sendNextChunk(); } - } public boolean canInteract(Vector3 pos, double maxDistance) { @@ -1986,6 +1947,11 @@ protected void processLogin() { this.forceMovement = this.teleportPosition = this.getPosition(); + if (!this.namedTag.contains("TimeSinceRest")) { + this.namedTag.putInt("TimeSinceRest", 0); + } + this.timeSinceRest = this.namedTag.getInt("TimeSinceRest"); + ResourcePacksInfoPacket infoPacket = new ResourcePacksInfoPacket(); infoPacket.resourcePackEntries = this.server.getResourcePackManager().getResourceStack(); infoPacket.mustAccept = this.server.getForceResources(); @@ -2034,11 +2000,17 @@ protected void completeLoginSequence() { startGamePacket.levelId = ""; startGamePacket.worldName = this.getServer().getNetwork().getName(); startGamePacket.generator = 1; //0 old, 1 infinite, 2 flat - //startGamePacket.isInventoryServerAuthoritative = true; this.dataPacket(startGamePacket); this.dataPacket(new BiomeDefinitionListPacket()); this.dataPacket(new AvailableEntityIdentifiersPacket()); + this.inventory.sendCreativeContents(); + this.getAdventureSettings().update(); + + this.sendAttributes(); + + this.sendPotionEffects(this); + this.sendData(this); this.loggedIn = true; @@ -2072,7 +2044,7 @@ public void handleDataPacket(DataPacket packet) { return; } - try (Timing timing = Timings.getReceiveDataPacketTiming(packet)) { + try (Timing ignored = Timings.getReceiveDataPacketTiming(packet)) { DataPacketReceiveEvent ev = new DataPacketReceiveEvent(this, packet); this.server.getPluginManager().callEvent(ev); if (ev.isCancelled()) { @@ -2114,7 +2086,7 @@ public void handleDataPacket(DataPacket packet) { disconnectPacket.encode(); BatchPacket batch = new BatchPacket(); batch.payload = disconnectPacket.getBuffer(); - this.directDataPacket(batch); + this.dataPacket(batch); // Still want to run close() to allow the player to be removed properly } this.close("", message, false); @@ -2185,33 +2157,33 @@ public void handleDataPacket(DataPacket packet) { Player playerInstance = this; this.preLoginEventTask = new AsyncTask() { - - private PlayerAsyncPreLoginEvent e; + private PlayerAsyncPreLoginEvent event; @Override public void onRun() { - e = new PlayerAsyncPreLoginEvent(username, uuid, Player.this.getAddress(), Player.this.getPort()); - server.getPluginManager().callEvent(e); + this.event = new PlayerAsyncPreLoginEvent(username, uuid, loginChainData, playerInstance.getSkin(), playerInstance.getAddress(), playerInstance.getPort()); + server.getPluginManager().callEvent(this.event); } @Override public void onCompletion(Server server) { - if (!playerInstance.closed) { - if (e.getLoginResult() == LoginResult.KICK) { - playerInstance.close(e.getKickMessage(), e.getKickMessage()); - } else if (playerInstance.shouldLogin) { - playerInstance.completeLoginSequence(); - - for (Consumer action : e.getScheduledActions()) { - action.accept(server); - } + if (playerInstance.closed) { + return; + } + + if (this.event.getLoginResult() == LoginResult.KICK) { + playerInstance.close(this.event.getKickMessage(), this.event.getKickMessage()); + } else if (playerInstance.shouldLogin) { + playerInstance.setSkin(this.event.getSkin()); + playerInstance.completeLoginSequence(); + for (Consumer action : this.event.getScheduledActions()) { + action.accept(server); } } } }; this.server.getScheduler().scheduleAsyncTask(this.preLoginEventTask); - this.processLogin(); break; case ProtocolInfo.RESOURCE_PACK_CLIENT_RESPONSE_PACKET: @@ -2293,7 +2265,7 @@ public void onCompletion(Server server) { break; case ProtocolInfo.PACKET_VIOLATION_WARNING_PACKET: - log.warn("Received packet violation warning: " + packet.toString()); + log.warn("Violation warning from {}: {}", this.getName(), packet.toString()); break; case ProtocolInfo.EMOTE_PACKET: for (Player viewer : this.getViewers().values()) { @@ -2775,6 +2747,15 @@ public void onCompletion(Server server) { this.dataPacket(entityEventPacket); Server.broadcastPacket(this.getViewers().values(), entityEventPacket); + } else if (entityEventPacket.event == EntityEventPacket.ENCHANT) { + if (entityEventPacket.eid != this.id) { + break; + } + + Inventory inventory = this.getWindowById(ANVIL_WINDOW_ID); + if (inventory instanceof AnvilInventory) { + ((AnvilInventory) inventory).setCost(-entityEventPacket.data); + } } break; case ProtocolInfo.COMMAND_REQUEST_PACKET: @@ -2819,13 +2800,16 @@ public void onCompletion(Server server) { 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)); + this.closingWindowId = containerClosePacket.windowId; + this.removeWindow(this.windowIndex.get(containerClosePacket.windowId), true); + this.closingWindowId = Integer.MIN_VALUE; } if (containerClosePacket.windowId == -1) { this.craftingType = CRAFTING_SMALL; this.resetCraftingGridType(); this.addWindow(this.craftingGrid, ContainerIds.NONE); ContainerClosePacket pk = new ContainerClosePacket(); + pk.wasServerInitiated = false; pk.windowId = -1; this.dataPacket(pk); } @@ -2995,6 +2979,19 @@ public void onCompletion(Server server) { this.enchantTransaction = null; } return; + } else if (transactionPacket.isRepairItemPart) { + if (this.repairItemTransaction == null) { + this.repairItemTransaction = new RepairItemTransaction(this, actions); + } else { + for (InventoryAction action : actions) { + this.repairItemTransaction.addAction(action); + } + } + if (this.repairItemTransaction.canExecute()) { + this.repairItemTransaction.execute(); + this.repairItemTransaction = null; + } + return; } else if (this.craftingTransaction != null) { if (craftingTransaction.checkForCraftingPart(actions)) { for (InventoryAction action : actions) { @@ -3019,6 +3016,18 @@ public void onCompletion(Server server) { this.sendAllInventories(); this.enchantTransaction = null; } + } else if (this.repairItemTransaction != null) { + if (RepairItemTransaction.checkForRepairItemPart(actions)) { + for (InventoryAction action : actions) { + this.repairItemTransaction.addAction(action); + } + return; + } else { + this.server.getLogger().debug("Got unexpected normal inventory action with incomplete repair item transaction from " + this.getName() + ", refusing to execute repair item " + transactionPacket.toString()); + this.removeAllWindows(false); + this.sendAllInventories(); + this.repairItemTransaction = null; + } } switch (transactionPacket.transactionType) { @@ -3147,7 +3156,7 @@ public void onCompletion(Server server) { } if (item.onClickAir(this, directionVector)) { - if (this.isSurvival()) { + if (!this.isCreative()) { this.inventory.setItemInHand(item); } @@ -3379,6 +3388,14 @@ public void onCompletion(Server server) { } } break; + case ProtocolInfo.FILTER_TEXT_PACKET: + FilterTextPacket filterTextPacket = (FilterTextPacket) packet; + + FilterTextPacket textResponsePacket = new FilterTextPacket(); + textResponsePacket.text = filterTextPacket.text; + textResponsePacket.fromServer = true; + this.dataPacket(textResponsePacket); + break; default: break; } @@ -3648,7 +3665,7 @@ public void close(TextContainer message, String reason, boolean notify) { if (notify && reason.length() > 0) { DisconnectPacket pk = new DisconnectPacket(); pk.message = reason; - this.directDataPacket(pk); + this.dataPacket(pk); } this.connected = false; @@ -3772,6 +3789,8 @@ public void save(boolean async) { this.namedTag.putInt("foodLevel", this.getFoodData().getLevel()); this.namedTag.putFloat("foodSaturationLevel", this.getFoodData().getFoodSaturationLevel()); + this.namedTag.putInt("TimeSinceRest", this.timeSinceRest); + if (!this.username.isEmpty() && this.namedTag != null) { this.server.saveOfflinePlayerData(this.uuid, this.namedTag, async); } @@ -3912,7 +3931,7 @@ public void kill() { ev.setKeepExperience(this.level.gameRules.getBoolean(GameRule.KEEP_INVENTORY)); ev.setKeepInventory(ev.getKeepExperience()); - if (cause != null && cause.getCause() != DamageCause.VOID) { + if (cause != null && cause.getCause() != DamageCause.VOID && cause.getCause() != DamageCause.SUICIDE) { PlayerOffhandInventory offhandInventory = this.getOffhandInventory(); PlayerInventory playerInventory = this.getInventory(); if (offhandInventory.getItem(0).getId() == Item.TOTEM || playerInventory.getItemInHand().getId() == Item.TOTEM) { @@ -3953,7 +3972,9 @@ public void kill() { if (!ev.getKeepInventory() && this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) { for (Item item : ev.getDrops()) { - this.level.dropItem(this, item, null, true, 40); + if (!item.hasEnchantment(Enchantment.ID_VANISHING_CURSE)) { + this.level.dropItem(this, item, null, true, 40); + } } if (this.inventory != null) { @@ -3973,6 +3994,8 @@ public void kill() { this.setExperience(0, 0); } + this.timeSinceRest = 0; + if (showMessages && !ev.getDeathMessage().toString().isEmpty()) { this.server.broadcast(ev.getDeathMessage(), Server.BROADCAST_CHANNEL_USERS); } @@ -4341,11 +4364,7 @@ protected void sendPlayStatus(int status, boolean immediate) { PlayStatusPacket pk = new PlayStatusPacket(); pk.status = status; - if (immediate) { - this.directDataPacket(pk); - } else { - this.dataPacket(pk); - } + this.dataPacket(pk); } @Override @@ -4626,8 +4645,12 @@ public Optional getTopWindow() { } public void removeWindow(Inventory inventory) { + this.removeWindow(inventory, false); + } + + protected void removeWindow(Inventory inventory, boolean isResponse) { inventory.close(this); - if (!this.permanentWindows.contains(this.getWindowId(inventory))) + if (isResponse && !this.permanentWindows.contains(this.getWindowId(inventory))) this.windows.remove(inventory); } @@ -4716,6 +4739,10 @@ public void removeAllWindows(boolean permanent) { } } + public int getClosingWindowId() { + return this.closingWindowId; + } + @Override public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { this.server.getPlayerMetadata().setMetadata(this, metadataKey, newMetadataValue); @@ -4816,7 +4843,7 @@ private void setDimension(int dimension) { pk.x = (float) this.x; pk.y = (float) this.y; pk.z = (float) this.z; - this.directDataPacket(pk); + this.dataPacket(pk); } @Override @@ -4882,8 +4909,6 @@ public void transfer(InetSocketAddress address) { pk.address = hostName; pk.port = port; this.dataPacket(pk); - String message = "Transferred to " + hostName + ":" + port; - this.close("", message, false); } public LoginChainData getLoginChainData() { @@ -4891,7 +4916,7 @@ public LoginChainData getLoginChainData() { } public boolean pickupEntity(Entity entity, boolean near) { - if (!this.spawned || !this.isAlive() || !this.isOnline() || this.getGamemode() == SPECTATOR || entity.isClosed()) { + if (!this.spawned || !this.isAlive() || !this.isOnline() || this.isSpectator() || entity.isClosed()) { return false; } @@ -4931,6 +4956,12 @@ public boolean pickupEntity(Entity entity, boolean near) { return false; } + InventoryPickupTridentEvent ev = new InventoryPickupTridentEvent(this.inventory, (EntityThrownTrident) entity); + this.server.getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + return false; + } + TakeItemEntityPacket pk = new TakeItemEntityPacket(); pk.entityId = this.getId(); pk.target = entity.getId(); @@ -5106,4 +5137,12 @@ public String toString() { "', location=" + super.toString() + ')'; } + + public int getTimeSinceRest() { + return timeSinceRest; + } + + public void setTimeSinceRest(int timeSinceRest) { + this.timeSinceRest = timeSinceRest; + } } diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index 663a119cbb7..900e62fd4ee 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -20,6 +20,7 @@ import cn.nukkit.event.server.BatchPacketsEvent; import cn.nukkit.event.server.PlayerDataSerializeEvent; import cn.nukkit.event.server.QueryRegenerateEvent; +import cn.nukkit.event.server.ServerStopEvent; import cn.nukkit.inventory.CraftingManager; import cn.nukkit.inventory.Recipe; import cn.nukkit.item.Item; @@ -45,6 +46,7 @@ import cn.nukkit.metadata.EntityMetadataStore; import cn.nukkit.metadata.LevelMetadataStore; import cn.nukkit.metadata.PlayerMetadataStore; +import cn.nukkit.metrics.NukkitMetrics; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.DoubleTag; @@ -246,7 +248,7 @@ public Level remove(Object key) { private DB nameLookup; - private PlayerDataSerializer playerDataSerializer = new DefaultPlayerDataSerializer(this); + private PlayerDataSerializer playerDataSerializer; private final Set ignoredPackets = new HashSet<>(); @@ -275,6 +277,8 @@ public Level remove(Object key) { this.consoleThread = new ConsoleThread(); this.consoleThread.start(); + this.playerDataSerializer = new DefaultPlayerDataSerializer(this); + //todo: VersionString 现在不必要 if (!new File(this.dataPath + "nukkit.yml").exists()) { @@ -462,6 +466,9 @@ public Level remove(Object key) { this.consoleSender = new ConsoleCommandSender(); this.commandMap = new SimpleCommandMap(this); + // Initialize metrics + new NukkitMetrics(this); + this.registerEntities(); this.registerBlockEntities(); @@ -654,26 +661,27 @@ public int broadcast(TextContainer message, String permissions) { } public static void broadcastPacket(Collection players, DataPacket packet) { - broadcastPacket(players.toArray(new Player[0]), packet); + packet.tryEncode(); + + for (Player player : players) { + player.dataPacket(packet); + } } public static void broadcastPacket(Player[] players, DataPacket packet) { - packet.encode(); - packet.isEncoded = true; + packet.tryEncode(); - if (packet.pid() == ProtocolInfo.BATCH_PACKET) { - for (Player player : players) { - player.dataPacket(packet); - } - } else { - getInstance().batchPackets(players, new DataPacket[]{packet}, true); + for (Player player : players) { + player.dataPacket(packet); } } + @Deprecated public void batchPackets(Player[] players, DataPacket[] packets) { this.batchPackets(players, packets, false); } + @Deprecated public void batchPackets(Player[] players, DataPacket[] packets, boolean forceSync) { if (players == null || packets == null || players.length == 0 || packets.length == 0) { return; @@ -687,18 +695,14 @@ public void batchPackets(Player[] players, DataPacket[] packets, boolean forceSy Timings.playerNetworkSendTimer.startTiming(); byte[][] payload = new byte[packets.length * 2][]; - int size = 0; for (int i = 0; i < packets.length; i++) { DataPacket p = packets[i]; - if (!p.isEncoded) { - p.encode(); - } + int idx = i * 2; + p.tryEncode(); byte[] buf = p.getBuffer(); - payload[i * 2] = Binary.writeUnsignedVarInt(buf.length); - payload[i * 2 + 1] = buf; + payload[idx] = Binary.writeUnsignedVarInt(buf.length); + payload[idx + 1] = buf; packets[i] = null; - size += payload[i * 2].length; - size += payload[i * 2 + 1].length; } List targets = new ArrayList<>(); @@ -836,6 +840,9 @@ public void forceShutdown() { this.hasStopped = true; + ServerStopEvent serverStopEvent = new ServerStopEvent(); + getPluginManager().callEvent(serverStopEvent); + if (this.rcon != null) { this.rcon.close(); } @@ -1040,6 +1047,13 @@ public void removePlayerListData(UUID uuid, Player[] players) { Server.broadcastPacket(players, pk); } + public void removePlayerListData(UUID uuid, Player player) { + PlayerListPacket pk = new PlayerListPacket(); + pk.type = PlayerListPacket.TYPE_REMOVE; + pk.entries = new PlayerListPacket.Entry[]{new PlayerListPacket.Entry(uuid)}; + player.dataPacket(pk); + } + public void removePlayerListData(UUID uuid, Collection players) { this.removePlayerListData(uuid, players.toArray(new Player[0])); } @@ -1436,7 +1450,7 @@ public static int getDifficultyFromString(String str) { public int getDifficulty() { if (this.difficulty == Integer.MAX_VALUE) { - this.difficulty = this.getPropertyInt("difficulty", 1); + this.difficulty = getDifficultyFromString(this.getPropertyString("difficulty", "1")); } return this.difficulty; } @@ -1472,7 +1486,11 @@ public String getMotd() { } public String getSubMotd() { - return this.getPropertyString("sub-motd", "https://nukkitx.com"); + String subMotd = this.getPropertyString("sub-motd", "https://nukkitx.com"); + if (subMotd.isEmpty()) { + subMotd = "https://nukkitx.com"; // The client doesn't allow empty sub-motd in 1.16.210 + } + return subMotd; } public boolean getForceResources() { @@ -2104,8 +2122,8 @@ public String getPropertyString(String variable) { return this.getPropertyString(variable, null); } - public String getPropertyString(String variable, String defaultValue) { - return this.properties.exists(variable) ? (String) this.properties.get(variable) : defaultValue; + public String getPropertyString(String key, String defaultValue) { + return this.properties.exists(key) ? this.properties.get(key).toString() : defaultValue; } public int getPropertyInt(String variable) { diff --git a/src/main/java/cn/nukkit/block/Block.java b/src/main/java/cn/nukkit/block/Block.java index 54f13304a55..bb4a2f193bb 100644 --- a/src/main/java/cn/nukkit/block/Block.java +++ b/src/main/java/cn/nukkit/block/Block.java @@ -559,10 +559,16 @@ public Item[] getDrops(Item item) { } } - private static double toolBreakTimeBonus0( - int toolType, int toolTier, boolean isWoolBlock, boolean isCobweb) { - if (toolType == ItemTool.TYPE_SWORD) return isCobweb ? 15.0 : 1.0; - if (toolType == ItemTool.TYPE_SHEARS) return isWoolBlock ? 5.0 : 15.0; + private static double toolBreakTimeBonus0(int toolType, int toolTier, int blockId) { + if (toolType == ItemTool.TYPE_SWORD) return blockId == Block.COBWEB ? 15.0 : 1.0; + if (toolType == ItemTool.TYPE_SHEARS) { + if (blockId == Block.WOOL || blockId == LEAVES || blockId == LEAVES2) { + return 5.0; + } else if (blockId == COBWEB) { + return 15.0; + } + return 1.0; + } if (toolType == ItemTool.TYPE_NONE) return 1.0; switch (toolTier) { case ItemTool.TIER_WOODEN: @@ -573,6 +579,8 @@ private static double toolBreakTimeBonus0( return 6.0; case ItemTool.TIER_DIAMOND: return 8.0; + case ItemTool.TIER_NETHERITE: + return 9.0; case ItemTool.TIER_GOLD: return 12.0; default: @@ -594,6 +602,7 @@ private static int toolType0(Item item) { if (item.isShovel()) return ItemTool.TYPE_SHOVEL; if (item.isPickaxe()) return ItemTool.TYPE_PICKAXE; if (item.isAxe()) return ItemTool.TYPE_AXE; + if (item.isHoe()) return ItemTool.TYPE_HOE; if (item.isShears()) return ItemTool.TYPE_SHEARS; return ItemTool.TYPE_NONE; } @@ -603,6 +612,7 @@ private static boolean correctTool0(int blockToolType, Item item) { (blockToolType == ItemTool.TYPE_SHOVEL && item.isShovel()) || (blockToolType == ItemTool.TYPE_PICKAXE && item.isPickaxe()) || (blockToolType == ItemTool.TYPE_AXE && item.isAxe()) || + (blockToolType == ItemTool.TYPE_HOE && item.isHoe()) || (blockToolType == ItemTool.TYPE_SHEARS && item.isShears()) || blockToolType == ItemTool.TYPE_NONE; } @@ -613,8 +623,7 @@ private static double breakTime0(double blockHardness, boolean correctTool, bool boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround) { double baseTime = ((correctTool || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness; double speed = 1.0 / baseTime; - boolean isWoolBlock = blockId == Block.WOOL, isCobweb = blockId == Block.COBWEB; - if (correctTool) speed *= toolBreakTimeBonus0(toolType, toolTier, isWoolBlock, isCobweb); + if (correctTool) speed *= toolBreakTimeBonus0(toolType, toolTier, blockId); speed += speedBonusByEfficiencyLore0(efficiencyLoreLevel); speed *= speedRateByHasteLore0(hasteEffectLevel); if (insideOfWaterWithoutAquaAffinity) speed *= 0.2; @@ -631,9 +640,10 @@ public double getBreakTime(Item item, Player player) { return 0; } - boolean correctTool = correctTool0(getToolType(), item); - boolean canHarvestWithHand = canHarvestWithHand(); int blockId = getId(); + boolean correctTool = correctTool0(getToolType(), item) + || item.isShears() && (blockId == COBWEB || blockId == LEAVES || blockId == LEAVES2); + boolean canHarvestWithHand = canHarvestWithHand(); int itemToolType = toolType0(item); int itemTier = item.getTier(); int efficiencyLoreLevel = Optional.ofNullable(item.getEnchantment(Enchantment.ID_EFFICIENCY)) @@ -662,7 +672,8 @@ public double getBreakTime(Item item) { } else if ( (this.getToolType() == ItemTool.TYPE_PICKAXE && item.isPickaxe()) || (this.getToolType() == ItemTool.TYPE_AXE && item.isAxe()) || - (this.getToolType() == ItemTool.TYPE_SHOVEL && item.isShovel()) + (this.getToolType() == ItemTool.TYPE_SHOVEL && item.isShovel()) || + (this.getToolType() == ItemTool.TYPE_HOE && item.isHoe()) ) { int tier = item.getTier(); switch (tier) { @@ -678,6 +689,9 @@ public double getBreakTime(Item item) { case ItemTool.TIER_DIAMOND: base /= 8; break; + case ItemTool.TIER_NETHERITE: + base /= 9; + break; case ItemTool.TIER_GOLD: base /= 12; break; diff --git a/src/main/java/cn/nukkit/block/BlockButton.java b/src/main/java/cn/nukkit/block/BlockButton.java index 15324634151..204a45425d6 100644 --- a/src/main/java/cn/nukkit/block/BlockButton.java +++ b/src/main/java/cn/nukkit/block/BlockButton.java @@ -134,7 +134,7 @@ public boolean onBreak(Item item) { @Override public Item toItem() { - return Item.get(this.getId(), 5); + return Item.get(this.getId(), 0); } @Override diff --git a/src/main/java/cn/nukkit/block/BlockCobweb.java b/src/main/java/cn/nukkit/block/BlockCobweb.java index 17d1ff2c6b9..16ca8a689d8 100644 --- a/src/main/java/cn/nukkit/block/BlockCobweb.java +++ b/src/main/java/cn/nukkit/block/BlockCobweb.java @@ -51,7 +51,11 @@ public void onEntityCollide(Entity entity) { @Override public Item[] getDrops(Item item) { - if (item.isShears() || item.isSword()) { + if (item.isShears()) { + return new Item[]{ + this.toItem() + }; + } else if (item.isSword()) { return new Item[]{ new ItemString() }; diff --git a/src/main/java/cn/nukkit/block/BlockConcretePowder.java b/src/main/java/cn/nukkit/block/BlockConcretePowder.java index 049c5c1692f..111c8d1baec 100644 --- a/src/main/java/cn/nukkit/block/BlockConcretePowder.java +++ b/src/main/java/cn/nukkit/block/BlockConcretePowder.java @@ -67,7 +67,7 @@ public int onUpdate(int type) { for (int side = 1; side <= 5; side++) { Block block = this.getSide(BlockFace.fromIndex(side)); - if (block.getId() == Block.WATER || block.getId() == Block.STILL_WATER || block.getId() == Block.LAVA || block.getId() == Block.STILL_LAVA) { + if (block.getId() == Block.WATER || block.getId() == Block.STILL_WATER) { this.level.setBlock(this, Block.get(Block.CONCRETE, this.meta), true, true); } } @@ -83,7 +83,7 @@ public boolean place(Item item, Block b, Block target, BlockFace face, double fx for (int side = 1; side <= 5; side++) { Block block = this.getSide(BlockFace.fromIndex(side)); - if (block.getId() == Block.WATER || block.getId() == Block.STILL_WATER || block.getId() == Block.LAVA || block.getId() == Block.STILL_LAVA) { + if (block.getId() == Block.WATER || block.getId() == Block.STILL_WATER) { concrete = true; break; } diff --git a/src/main/java/cn/nukkit/block/BlockDirt.java b/src/main/java/cn/nukkit/block/BlockDirt.java index 6a7db24cd13..dd87fb80c7e 100644 --- a/src/main/java/cn/nukkit/block/BlockDirt.java +++ b/src/main/java/cn/nukkit/block/BlockDirt.java @@ -54,10 +54,17 @@ public String getName() { @Override public boolean onActivate(Item item, Player player) { if (item.isHoe()) { - item.useOn(this); - this.getLevel().setBlock(this, this.getDamage() == 0 ? get(FARMLAND) : get(DIRT), true); - - return true; + if (this.up() instanceof BlockAir) { + item.useOn(this); + this.getLevel().setBlock(this, this.getDamage() == 0 ? get(FARMLAND) : get(DIRT), true); + return true; + } + } else if (item.isShovel()) { + if (this.up() instanceof BlockAir) { + item.useOn(this); + this.getLevel().setBlock(this, get(GRASS_PATH)); + return true; + } } return false; diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java index 7ed473e1c77..0d57b49a94e 100644 --- a/src/main/java/cn/nukkit/block/BlockDispenser.java +++ b/src/main/java/cn/nukkit/block/BlockDispenser.java @@ -209,7 +209,7 @@ public void dispense() { pk.evid = LevelEventPacket.EVENT_SOUND_CLICK; pk.data = 1000; - this.level.addChunkPacket(getChunkX(), getChunkZ(), pk.clone()); + //this.level.addChunkPacket(getChunkX(), getChunkZ(), pk.clone()); } pk.evid = LevelEventPacket.EVENT_PARTICLE_SHOOT; diff --git a/src/main/java/cn/nukkit/block/BlockDragonEgg.java b/src/main/java/cn/nukkit/block/BlockDragonEgg.java index 9c9a0f30f38..96915058537 100644 --- a/src/main/java/cn/nukkit/block/BlockDragonEgg.java +++ b/src/main/java/cn/nukkit/block/BlockDragonEgg.java @@ -1,5 +1,6 @@ package cn.nukkit.block; +import cn.nukkit.event.block.BlockFromToEvent; import cn.nukkit.level.Level; import cn.nukkit.network.protocol.LevelEventPacket; import cn.nukkit.utils.BlockColor; @@ -55,12 +56,18 @@ public int onUpdate(int type) { } public void teleport() { + ThreadLocalRandom random = ThreadLocalRandom.current(); for (int i = 0; i < 1000; ++i) { - Block t = this.getLevel().getBlock(this.add(ThreadLocalRandom.current().nextInt(-16, 16), ThreadLocalRandom.current().nextInt(-16, 16), ThreadLocalRandom.current().nextInt(-16, 16))); - if (t.getId() == AIR) { - int diffX = this.getFloorX() - t.getFloorX(); - int diffY = this.getFloorY() - t.getFloorY(); - int diffZ = this.getFloorZ() - t.getFloorZ(); + Block to = this.getLevel().getBlock(this.add(random.nextInt(-16, 16), random.nextInt(-16, 16), random.nextInt(-16, 16))); + if (to.getId() == AIR) { + BlockFromToEvent event = new BlockFromToEvent(this, to); + this.level.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) return; + to = event.getTo(); + + int diffX = this.getFloorX() - to.getFloorX(); + int diffY = this.getFloorY() - to.getFloorY(); + int diffZ = this.getFloorZ() - to.getFloorZ(); LevelEventPacket pk = new LevelEventPacket(); pk.evid = LevelEventPacket.EVENT_PARTICLE_DRAGON_EGG_TELEPORT; pk.data = (((((Math.abs(diffX) << 16) | (Math.abs(diffY) << 8)) | Math.abs(diffZ)) | ((diffX < 0 ? 1 : 0) << 24)) | ((diffY < 0 ? 1 : 0) << 25)) | ((diffZ < 0 ? 1 : 0) << 26); @@ -69,7 +76,7 @@ public void teleport() { pk.z = this.getFloorZ(); this.getLevel().addChunkPacket(this.getFloorX() >> 4, this.getFloorZ() >> 4, pk); this.getLevel().setBlock(this, get(AIR), true); - this.getLevel().setBlock(t, this, true); + this.getLevel().setBlock(to, this, true); return; } } diff --git a/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java b/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java index ff26e94a97c..2dd387a94e2 100644 --- a/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java +++ b/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java @@ -9,6 +9,9 @@ import cn.nukkit.utils.BlockColor; import cn.nukkit.utils.Faceable; +import java.util.ArrayList; +import java.util.List; + /** * Created by Pub4Game on 26.12.2015. */ @@ -95,7 +98,7 @@ public boolean onActivate(Item item, Player player) { } public void createPortal() { - Vector3 centerSpot = this.searchCenter(); + Vector3 centerSpot = this.searchCenter(new ArrayList<>()); if(centerSpot != null) { for(int x = -2; x <= 2; x++) { for(int z = -2; z <= 2; z++) { @@ -121,15 +124,16 @@ public void createPortal() { } } - private Vector3 searchCenter() { + private Vector3 searchCenter(List visited) { for(int x = -2; x <= 2; x++) { if(x == 0) continue; Block block = this.getLevel().getBlock(this.add(x, 0, 0)); Block iBlock = this.getLevel().getBlock(this.add(x * 2, 0, 0)); - if(this.checkFrame(block)) { + if(this.checkFrame(block) && !visited.contains(block)) { + visited.add(block); if((x == -1 || x == 1) && this.checkFrame(iBlock)) - return ((BlockEndPortalFrame) block).searchCenter(); + return ((BlockEndPortalFrame) block).searchCenter(visited); for(int z = -4; z <= 4; z++) { if(z == 0) continue; @@ -145,9 +149,10 @@ private Vector3 searchCenter() { continue; Block block = this.getLevel().getBlock(this.add(0, 0, z)); Block iBlock = this.getLevel().getBlock(this.add(0, 0, z * 2)); - if(this.checkFrame(block)) { + if(this.checkFrame(block) && !visited.contains(block)) { + visited.add(block); if((z == -1 || z == 1) && this.checkFrame(iBlock)) - return ((BlockEndPortalFrame) block).searchCenter(); + return ((BlockEndPortalFrame) block).searchCenter(visited); for(int x = -4; x <= 4; x++) { if(x == 0) continue; diff --git a/src/main/java/cn/nukkit/block/BlockFarmland.java b/src/main/java/cn/nukkit/block/BlockFarmland.java index 2b4945d31d7..7301b15da03 100644 --- a/src/main/java/cn/nukkit/block/BlockFarmland.java +++ b/src/main/java/cn/nukkit/block/BlockFarmland.java @@ -48,7 +48,7 @@ public int getToolType() { @Override public double getMaxY() { - return this.y + 0.9375; + return this.y + 1; } @Override diff --git a/src/main/java/cn/nukkit/block/BlockFlowerPot.java b/src/main/java/cn/nukkit/block/BlockFlowerPot.java index 08363acea48..1d8d7639957 100644 --- a/src/main/java/cn/nukkit/block/BlockFlowerPot.java +++ b/src/main/java/cn/nukkit/block/BlockFlowerPot.java @@ -32,7 +32,6 @@ protected static boolean canPlaceIntoFlowerPot(int id) { case RED_MUSHROOM: case BROWN_MUSHROOM: case CACTUS: - // TODO: 2016/2/4 case NETHER_WART: return true; default: return false; @@ -95,7 +94,25 @@ public boolean onActivate(Item item) { public boolean onActivate(Item item, Player player) { BlockEntity blockEntity = getLevel().getBlockEntity(this); if (!(blockEntity instanceof BlockEntityFlowerPot)) return false; - if (blockEntity.namedTag.getShort("item") != 0 || blockEntity.namedTag.getInt("mData") != 0) return false; + + if (blockEntity.namedTag.getShort("item") != AIR || blockEntity.namedTag.getInt("mData") != AIR) { + if (!canPlaceIntoFlowerPot(item.getId())) { + int id = blockEntity.namedTag.getShort("item"); + if (id == AIR) id = blockEntity.namedTag.getInt("mData"); + for (Item drop : player.getInventory().addItem(Item.get(id, blockEntity.namedTag.getInt("data")))) { + player.dropItem(drop); + } + + blockEntity.namedTag.putShort("item", AIR); + blockEntity.namedTag.putInt("data", 0); + this.setDamage(0); + this.level.setBlock(this, this, true); + ((BlockEntityFlowerPot) blockEntity).spawnToAll(); + return true; + } + return false; + } + int itemID; int itemMeta; if (!canPlaceIntoFlowerPot(item.getId())) { diff --git a/src/main/java/cn/nukkit/block/BlockGrassPath.java b/src/main/java/cn/nukkit/block/BlockGrassPath.java index ede42b4ba06..29d2442850c 100644 --- a/src/main/java/cn/nukkit/block/BlockGrassPath.java +++ b/src/main/java/cn/nukkit/block/BlockGrassPath.java @@ -31,7 +31,7 @@ public int getToolType() { @Override public double getMaxY() { - return this.y + 0.9375; + return this.y + 1; } @Override diff --git a/src/main/java/cn/nukkit/block/BlockHayBale.java b/src/main/java/cn/nukkit/block/BlockHayBale.java index 3f83d753ae5..a027f9a6907 100644 --- a/src/main/java/cn/nukkit/block/BlockHayBale.java +++ b/src/main/java/cn/nukkit/block/BlockHayBale.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.item.Item; +import cn.nukkit.item.ItemTool; import cn.nukkit.math.BlockFace; import cn.nukkit.utils.BlockColor; import cn.nukkit.utils.Faceable; @@ -39,6 +40,11 @@ public double getResistance() { return 2.5; } + @Override + public int getToolType() { + return ItemTool.TYPE_HOE; + } + @Override public int getBurnChance() { return 60; diff --git a/src/main/java/cn/nukkit/block/BlockIce.java b/src/main/java/cn/nukkit/block/BlockIce.java index 50fe3aae032..fe039468082 100644 --- a/src/main/java/cn/nukkit/block/BlockIce.java +++ b/src/main/java/cn/nukkit/block/BlockIce.java @@ -3,6 +3,7 @@ import cn.nukkit.event.block.BlockFadeEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.level.Level; import cn.nukkit.utils.BlockColor; @@ -57,15 +58,13 @@ public boolean onBreak(Item item) { @Override public int onUpdate(int type) { if (type == Level.BLOCK_UPDATE_RANDOM) { - if (this.getLevel().getDimension() != Level.DIMENSION_NETHER) { - if (this.getLevel().getBlockLightAt((int) this.x, (int) this.y, (int) this.z) >= 12) { - BlockFadeEvent event = new BlockFadeEvent(this, get(WATER)); - level.getServer().getPluginManager().callEvent(event); - if (!event.isCancelled()) { - level.setBlock(this, event.getNewState(), true); - } - return Level.BLOCK_UPDATE_NORMAL; + if (level.getBlockLightAt((int) this.x, (int) this.y, (int) this.z) >= 12) { + BlockFadeEvent event = new BlockFadeEvent(this, level.getDimension() == Level.DIMENSION_NETHER ? get(AIR) : get(WATER)); + level.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + level.setBlock(this, event.getNewState(), true); } + return Level.BLOCK_UPDATE_RANDOM; } } return 0; @@ -73,6 +72,9 @@ public int onUpdate(int type) { @Override public Item[] getDrops(Item item) { + if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) { + return new Item[]{this.toItem()}; + } return new Item[0]; } diff --git a/src/main/java/cn/nukkit/block/BlockLava.java b/src/main/java/cn/nukkit/block/BlockLava.java index 28c2f91d67b..1f551f4c048 100644 --- a/src/main/java/cn/nukkit/block/BlockLava.java +++ b/src/main/java/cn/nukkit/block/BlockLava.java @@ -153,6 +153,9 @@ public BlockLiquid getBlock(int meta) { @Override public int tickRate() { + if (this.level.getDimension() == Level.DIMENSION_NETHER) { + return 10; + } return 30; } diff --git a/src/main/java/cn/nukkit/block/BlockLeaves.java b/src/main/java/cn/nukkit/block/BlockLeaves.java index 87fdd0a65fc..24fbde2983d 100644 --- a/src/main/java/cn/nukkit/block/BlockLeaves.java +++ b/src/main/java/cn/nukkit/block/BlockLeaves.java @@ -45,7 +45,7 @@ public double getHardness() { @Override public int getToolType() { - return ItemTool.TYPE_SHEARS; + return ItemTool.TYPE_HOE; } @Override diff --git a/src/main/java/cn/nukkit/block/BlockLiquid.java b/src/main/java/cn/nukkit/block/BlockLiquid.java index 6c49f125c56..2bf937a245f 100644 --- a/src/main/java/cn/nukkit/block/BlockLiquid.java +++ b/src/main/java/cn/nukkit/block/BlockLiquid.java @@ -280,7 +280,7 @@ protected void flowIntoBlock(Block block, int newFlowDecay) { level.getServer().getPluginManager().callEvent(event); if (!event.isCancelled()) { if (block.getId() > 0) { - this.level.useBreakOn(block); + this.level.useBreakOn(block, block.getId() == COBWEB ? Item.get(Item.WOODEN_SWORD) : null); } this.level.setBlock(block, getBlock(newFlowDecay), true, true); this.level.scheduleUpdate(block, this.tickRate()); diff --git a/src/main/java/cn/nukkit/block/BlockMushroom.java b/src/main/java/cn/nukkit/block/BlockMushroom.java index 0ab185efa3c..350229a9eef 100644 --- a/src/main/java/cn/nukkit/block/BlockMushroom.java +++ b/src/main/java/cn/nukkit/block/BlockMushroom.java @@ -1,8 +1,10 @@ package cn.nukkit.block; import cn.nukkit.Player; +import cn.nukkit.event.level.StructureGrowEvent; import cn.nukkit.item.Item; import cn.nukkit.level.Level; +import cn.nukkit.level.ListChunkManager; import cn.nukkit.level.generator.object.mushroom.BigMushroom; import cn.nukkit.level.particle.BoneMealParticle; import cn.nukkit.math.BlockFace; @@ -70,7 +72,16 @@ public boolean grow() { BigMushroom generator = new BigMushroom(getType()); - if (generator.generate(this.level, new NukkitRandom(), this)) { + ListChunkManager chunkManager = new ListChunkManager(this.level); + if (generator.generate(chunkManager, new NukkitRandom(), this)) { + StructureGrowEvent ev = new StructureGrowEvent(this, chunkManager.getBlocks()); + this.level.getServer().getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + return false; + } + for(Block block : ev.getBlockList()) { + this.level.setBlockAt(block.getFloorX(), block.getFloorY(), block.getFloorZ(), block.getId(), block.getDamage()); + } return true; } else { this.level.setBlock(this, this, true, false); diff --git a/src/main/java/cn/nukkit/block/BlockNetherPortal.java b/src/main/java/cn/nukkit/block/BlockNetherPortal.java index badd01422f7..708382a93be 100644 --- a/src/main/java/cn/nukkit/block/BlockNetherPortal.java +++ b/src/main/java/cn/nukkit/block/BlockNetherPortal.java @@ -34,6 +34,11 @@ public int getId() { return NETHER_PORTAL; } + @Override + public boolean canBeFlowedInto() { + return false; + } + @Override public boolean canPassThrough() { return true; diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index a616de58967..654e040f504 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -1,6 +1,7 @@ package cn.nukkit.block; import cn.nukkit.Player; +import cn.nukkit.Server; import cn.nukkit.blockentity.BlockEntity; import cn.nukkit.blockentity.BlockEntityMovingBlock; import cn.nukkit.blockentity.BlockEntityPistonArm; @@ -202,8 +203,9 @@ private boolean doMove(boolean extending) { } List newBlocks = calculator.getBlocksToMove(); - attached = newBlocks.stream().map(Vector3::asBlockVector3).collect(Collectors.toList()); + if(!isExtended())Collections.reverse(newBlocks); + if(!isExtended())Collections.reverse(attached); BlockFace side = extending ? direction : direction.getOpposite(); @@ -213,7 +215,7 @@ private boolean doMove(boolean extending) { BlockEntity blockEntity = this.level.getBlockEntity(oldPos); - this.level.setBlock(newBlock, Block.get(BlockID.MOVING_BLOCK), true); + this.level.setBlock(newBlock, Block.get(newBlock.getId()), true); CompoundTag nbt = BlockEntity.getDefaultCompound(newBlock, BlockEntity.MOVING_BLOCK) .putInt("pistonPosX", this.getFloorX()) @@ -237,8 +239,11 @@ private boolean doMove(boolean extending) { new BlockEntityMovingBlock(this.level.getChunk(newBlock.getChunkX(), newBlock.getChunkZ()), nbt); - if (this.level.getBlockIdAt(oldPos.getFloorX(), oldPos.getFloorY(), oldPos.getFloorZ()) != BlockID.MOVING_BLOCK) { - this.level.setBlock(oldPos, Block.get(BlockID.AIR)); + if (sticky) { + if(!(getLevel().getBlock(oldPos).getSide(side.getOpposite()).getLocation().equals(getLocation()))) { + this.level.setBlock(oldPos, Block.get(0)); + this.level.setBlock(oldPos.getSide(side), newBlock); + } } } } diff --git a/src/main/java/cn/nukkit/block/BlockSapling.java b/src/main/java/cn/nukkit/block/BlockSapling.java index f6335b4366d..7bfb9b1dd34 100644 --- a/src/main/java/cn/nukkit/block/BlockSapling.java +++ b/src/main/java/cn/nukkit/block/BlockSapling.java @@ -1,16 +1,22 @@ package cn.nukkit.block; import cn.nukkit.Player; +import cn.nukkit.event.level.StructureGrowEvent; import cn.nukkit.item.Item; import cn.nukkit.level.Level; +import cn.nukkit.level.ListChunkManager; import cn.nukkit.level.generator.object.BasicGenerator; import cn.nukkit.level.generator.object.tree.*; import cn.nukkit.level.particle.BoneMealParticle; import cn.nukkit.math.BlockFace; import cn.nukkit.math.NukkitRandom; +import cn.nukkit.math.Vector2; import cn.nukkit.math.Vector3; import cn.nukkit.utils.BlockColor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** @@ -117,25 +123,18 @@ private void grow() { BasicGenerator generator = null; boolean bigTree = false; - int x = 0; - int z = 0; + Vector3 vector3 = new Vector3(); switch (this.getDamage() & 0x07) { case JUNGLE: - loop: - for (; x >= -1; --x) { - for (; z >= -1; --z) { - if (this.findSaplings(x, z, JUNGLE)) { - generator = new ObjectJungleBigTree(10, 20, Block.get(BlockID.WOOD, BlockWood.JUNGLE), Block.get(BlockID.LEAVES, BlockLeaves.JUNGLE)); - bigTree = true; - break loop; - } - } + Vector2 vector2; + if ((vector2 = this.findSaplings(JUNGLE)) != null) { + vector3 = this.add(vector2.getFloorX(), 0, vector2.getFloorY()); + generator = new ObjectJungleBigTree(10, 20, Block.get(BlockID.WOOD, BlockWood.JUNGLE), Block.get(BlockID.LEAVES, BlockLeaves.JUNGLE)); + bigTree = true; } if (!bigTree) { - x = 0; - z = 0; generator = new NewJungleTree(4, 7); } break; @@ -143,15 +142,10 @@ private void grow() { generator = new ObjectSavannaTree(); break; case DARK_OAK: - loop: - for (; x >= -1; --x) { - for (; z >= -1; --z) { - if (this.findSaplings(x, z, DARK_OAK)) { - generator = new ObjectDarkOakTree(); - bigTree = true; - break loop; - } - } + if ((vector2 = this.findSaplings(DARK_OAK)) != null) { + vector3 = this.add(vector2.getFloorX(), 0, vector2.getFloorY()); + generator = new ObjectDarkOakTree(); + bigTree = true; } if (!bigTree) { @@ -160,33 +154,73 @@ private void grow() { break; //TODO: big spruce default: - ObjectTree.growTree(this.level, this.getFloorX(), this.getFloorY(), this.getFloorZ(), new NukkitRandom(), this.getDamage() & 0x07); + ListChunkManager chunkManager = new ListChunkManager(this.level); + ObjectTree.growTree(chunkManager, this.getFloorX(), this.getFloorY(), this.getFloorZ(), new NukkitRandom(), this.getDamage() & 0x07); + StructureGrowEvent ev = new StructureGrowEvent(this, chunkManager.getBlocks()); + this.level.getServer().getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + return; + } + for(Block block : ev.getBlockList()) { + this.level.setBlockAt(block.getFloorX(), block.getFloorY(), block.getFloorZ(), block.getId(), block.getDamage()); + } return; } if (bigTree) { - this.level.setBlock(this.add(x, 0, z), get(AIR), true, false); - this.level.setBlock(this.add(x + 1, 0, z), get(AIR), true, false); - this.level.setBlock(this.add(x, 0, z + 1), get(AIR), true, false); - this.level.setBlock(this.add(x + 1, 0, z + 1), get(AIR), true, false); + this.level.setBlock(vector3, get(AIR), true, false); + this.level.setBlock(vector3.add(1, 0, 0), get(AIR), true, false); + this.level.setBlock(vector3.add(0, 0, 1), get(AIR), true, false); + this.level.setBlock(vector3.add(1, 0, 1), get(AIR), true, false); } else { this.level.setBlock(this, get(AIR), true, false); } - if (!generator.generate(this.level, new NukkitRandom(), this.add(x, 0, z))) { + ListChunkManager chunkManager = new ListChunkManager(this.level); + boolean success = generator.generate(chunkManager, new NukkitRandom(), vector3); + StructureGrowEvent ev = new StructureGrowEvent(this, chunkManager.getBlocks()); + this.level.getServer().getPluginManager().callEvent(ev); + if (ev.isCancelled() || !success) { if (bigTree) { - this.level.setBlock(this.add(x, 0, z), this, true, false); - this.level.setBlock(this.add(x + 1, 0, z), this, true, false); - this.level.setBlock(this.add(x, 0, z + 1), this, true, false); - this.level.setBlock(this.add(x + 1, 0, z + 1), this, true, false); + this.level.setBlock(vector3, this, true, false); + this.level.setBlock(vector3.add(1, 0, 0), this, true, false); + this.level.setBlock(vector3.add(0, 0, 1), this, true, false); + this.level.setBlock(vector3.add(1, 0, 1), this, true, false); } else { this.level.setBlock(this, this, true, false); } + return; + } + for(Block block : ev.getBlockList()) { + this.level.setBlockAt(block.getFloorX(), block.getFloorY(), block.getFloorZ(), block.getId(), block.getDamage()); } } - private boolean findSaplings(int x, int z, int type) { - return this.isSameType(this.add(x, 0, z), type) && this.isSameType(this.add(x + 1, 0, z), type) && this.isSameType(this.add(x, 0, z + 1), type) && this.isSameType(this.add(x + 1, 0, z + 1), type); + private Vector2 findSaplings(int type) { + List> validVectorsList = new ArrayList<>(); + validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1))); + validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(-1, 0), new Vector2(0, -1), new Vector2(-1, -1))); + validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, -1), new Vector2(1, -1))); + validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(-1, 0), new Vector2(0, 1), new Vector2(-1, 1))); + for(List validVectors : validVectorsList) { + boolean correct = true; + for(Vector2 vector2 : validVectors) { + if(!this.isSameType(this.add(vector2.x, 0, vector2.y), type)) + correct = false; + } + if(correct) { + int lowestX = 0; + int lowestZ = 0; + for(Vector2 vector2 : validVectors) { + if(vector2.getFloorX() < lowestX) + lowestX = vector2.getFloorX(); + if(vector2.getFloorY() < lowestZ) + lowestZ = vector2.getFloorY(); + } + return new Vector2(lowestX, lowestZ); + } + } + return null; } public boolean isSameType(Vector3 pos, int type) { diff --git a/src/main/java/cn/nukkit/block/BlockSnow.java b/src/main/java/cn/nukkit/block/BlockSnow.java index 2124e6e8c9e..2ca14c92264 100644 --- a/src/main/java/cn/nukkit/block/BlockSnow.java +++ b/src/main/java/cn/nukkit/block/BlockSnow.java @@ -1,5 +1,6 @@ package cn.nukkit.block; +import cn.nukkit.Player; import cn.nukkit.item.Item; import cn.nukkit.item.ItemSnowball; import cn.nukkit.item.ItemTool; @@ -12,7 +13,7 @@ public BlockSnow() { @Override public String getName() { - return "Snow Block"; + return "Snow"; } @Override @@ -61,4 +62,19 @@ public boolean canHarvestWithHand() { public boolean canSilkTouch() { return true; } + + @Override + public boolean canBeActivated() { + return true; + } + + @Override + public boolean onActivate(Item item, Player player) { + if (item.isShovel()) { + item.useOn(this); + this.level.useBreakOn(this, item.clone().clearNamedTag(), null, true); + return true; + } + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockSnowLayer.java b/src/main/java/cn/nukkit/block/BlockSnowLayer.java index 7cda0ad1a8e..31e589e8d5c 100644 --- a/src/main/java/cn/nukkit/block/BlockSnowLayer.java +++ b/src/main/java/cn/nukkit/block/BlockSnowLayer.java @@ -5,8 +5,8 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemSnowball; import cn.nukkit.item.ItemTool; +import cn.nukkit.level.GameRule; import cn.nukkit.level.Level; -import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.BlockFace; import cn.nukkit.utils.BlockColor; @@ -26,6 +26,11 @@ public BlockSnowLayer(int meta) { this.meta = meta; } + @Override + public final int getFullId() { + return (this.getId() << 4) + this.getDamage(); + } + @Override public final int getDamage() { return this.meta; @@ -38,7 +43,7 @@ public final void setDamage(int meta) { @Override public String getName() { - return "Snow Layer"; + return "Top Snow"; } @Override @@ -63,13 +68,12 @@ public int getToolType() { @Override public boolean canBeReplaced() { - return true; + return (this.getDamage() & 0x7) != 0x7; } @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { - Block down = this.down(); - if (down.isSolid()) { + if (this.canSurvive()) { this.getLevel().setBlock(block, this, true); return true; @@ -77,12 +81,27 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl return false; } + private boolean canSurvive() { + Block below = this.down(); + return below.getId() != ICE && below.getId() != PACKED_ICE && below.getId() != ICE_FROSTED && (below.isSolid() || (this.getDamage() & 0x7) == 0x7); + } + @Override public int onUpdate(int type) { - super.onUpdate(type); - if (type == Level.BLOCK_UPDATE_RANDOM) { + if (type == Level.BLOCK_UPDATE_NORMAL) { + if ((this.getDamage() & 0x7) != 0x7 || this.up().getId() != SNOW_LAYER) { + super.onUpdate(type); + } + + if (this.level.getBlockIdAt(this.getFloorX(), this.getFloorY(), this.getFloorZ()) == SNOW_LAYER && !this.canSurvive()) { + this.level.useBreakOn(this, null, null, true); + if (this.level.getGameRules().getBoolean(GameRule.DO_TILE_DROPS)) { + this.level.dropItem(this, this.toItem()); + } + } + } else if (type == Level.BLOCK_UPDATE_RANDOM) { if (this.getLevel().getBlockLightAt((int) this.x, (int) this.y, (int) this.z) >= 10) { - BlockFadeEvent event = new BlockFadeEvent(this, get(AIR)); + BlockFadeEvent event = new BlockFadeEvent(this, (this.getDamage() & 0x7) > 0 ? get(SNOW_LAYER, this.getDamage() - 1) : get(AIR)); level.getServer().getPluginManager().callEvent(event); if (!event.isCancelled()) { level.setBlock(this, event.getNewState(), true); @@ -101,9 +120,10 @@ public Item toItem() { @Override public Item[] getDrops(Item item) { if (item.isShovel() && item.getTier() >= ItemTool.TIER_WOODEN) { - return new Item[]{ - this.toItem() - }; + Item drop = this.toItem(); + int height = this.getDamage() & 0x7; + drop.setCount(height < 3 ? 1 : height < 5 ? 2 : height == 7 ? 4 : 3); + return new Item[]{drop}; } else { return new Item[0]; } @@ -121,7 +141,7 @@ public boolean canHarvestWithHand() { @Override public boolean isTransparent() { - return true; + return (this.getDamage() & 0x7) != 0x7; } @Override @@ -130,19 +150,40 @@ public boolean canBeFlowedInto() { } @Override - public boolean canPassThrough() { - return true; + public boolean isSolid() { + return (this.getDamage() & 0x7) == 0x7; } @Override - public boolean isSolid() { - return false; + public double getMaxY() { + int height = this.getDamage() & 0x7; + return height < 3 ? this.y : height == 7 ? this.y + 1 : this.y + 0.5; } @Override - protected AxisAlignedBB recalculateBoundingBox() { - return null; + public boolean canBeActivated() { + return true; } -} + @Override + public boolean onActivate(Item item, Player player) { + if (item.isShovel()) { + item.useOn(this); + this.level.useBreakOn(this, item.clone().clearNamedTag(), null, true); + return true; + } else if (item.getId() == SNOW_LAYER) { + if ((this.getDamage() & 0x7) != 0x7) { + this.setDamage(this.getDamage() + 1); + this.level.setBlock(this ,this, true); + if (player != null && (player.gamemode & 0x1) == 0) { + item.count--; + } + return true; + } else { + this.level.setBlock(this ,this, true); + } + } + return false; + } +} diff --git a/src/main/java/cn/nukkit/block/BlockSponge.java b/src/main/java/cn/nukkit/block/BlockSponge.java index 916d8c11bea..db77752a515 100644 --- a/src/main/java/cn/nukkit/block/BlockSponge.java +++ b/src/main/java/cn/nukkit/block/BlockSponge.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.item.Item; +import cn.nukkit.item.ItemTool; import cn.nukkit.level.GlobalBlockPalette; import cn.nukkit.level.Level; import cn.nukkit.level.particle.SmokeParticle; @@ -11,6 +12,7 @@ import java.util.ArrayDeque; import java.util.Queue; +import java.util.concurrent.ThreadLocalRandom; /** * author: Angelic47 @@ -48,6 +50,11 @@ public double getResistance() { return 3; } + @Override + public int getToolType() { + return ItemTool.TYPE_HOE; + } + @Override public String getName() { return NAMES[this.getDamage() & 0b1]; @@ -60,34 +67,32 @@ public BlockColor getColor() { @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { - Level level = block.getLevel(); - boolean blockSet = level.setBlock(block, this); - - if (blockSet) { - if (this.getDamage() == WET && level.getDimension() == Level.DIMENSION_NETHER) { - level.setBlock(block, Block.get(BlockID.SPONGE, DRY)); - this.getLevel().addLevelEvent(block.add(0.5, 0.875, 0.5), LevelEventPacket.EVENT_SOUND_EXPLODE); - - for (int i = 0; i < 8; ++i) { - this.getLevel().addParticle( - //TODO: Use correct smoke particle - new SmokeParticle(block.getLocation().add(Math.random(), 1, Math.random()))); - } - } else if (this.getDamage() == DRY && performWaterAbsorb(block)) { - level.setBlock(block, Block.get(BlockID.SPONGE, WET)); - - for (int i = 0; i < 4; i++) { - LevelEventPacket packet = new LevelEventPacket(); - packet.evid = 2001; - packet.x = (float) block.getX(); - packet.y = (float) block.getY(); - packet.z = (float) block.getZ(); - packet.data = GlobalBlockPalette.getOrCreateRuntimeId(BlockID.WATER, 0); - level.addChunkPacket(getChunkX(), getChunkZ(), packet); - } + if (this.getDamage() == WET && level.getDimension() == Level.DIMENSION_NETHER) { + level.setBlock(block, Block.get(BlockID.SPONGE, DRY), true, true); + this.getLevel().addLevelEvent(block.add(0.5, 0.875, 0.5), LevelEventPacket.EVENT_SOUND_EXPLODE); + + for (int i = 0; i < 8; ++i) { + level.addParticle(new SmokeParticle(block.getLocation().add(ThreadLocalRandom.current().nextDouble(), 1, ThreadLocalRandom.current().nextDouble()))); + } + + return true; + } else if (this.getDamage() == DRY && block instanceof BlockWater && performWaterAbsorb(block)) { + level.setBlock(block, Block.get(BlockID.SPONGE, WET), true, true); + + for (int i = 0; i < 4; i++) { + LevelEventPacket packet = new LevelEventPacket(); + packet.evid = LevelEventPacket.EVENT_PARTICLE_DESTROY; + packet.x = (float) block.getX() + 0.5f; + packet.y = (float) block.getY() + 1f; + packet.z = (float) block.getZ() + 0.5f; + packet.data = GlobalBlockPalette.getOrCreateRuntimeId(BlockID.WATER, 0); + level.addChunkPacket(getChunkX(), getChunkZ(), packet); } + + return true; } - return blockSet; + + return super.place(item, block, target, face, fx, fy, fz, player); } private boolean performWaterAbsorb(Block block) { diff --git a/src/main/java/cn/nukkit/block/BlockSugarcane.java b/src/main/java/cn/nukkit/block/BlockSugarcane.java index 4948a709b43..4eaf9e1f903 100644 --- a/src/main/java/cn/nukkit/block/BlockSugarcane.java +++ b/src/main/java/cn/nukkit/block/BlockSugarcane.java @@ -134,7 +134,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl if (down.getId() == SUGARCANE_BLOCK) { this.getLevel().setBlock(block, Block.get(BlockID.SUGARCANE_BLOCK), true); return true; - } else if (down.getId() == GRASS || down.getId() == DIRT || down.getId() == SAND) { + } else if (down.getId() == GRASS || down.getId() == DIRT || down.getId() == SAND || down.getId() == PODZOL) { Block block0 = down.north(); Block block1 = down.south(); Block block2 = down.west(); diff --git a/src/main/java/cn/nukkit/block/BlockTNT.java b/src/main/java/cn/nukkit/block/BlockTNT.java index ed02b426f2e..05d99d8522a 100644 --- a/src/main/java/cn/nukkit/block/BlockTNT.java +++ b/src/main/java/cn/nukkit/block/BlockTNT.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.entity.Entity; import cn.nukkit.item.Item; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.level.Level; import cn.nukkit.math.NukkitRandom; import cn.nukkit.nbt.tag.CompoundTag; @@ -110,9 +111,12 @@ public boolean onActivate(Item item, Player player) { item.useOn(this); this.prime(80, player); return true; - } - if (item.getId() == Item.FIRE_CHARGE) { - if (!player.isCreative()) player.getInventory().removeItem(Item.get(Item.FIRE_CHARGE, 0, 1)); + } else if (item.getId() == Item.FIRE_CHARGE) { + if (!player.isCreative()) item.count--; + this.prime(80, player); + return true; + } else if (item.hasEnchantment(Enchantment.ID_FIRE_ASPECT)) { + item.useOn(this); this.prime(80, player); return true; } diff --git a/src/main/java/cn/nukkit/block/BlockVine.java b/src/main/java/cn/nukkit/block/BlockVine.java index 8d811c48298..bc097954203 100644 --- a/src/main/java/cn/nukkit/block/BlockVine.java +++ b/src/main/java/cn/nukkit/block/BlockVine.java @@ -2,6 +2,8 @@ import cn.nukkit.Player; import cn.nukkit.entity.Entity; +import cn.nukkit.event.block.BlockGrowEvent; +import cn.nukkit.event.block.BlockSpreadEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; import cn.nukkit.item.ItemTool; @@ -11,6 +13,11 @@ import cn.nukkit.math.SimpleAxisAlignedBB; import cn.nukkit.utils.BlockColor; +import java.util.EnumSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + /** * Created by Pub4Game on 15.01.2016. */ @@ -131,7 +138,7 @@ protected AxisAlignedBB recalculateBoundingBox() { @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { - if (target.isSolid() && face.getHorizontalIndex() != -1) { + if (block.getId() != VINE && target.isSolid() && face.getHorizontalIndex() != -1) { this.setDamage(getMetaFromFace(face.getOpposite())); this.getLevel().setBlock(block, this, true, true); return true; @@ -159,33 +166,166 @@ public Item toItem() { @Override public int onUpdate(int type) { if (type == Level.BLOCK_UPDATE_NORMAL) { - if (!this.getSide(getFace()).isSolid()) { - Block up = this.up(); - if (up.getId() != this.getId() || up.getDamage() != this.getDamage()) { - this.getLevel().useBreakOn(this, null, null, true); - return Level.BLOCK_UPDATE_NORMAL; + Block up = this.up(); + Set upFaces = up instanceof BlockVine ? ((BlockVine) up).getFaces() : null; + Set faces = this.getFaces(); + for (BlockFace face : BlockFace.Plane.HORIZONTAL) { + if (!this.getSide(face).isSolid() && (upFaces == null || !upFaces.contains(face))) { + faces.remove(face); + } + } + if (faces.isEmpty() && !up.isSolid()) { + this.getLevel().useBreakOn(this, null, null, true); + return Level.BLOCK_UPDATE_NORMAL; + } + int meta = getMetaFromFaces(faces); + if (meta != this.getDamage()) { + this.level.setBlock(this, Block.get(VINE, meta), true); + return Level.BLOCK_UPDATE_NORMAL; + } + } else if (type == Level.BLOCK_UPDATE_RANDOM) { + Random random = ThreadLocalRandom.current(); + if (random.nextInt(4) == 0) { + BlockFace face = BlockFace.random(random); + Block block = this.getSide(face); + int faceMeta = getMetaFromFace(face); + int meta = this.getDamage(); + + if (this.y < 255 && face == BlockFace.UP && block.getId() == AIR) { + if (this.canSpread()) { + for (BlockFace horizontalFace : BlockFace.Plane.HORIZONTAL) { + if (random.nextBoolean() || !this.getSide(horizontalFace).getSide(face).isSolid()) { + meta &= ~getMetaFromFace(horizontalFace); + } + } + putVineOnHorizontalFace(block, meta, this); + } + } else if (face.getHorizontalIndex() != -1 && (meta & faceMeta) != faceMeta) { + if (this.canSpread()) { + if (block.getId() == AIR) { + BlockFace cwFace = face.rotateY(); + BlockFace ccwFace = face.rotateYCCW(); + Block cwBlock = block.getSide(cwFace); + Block ccwBlock = block.getSide(ccwFace); + int cwMeta = getMetaFromFace(cwFace); + int ccwMeta = getMetaFromFace(ccwFace); + boolean onCw = (meta & cwMeta) == cwMeta; + boolean onCcw = (meta & ccwMeta) == ccwMeta; + + if (onCw && cwBlock.isSolid()) { + putVine(block, getMetaFromFace(cwFace), this); + } else if (onCcw && ccwBlock.isSolid()) { + putVine(block, getMetaFromFace(ccwFace), this); + } else if (onCw && cwBlock.getId() == AIR && this.getSide(cwFace).isSolid()) { + putVine(cwBlock, getMetaFromFace(face.getOpposite()), this); + } else if (onCcw && ccwBlock.getId() == AIR && this.getSide(ccwFace).isSolid()) { + putVine(ccwBlock, getMetaFromFace(face.getOpposite()), this); + } else if (block.up().isSolid()) { + putVine(block, 0, this); + } + } else if (!block.isTransparent()) { + meta |= getMetaFromFace(face); + putVine(this, meta, null); + } + } + } else if (this.y > 0) { + Block below = this.down(); + int id = below.getId(); + if (id == AIR || id == VINE) { + for (BlockFace horizontalFace : BlockFace.Plane.HORIZONTAL) { + if (random.nextBoolean()) { + meta &= ~getMetaFromFace(horizontalFace); + } + } + putVineOnHorizontalFace(below, below.getDamage() | meta, id == AIR ? this : null); + } } + return Level.BLOCK_UPDATE_RANDOM; } } return 0; } - private BlockFace getFace() { + private boolean canSpread() { + int blockX = this.getFloorX(); + int blockY = this.getFloorY(); + int blockZ = this.getFloorZ(); + + int count = 0; + for (int x = blockX - 4; x <= blockX + 4; x++) { + for (int z = blockZ - 4; z <= blockZ + 4; z++) { + for (int y = blockY - 1; y <= blockY + 1; y++) { + if (this.level.getBlock(x, y, z).getId() == VINE) { + if (++count >= 5) return false; + } + } + } + } + return true; + } + + private void putVine(Block block, int meta, Block source) { + if (block.getId() == VINE && block.getDamage() == meta) return; + Block vine = get(VINE, meta); + BlockGrowEvent event; + if (source != null) { + event = new BlockSpreadEvent(block, source, vine); + } else { + event = new BlockGrowEvent(block, vine); + } + this.level.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.level.setBlock(block, vine, true); + } + } + + private void putVineOnHorizontalFace(Block block, int meta, Block source) { + if (block.getId() == VINE && block.getDamage() == meta) return; + boolean isOnHorizontalFace = false; + for (BlockFace face : BlockFace.Plane.HORIZONTAL) { + int faceMeta = getMetaFromFace(face); + if ((meta & faceMeta) == faceMeta) { + isOnHorizontalFace = true; + break; + } + } + if (isOnHorizontalFace) { + putVine(block, meta, source); + } + } + + private Set getFaces() { + Set faces = EnumSet.noneOf(BlockFace.class); + int meta = this.getDamage(); if ((meta & 1) > 0) { - return BlockFace.SOUTH; - } else if ((meta & 2) > 0) { - return BlockFace.WEST; - } else if ((meta & 4) > 0) { - return BlockFace.NORTH; - } else if ((meta & 8) > 0) { - return BlockFace.EAST; + faces.add(BlockFace.SOUTH); + } + if ((meta & 2) > 0) { + faces.add(BlockFace.WEST); + } + if ((meta & 4) > 0) { + faces.add(BlockFace.NORTH); + } + if ((meta & 8) > 0) { + faces.add(BlockFace.EAST); } - return BlockFace.SOUTH; + return faces; } - private int getMetaFromFace(BlockFace face) { + private static int getMetaFromFaces(Set faces) { + int meta = 0; + + for (BlockFace face : faces) { + meta |= getMetaFromFace(face); + + } + + return meta; + } + + private static int getMetaFromFace(BlockFace face) { switch (face) { case SOUTH: default: @@ -201,7 +341,7 @@ private int getMetaFromFace(BlockFace face) { @Override public int getToolType() { - return ItemTool.TYPE_SHEARS; + return ItemTool.TYPE_AXE; } @Override @@ -210,12 +350,7 @@ public BlockColor getColor() { } @Override - public boolean breaksWhenMoved() { + public boolean canSilkTouch() { return true; } - - @Override - public boolean sticksToPiston() { - return false; - } } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java index c2dfefffc4d..f84df308af3 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java @@ -112,9 +112,9 @@ public boolean onUpdate() { //If secondary is selected as the primary too, apply 2 amplification if (getSecondaryPower() == getPrimaryPower()) { - e.setAmplifier(2); - } else { e.setAmplifier(1); + } else { + e.setAmplifier(0); } //Hide particles diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java b/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java index efcc91b9eb8..a9362fb6b43 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java @@ -53,10 +53,6 @@ public void close() { for (Player player : new HashSet<>(this.getInventory().getViewers())) { player.removeWindow(this.getInventory()); } - - for (Player player : new HashSet<>(this.getInventory().getViewers())) { - player.removeWindow(this.getRealInventory()); - } super.close(); } } diff --git a/src/main/java/cn/nukkit/command/Command.java b/src/main/java/cn/nukkit/command/Command.java index 6b6e6897413..fbbd4954eb7 100644 --- a/src/main/java/cn/nukkit/command/Command.java +++ b/src/main/java/cn/nukkit/command/Command.java @@ -68,7 +68,7 @@ public Command(String name, String description, String usageMessage, String[] al this.aliases = aliases; this.activeAliases = aliases; this.timing = Timings.getCommandTiming(this); - this.commandParameters.put("default", new CommandParameter[]{new CommandParameter("args", CommandParamType.RAWTEXT, true)}); + this.commandParameters.put("default", new CommandParameter[]{CommandParameter.newType("args", true, CommandParamType.RAWTEXT)}); } /** diff --git a/src/main/java/cn/nukkit/command/SimpleCommandMap.java b/src/main/java/cn/nukkit/command/SimpleCommandMap.java index c4d8fe06194..366dfb16933 100644 --- a/src/main/java/cn/nukkit/command/SimpleCommandMap.java +++ b/src/main/java/cn/nukkit/command/SimpleCommandMap.java @@ -144,7 +144,7 @@ public void registerSimpleCommands(Object object) { if (commandParameters != null) { Map map = Arrays.stream(commandParameters.parameters()) .collect(Collectors.toMap(Parameters::name, parameters -> Arrays.stream(parameters.parameters()) - .map(parameter -> new CommandParameter(parameter.name(), parameter.type(), parameter.optional())) + .map(parameter -> CommandParameter.newType(parameter.name(), parameter.optional(), parameter.type())) .distinct() .toArray(CommandParameter[]::new))); diff --git a/src/main/java/cn/nukkit/command/data/CommandEnum.java b/src/main/java/cn/nukkit/command/data/CommandEnum.java index 20f482c0de3..6e18e36039b 100644 --- a/src/main/java/cn/nukkit/command/data/CommandEnum.java +++ b/src/main/java/cn/nukkit/command/data/CommandEnum.java @@ -1,5 +1,11 @@ package cn.nukkit.command.data; +import cn.nukkit.block.BlockID; +import cn.nukkit.item.ItemID; +import com.google.common.collect.ImmutableList; + +import java.lang.reflect.Field; +import java.util.Arrays; import java.util.List; /** @@ -7,9 +13,34 @@ */ public class CommandEnum { + public static final CommandEnum ENUM_BOOLEAN = new CommandEnum("Boolean", ImmutableList.of("true", "false")); + public static final CommandEnum ENUM_GAMEMODE = new CommandEnum("GameMode", + ImmutableList.of("survival", "creative", "s", "c", "adventure", "a", "spectator", "view", "v", "spc")); + public static final CommandEnum ENUM_BLOCK; + public static final CommandEnum ENUM_ITEM; + + static { + ImmutableList.Builder blocks = ImmutableList.builder(); + for (Field field : BlockID.class.getDeclaredFields()) { + blocks.add(field.getName().toLowerCase()); + } + ENUM_BLOCK = new CommandEnum("Block", blocks.build()); + + ImmutableList.Builder items = ImmutableList.builder(); + for (Field field : ItemID.class.getDeclaredFields()) { + items.add(field.getName().toLowerCase()); + } + items.addAll(ENUM_BLOCK.getValues()); + ENUM_ITEM = new CommandEnum("Item", items.build()); + } + private String name; private List values; + public CommandEnum(String name, String... values) { + this(name, Arrays.asList(values)); + } + public CommandEnum(String name, List values) { this.name = name; this.values = values; diff --git a/src/main/java/cn/nukkit/command/data/CommandParameter.java b/src/main/java/cn/nukkit/command/data/CommandParameter.java index befe39246ab..a657455d1b0 100644 --- a/src/main/java/cn/nukkit/command/data/CommandParameter.java +++ b/src/main/java/cn/nukkit/command/data/CommandParameter.java @@ -1,28 +1,9 @@ package cn.nukkit.command.data; - import java.util.ArrayList; -import java.util.Arrays; public class CommandParameter { - public final static String ARG_TYPE_STRING = "string"; - public final static String ARG_TYPE_STRING_ENUM = "stringenum"; - public final static String ARG_TYPE_BOOL = "bool"; - public final static String ARG_TYPE_TARGET = "target"; - public final static String ARG_TYPE_PLAYER = "target"; - public final static String ARG_TYPE_BLOCK_POS = "blockpos"; - public final static String ARG_TYPE_RAW_TEXT = "rawtext"; - public final static String ARG_TYPE_INT = "int"; - - public static final String ENUM_TYPE_ITEM_LIST = "itemType"; - public static final String ENUM_TYPE_BLOCK_LIST = "blockType"; - public static final String ENUM_TYPE_COMMAND_LIST = "commandName"; - public static final String ENUM_TYPE_ENCHANTMENT_LIST = "enchantmentType"; - public static final String ENUM_TYPE_ENTITY_LIST = "entityType"; - public static final String ENUM_TYPE_EFFECT_LIST = "effectType"; - public static final String ENUM_TYPE_PARTICLE_LIST = "particleType"; - public String name; public CommandParamType type; public boolean optional; @@ -31,25 +12,44 @@ public class CommandParameter { public CommandEnum enumData; public String postFix; + /** + * @deprecated use {@link #newType(String, boolean, CommandParamType)} instead + */ @Deprecated public CommandParameter(String name, String type, boolean optional) { this(name, fromString(type), optional); } + /** + * @deprecated use {@link #newType(String, boolean, CommandParamType)} instead + */ + @Deprecated public CommandParameter(String name, CommandParamType type, boolean optional) { this.name = name; this.type = type; this.optional = optional; } + /** + * @deprecated use {@link #newType(String, boolean, CommandParamType)} instead + */ + @Deprecated public CommandParameter(String name, boolean optional) { this(name, CommandParamType.RAWTEXT, optional); } + /** + * @deprecated use {@link #newType(String, CommandParamType)} instead + */ + @Deprecated public CommandParameter(String name) { this(name, false); } + /** + * @deprecated use {@link #newEnum(String, boolean, String)} instead + */ + @Deprecated public CommandParameter(String name, boolean optional, String enumType) { this.name = name; this.type = CommandParamType.RAWTEXT; @@ -57,21 +57,81 @@ public CommandParameter(String name, boolean optional, String enumType) { this.enumData = new CommandEnum(enumType, new ArrayList<>()); } + /** + * @deprecated use {@link #newEnum(String, boolean, String[])} instead + */ + @Deprecated public CommandParameter(String name, boolean optional, String[] enumValues) { this.name = name; this.type = CommandParamType.RAWTEXT; this.optional = optional; - this.enumData = new CommandEnum(name + "Enums", Arrays.asList(enumValues)); + this.enumData = new CommandEnum(name + "Enums", enumValues); } + /** + * @deprecated use {@link #newEnum(String, String)} instead + */ + @Deprecated public CommandParameter(String name, String enumType) { this(name, false, enumType); } + /** + * @deprecated use {@link #newEnum(String, String[])} instead + */ + @Deprecated public CommandParameter(String name, String[] enumValues) { this(name, false, enumValues); } + private CommandParameter(String name, boolean optional, CommandParamType type, CommandEnum enumData, String postFix) { + this.name = name; + this.optional = optional; + this.type = type; + this.enumData = enumData; + this.postFix = postFix; + } + + public static CommandParameter newType(String name, CommandParamType type) { + return newType(name, false, type); + } + + public static CommandParameter newType(String name, boolean optional, CommandParamType type) { + return new CommandParameter(name, optional, type, null, null); + } + + public static CommandParameter newEnum(String name, String[] values) { + return newEnum(name, false, values); + } + + public static CommandParameter newEnum(String name, boolean optional, String[] values) { + return newEnum(name, optional, new CommandEnum(name + "Enums", values)); + } + + public static CommandParameter newEnum(String name, String type) { + return newEnum(name, false, type); + } + + public static CommandParameter newEnum(String name, boolean optional, String type) { + return newEnum(name, optional, new CommandEnum(type, new ArrayList<>())); + } + + public static CommandParameter newEnum(String name, CommandEnum data) { + return newEnum(name, false, data); + } + + public static CommandParameter newEnum(String name, boolean optional, CommandEnum data) { + return new CommandParameter(name, optional, CommandParamType.RAWTEXT, data, null); + } + + public static CommandParameter newPostfix(String name, String postfix) { + return newPostfix(name, false, postfix); + } + + public static CommandParameter newPostfix(String name, boolean optional, String postfix) { + return new CommandParameter(name, optional, CommandParamType.RAWTEXT, null, postfix); + } + protected static CommandParamType fromString(String param) { switch (param) { case "string": diff --git a/src/main/java/cn/nukkit/command/defaults/BanCommand.java b/src/main/java/cn/nukkit/command/defaults/BanCommand.java index 9ae05584693..ca437e55712 100644 --- a/src/main/java/cn/nukkit/command/defaults/BanCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/BanCommand.java @@ -20,8 +20,8 @@ public BanCommand(String name) { this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("reason", CommandParamType.STRING, true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newType("reason", true, CommandParamType.STRING) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java b/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java index 3ce50c08016..7a1b48bb89e 100644 --- a/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java @@ -30,8 +30,12 @@ public BanIpCommand(String name) { this.setAliases(new String[]{"banip"}); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("reason", CommandParamType.STRING, true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newType("reason", true, CommandParamType.STRING) + }); + this.commandParameters.put("byIp", new CommandParameter[]{ + CommandParameter.newType("ip", CommandParamType.STRING), + CommandParameter.newType("reason", true, CommandParamType.STRING) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/BanListCommand.java b/src/main/java/cn/nukkit/command/defaults/BanListCommand.java index 2ba24980801..0ca654c4e88 100644 --- a/src/main/java/cn/nukkit/command/defaults/BanListCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/BanListCommand.java @@ -1,6 +1,7 @@ package cn.nukkit.command.defaults; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; import cn.nukkit.permission.BanEntry; @@ -18,7 +19,7 @@ public BanListCommand(String name) { this.setPermission("nukkit.command.ban.list"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("ips|players", true) + CommandParameter.newEnum("type", true, new CommandEnum("BanListType", "ips", "players")) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/DebugPasteCommand.java b/src/main/java/cn/nukkit/command/defaults/DebugPasteCommand.java index 1617db80816..57e765d08bb 100644 --- a/src/main/java/cn/nukkit/command/defaults/DebugPasteCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/DebugPasteCommand.java @@ -18,6 +18,7 @@ public class DebugPasteCommand extends VanillaCommand { public DebugPasteCommand(String name) { super(name, "%nukkit.command.debug.description", "%nukkit.command.debug.usage"); this.setPermission("nukkit.command.debug.perform"); + this.commandParameters.clear(); } @Override diff --git a/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java b/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java index 8594186b679..73604ce3cd3 100644 --- a/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java @@ -2,6 +2,7 @@ import cn.nukkit.Server; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -17,11 +18,10 @@ public DefaultGamemodeCommand(String name) { this.setPermission("nukkit.command.defaultgamemode"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("mode", CommandParamType.INT, false) + CommandParameter.newType("gameMode", CommandParamType.INT) }); this.commandParameters.put("byString", new CommandParameter[]{ - new CommandParameter("mode", new String[]{"survival", "creative", "s", "c", - "adventure", "a", "spectator", "view", "v"}) + CommandParameter.newEnum("gameMode", CommandEnum.ENUM_GAMEMODE) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/DeopCommand.java b/src/main/java/cn/nukkit/command/defaults/DeopCommand.java index 358599f3786..4934da84e69 100644 --- a/src/main/java/cn/nukkit/command/defaults/DeopCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/DeopCommand.java @@ -18,7 +18,7 @@ public DeopCommand(String name) { super(name, "%nukkit.command.deop.description", "%commands.deop.description"); this.setPermission("nukkit.command.op.take"); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false) + CommandParameter.newType("player", CommandParamType.TARGET) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java b/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java index 081312233df..4fad9e3b8fc 100644 --- a/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Server; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -21,11 +22,10 @@ public DifficultyCommand(String name) { this.setPermission("nukkit.command.difficulty"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("difficulty", CommandParamType.INT, false) + CommandParameter.newType("difficulty", CommandParamType.INT) }); this.commandParameters.put("byString", new CommandParameter[]{ - new CommandParameter("difficulty", new String[]{"peaceful", "p", "easy", "e", - "normal", "n", "hard", "h"}) + CommandParameter.newEnum("difficulty", new CommandEnum("Difficulty", "peaceful", "p", "easy", "e", "normal", "n", "hard", "h")) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/EffectCommand.java b/src/main/java/cn/nukkit/command/defaults/EffectCommand.java index 78dbfe90849..760db564e36 100644 --- a/src/main/java/cn/nukkit/command/defaults/EffectCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/EffectCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -11,6 +12,11 @@ import cn.nukkit.utils.ServerException; import cn.nukkit.utils.TextFormat; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + /** * Created by Snake1999 and Pub4Game on 2016/1/23. * Package cn.nukkit.command.defaults in project nukkit. @@ -20,16 +26,24 @@ public EffectCommand(String name) { super(name, "%nukkit.command.effect.description", "%commands.effect.usage"); this.setPermission("nukkit.command.effect"); this.commandParameters.clear(); + + List effects = new ArrayList<>(); + for (Field field : Effect.class.getDeclaredFields()) { + if (field.getType() == int.class && field.getModifiers() == (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL)) { + effects.add(field.getName().toLowerCase()); + } + } + this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("effect", CommandParamType.STRING, false), //Do not use Enum here because of buggy behavior - new CommandParameter("seconds", CommandParamType.INT, true), - new CommandParameter("amplifier", true), - new CommandParameter("hideParticle", true, new String[]{"true", "false"}) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("effect", new CommandEnum("Effect", effects)), + CommandParameter.newType("seconds", true, CommandParamType.INT), + CommandParameter.newType("amplifier", true, CommandParamType.INT), + CommandParameter.newEnum("hideParticle", true, CommandEnum.ENUM_BOOLEAN) }); this.commandParameters.put("clear", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("clear", new String[]{"clear"}) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("clear", new CommandEnum("ClearEffects", "clear")) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java b/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java index 5fe9bf8f123..e262a9ca62a 100644 --- a/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.item.Item; @@ -20,14 +21,19 @@ public EnchantCommand(String name) { this.setPermission("nukkit.command.enchant"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("enchantment ID", CommandParamType.INT, false), - new CommandParameter("level", CommandParamType.INT, true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newType("enchantmentId", CommandParamType.INT), + CommandParameter.newType("level", true, CommandParamType.INT) }); this.commandParameters.put("byName", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("id", false, CommandParameter.ENUM_TYPE_ENCHANTMENT_LIST), - new CommandParameter("level", CommandParamType.INT, true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("enchantmentName", new CommandEnum("Enchant", + "protection", "fire_protection", "feather_falling", "blast_protection", "projectile_projection", "thorns", "respiration", + "aqua_affinity", "depth_strider", "sharpness", "smite", "bane_of_arthropods", "knockback", "fire_aspect", "looting", "efficiency", + "silk_touch", "durability", "fortune", "power", "punch", "flame", "infinity", "luck_of_the_sea", "lure", "frost_walker", "mending", + "binding_curse", "vanishing_curse", "impaling", "loyality", "riptide", "channeling", "multishot", "piercing", "quick_charge", + "soul_speed")), + CommandParameter.newType("level", true, CommandParamType.INT) }); } @@ -139,6 +145,14 @@ public int getIdByName(String value) throws NumberFormatException { return 31; case "channeling": return 32; + case "multishot": + return 33; + case "piercing": + return 34; + case "quick_charge": + return 35; + case "soul_speed": + return 36; default: return Integer.parseInt(value); } diff --git a/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java b/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java index 3dd8a1891e5..417c473e4a4 100644 --- a/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java @@ -4,6 +4,7 @@ import cn.nukkit.Server; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -25,13 +26,12 @@ public GamemodeCommand(String name) { "nukkit.command.gamemode.other"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("mode", CommandParamType.INT, false), - new CommandParameter("player", CommandParamType.TARGET, true) + CommandParameter.newType("gameMode", CommandParamType.INT), + CommandParameter.newType("player", true, CommandParamType.TARGET) }); this.commandParameters.put("byString", new CommandParameter[]{ - new CommandParameter("mode", new String[]{"survival", "s", "creative", "c", - "adventure", "a", "spectator", "spc", "view", "v"}), - new CommandParameter("player", CommandParamType.TARGET, true) + CommandParameter.newEnum("gameMode", CommandEnum.ENUM_GAMEMODE), + CommandParameter.newType("player", true, CommandParamType.TARGET) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java b/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java index 26a7d180633..1015a4cc662 100644 --- a/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java @@ -2,13 +2,16 @@ import cn.nukkit.Player; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; import cn.nukkit.level.GameRule; import cn.nukkit.level.GameRules; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Optional; import java.util.StringJoiner; @@ -18,10 +21,55 @@ public GameruleCommand(String name) { super(name, "%nukkit.command.gamerule.description", "%nukkit.command.gamerule.usage"); this.setPermission("nukkit.command.gamerule"); this.commandParameters.clear(); - this.commandParameters.put("byString", new CommandParameter[]{ - new CommandParameter("gamerule", true , GameRule.getNames()), - new CommandParameter("value", CommandParamType.STRING, true) + + GameRules rules = GameRules.getDefault(); + List boolGameRules = new ArrayList<>(); + List intGameRules = new ArrayList<>(); + List floatGameRules = new ArrayList<>(); + List unknownGameRules = new ArrayList<>(); + + rules.getGameRules().forEach((rule, value) -> { + switch (value.getType()) { + case BOOLEAN: + boolGameRules.add(rule.getName().toLowerCase()); + break; + case INTEGER: + intGameRules.add(rule.getName().toLowerCase()); + break; + case FLOAT: + floatGameRules.add(rule.getName().toLowerCase()); + break; + case UNKNOWN: + default: + unknownGameRules.add(rule.getName().toLowerCase()); + break; + } }); + + if (!boolGameRules.isEmpty()) { + this.commandParameters.put("boolGameRules", new CommandParameter[]{ + CommandParameter.newEnum("rule", new CommandEnum("BoolGameRule", boolGameRules)), + CommandParameter.newEnum("value", true, CommandEnum.ENUM_BOOLEAN) + }); + } + if (!intGameRules.isEmpty()) { + this.commandParameters.put("intGameRules", new CommandParameter[]{ + CommandParameter.newEnum("rule", new CommandEnum("IntGameRule", intGameRules)), + CommandParameter.newType("value", true, CommandParamType.INT) + }); + } + if (!floatGameRules.isEmpty()) { + this.commandParameters.put("floatGameRules", new CommandParameter[]{ + CommandParameter.newEnum("rule", new CommandEnum("FloatGameRule", floatGameRules)), + CommandParameter.newType("value", true, CommandParamType.FLOAT) + }); + } + if (!unknownGameRules.isEmpty()) { + this.commandParameters.put("unknownGameRules", new CommandParameter[]{ + CommandParameter.newEnum("rule", new CommandEnum("UnknownGameRule", unknownGameRules)), + CommandParameter.newType("value", true, CommandParamType.STRING) + }); + } } @Override @@ -51,7 +99,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) return true; } - sender.sendMessage(gameRule.get().getName() + " = " + rules.getString(gameRule.get())); + sender.sendMessage(gameRule.get().getName() .toLowerCase()+ " = " + rules.getString(gameRule.get())); return true; default: Optional optionalRule = GameRule.parseString(args[0]); @@ -64,7 +112,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) try { rules.setGameRules(optionalRule.get(), args[1]); - sender.sendMessage(new TranslationContainer("commands.gamerule.success", optionalRule.get().getName(), args[1])); + sender.sendMessage(new TranslationContainer("commands.gamerule.success", optionalRule.get().getName().toLowerCase(), args[1])); } catch (IllegalArgumentException e) { sender.sendMessage(new TranslationContainer("commands.generic.syntax", "/gamerule " + args[0] + " ", args[1], " " + String.join(" ", Arrays.copyOfRange(args, 2, args.length)))); } diff --git a/src/main/java/cn/nukkit/command/defaults/GiveCommand.java b/src/main/java/cn/nukkit/command/defaults/GiveCommand.java index e3ca0b16209..d3cef0469d9 100644 --- a/src/main/java/cn/nukkit/command/defaults/GiveCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/GiveCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.item.Item; @@ -19,23 +20,22 @@ public GiveCommand(String name) { this.setPermission("nukkit.command.give"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("item", false, CommandParameter.ENUM_TYPE_ITEM_LIST), - new CommandParameter("amount", CommandParamType.INT, true), - new CommandParameter("meta", CommandParamType.INT, true), - new CommandParameter("tags...", CommandParamType.RAWTEXT, true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("itemName", CommandEnum.ENUM_ITEM), + CommandParameter.newType("amount", true, CommandParamType.INT), + CommandParameter.newType("tags", true, CommandParamType.RAWTEXT) }); this.commandParameters.put("toPlayerById", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("item ID", CommandParamType.INT, false), - new CommandParameter("amount", CommandParamType.INT, true), - new CommandParameter("tags...", CommandParamType.RAWTEXT, true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newType("itemId", CommandParamType.INT), + CommandParameter.newType("amount", true, CommandParamType.INT), + CommandParameter.newType("tags", true, CommandParamType.RAWTEXT) }); this.commandParameters.put("toPlayerByIdMeta", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("item ID:meta", CommandParamType.RAWTEXT, false), - new CommandParameter("amount", CommandParamType.INT, true), - new CommandParameter("tags...", CommandParamType.RAWTEXT, true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newType("itemAndData", CommandParamType.STRING), + CommandParameter.newType("amount", true, CommandParamType.INT), + CommandParameter.newType("tags", true, CommandParamType.RAWTEXT) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/HelpCommand.java b/src/main/java/cn/nukkit/command/defaults/HelpCommand.java index 5c8f7b718ee..124c35b46f1 100644 --- a/src/main/java/cn/nukkit/command/defaults/HelpCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/HelpCommand.java @@ -22,7 +22,7 @@ public HelpCommand(String name) { this.setPermission("nukkit.command.help"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("page", CommandParamType.INT, true) + CommandParameter.newType("page", true, CommandParamType.INT) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/KickCommand.java b/src/main/java/cn/nukkit/command/defaults/KickCommand.java index 01045116f8b..1caf85ac982 100644 --- a/src/main/java/cn/nukkit/command/defaults/KickCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/KickCommand.java @@ -20,8 +20,8 @@ public KickCommand(String name) { this.setPermission("nukkit.command.kick"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("reason", true) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newType("reason", true, CommandParamType.MESSAGE) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/KillCommand.java b/src/main/java/cn/nukkit/command/defaults/KillCommand.java index 2f35f45dc2d..a6fa78e8563 100644 --- a/src/main/java/cn/nukkit/command/defaults/KillCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/KillCommand.java @@ -27,7 +27,7 @@ public KillCommand(String name) { + "nukkit.command.kill.other"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, true) + CommandParameter.newType("player", true, CommandParamType.TARGET) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/MeCommand.java b/src/main/java/cn/nukkit/command/defaults/MeCommand.java index fb701d91d5d..57a35cfc010 100644 --- a/src/main/java/cn/nukkit/command/defaults/MeCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/MeCommand.java @@ -18,7 +18,7 @@ public MeCommand(String name) { this.setPermission("nukkit.command.me"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("action ...", CommandParamType.RAWTEXT, false) + CommandParameter.newType("message", CommandParamType.MESSAGE) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/OpCommand.java b/src/main/java/cn/nukkit/command/defaults/OpCommand.java index 2c8493340c3..6afc9fdcc6c 100644 --- a/src/main/java/cn/nukkit/command/defaults/OpCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/OpCommand.java @@ -20,7 +20,7 @@ public OpCommand(String name) { this.setPermission("nukkit.command.op.give"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false) + CommandParameter.newType("player", CommandParamType.TARGET) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/PardonCommand.java b/src/main/java/cn/nukkit/command/defaults/PardonCommand.java index d97dd30d0ca..0ffd5296b72 100644 --- a/src/main/java/cn/nukkit/command/defaults/PardonCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/PardonCommand.java @@ -18,7 +18,7 @@ public PardonCommand(String name) { this.setAliases(new String[]{"unban"}); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false) + CommandParameter.newType("player", CommandParamType.TARGET) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java index bc61968664f..d3ae0695a85 100644 --- a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java @@ -2,6 +2,7 @@ import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -21,7 +22,7 @@ public PardonIpCommand(String name) { this.setAliases(new String[]{"unbanip", "unban-ip", "pardonip"}); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("ip") + CommandParameter.newType("ip", CommandParamType.STRING) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java b/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java index d4812d7b117..aea1e78c51e 100644 --- a/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.block.Block; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.item.Item; @@ -12,6 +13,7 @@ import cn.nukkit.math.Vector3; import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; /** * Created on 2015/11/12 by xtypr. @@ -27,10 +29,10 @@ public ParticleCommand(String name) { this.setPermission("nukkit.command.particle"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("name", false, ENUM_VALUES), - new CommandParameter("position", CommandParamType.POSITION, false), - new CommandParameter("count", CommandParamType.INT, true), - new CommandParameter("data", true) + CommandParameter.newEnum("effect", new CommandEnum("Particle", ENUM_VALUES)), + CommandParameter.newType("position", CommandParamType.POSITION), + CommandParameter.newType("count", true, CommandParamType.INT), + CommandParameter.newType("data", true, CommandParamType.INT) }); } @@ -98,7 +100,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) sender.sendMessage(new TranslationContainer("commands.particle.success", name, String.valueOf(count))); - Random random = new Random(System.currentTimeMillis()); + Random random = ThreadLocalRandom.current(); for (int i = 0; i < count; i++) { particle.setComponents( diff --git a/src/main/java/cn/nukkit/command/defaults/SayCommand.java b/src/main/java/cn/nukkit/command/defaults/SayCommand.java index 738bbac5bf1..a55d3c459cf 100644 --- a/src/main/java/cn/nukkit/command/defaults/SayCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/SayCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.command.CommandSender; import cn.nukkit.command.ConsoleCommandSender; +import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; import cn.nukkit.utils.TextFormat; @@ -18,7 +19,7 @@ public SayCommand(String name) { this.setPermission("nukkit.command.say"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("message") + CommandParameter.newType("message", CommandParamType.MESSAGE) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java b/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java index 83720b6a3d7..fcb93688eb2 100644 --- a/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java @@ -21,7 +21,7 @@ public SetWorldSpawnCommand(String name) { this.setPermission("nukkit.command.setworldspawn"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("blockPos", CommandParamType.POSITION, true) + CommandParameter.newType("spawnPoint", true, CommandParamType.POSITION) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java b/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java index 8b98b7ab06e..bf10543d072 100644 --- a/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java @@ -22,7 +22,8 @@ public SpawnpointCommand(String name) { this.setPermission("nukkit.command.spawnpoint"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("blockPos", CommandParamType.POSITION, true), + CommandParameter.newType("player", true, CommandParamType.TARGET), + CommandParameter.newType("spawnPos", true, CommandParamType.POSITION), }); } diff --git a/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java b/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java index 364e9f46963..e69aa948808 100644 --- a/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java @@ -21,18 +21,22 @@ public TeleportCommand(String name) { this.setPermission("nukkit.command.teleport"); this.commandParameters.clear(); this.commandParameters.put("->Player", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), + CommandParameter.newType("destination", CommandParamType.TARGET), }); this.commandParameters.put("Player->Player", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("target", CommandParamType.TARGET, false), + CommandParameter.newType("victim", CommandParamType.TARGET), + CommandParameter.newType("destination", CommandParamType.TARGET) }); this.commandParameters.put("Player->Pos", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("blockPos", CommandParamType.POSITION, false), + CommandParameter.newType("victim", CommandParamType.TARGET), + CommandParameter.newType("destination", CommandParamType.POSITION), + CommandParameter.newType("yRot", true, CommandParamType.VALUE), + CommandParameter.newType("xRot", true, CommandParamType.VALUE) }); this.commandParameters.put("->Pos", new CommandParameter[]{ - new CommandParameter("blockPos", CommandParamType.POSITION, false), + CommandParameter.newType("destination", CommandParamType.POSITION), + CommandParameter.newType("yRot", true, CommandParamType.VALUE), + CommandParameter.newType("xRot", true, CommandParamType.VALUE) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/TellCommand.java b/src/main/java/cn/nukkit/command/defaults/TellCommand.java index fa5640bfe9e..37dc1426bd7 100644 --- a/src/main/java/cn/nukkit/command/defaults/TellCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/TellCommand.java @@ -20,8 +20,8 @@ public TellCommand(String name) { this.setPermission("nukkit.command.tell"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("message") + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newType("message", CommandParamType.MESSAGE) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/TimeCommand.java b/src/main/java/cn/nukkit/command/defaults/TimeCommand.java index b0816b164e8..5d6f39ad95d 100644 --- a/src/main/java/cn/nukkit/command/defaults/TimeCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/TimeCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -23,15 +24,19 @@ public TimeCommand(String name) { "nukkit.command.time.stop"); this.commandParameters.clear(); this.commandParameters.put("1arg", new CommandParameter[]{ - new CommandParameter("start|stop", CommandParamType.STRING, false) + CommandParameter.newEnum("mode", new CommandEnum("TimeMode", "query", "start", "stop")) }); - this.commandParameters.put("2args", new CommandParameter[]{ - new CommandParameter("add|set", CommandParamType.STRING, false), - new CommandParameter("value", CommandParamType.INT, false) + this.commandParameters.put("add", new CommandParameter[]{ + CommandParameter.newEnum("mode", new CommandEnum("TimeModeAdd", "add")), + CommandParameter.newType("amount", CommandParamType.INT) }); - this.commandParameters.put("2args_", new CommandParameter[]{ - new CommandParameter("add|set", CommandParamType.STRING, false), - new CommandParameter("value", CommandParamType.STRING, false) + this.commandParameters.put("setAmount", new CommandParameter[]{ + CommandParameter.newEnum("mode", false, new CommandEnum("TimeModeSet", "set")), + CommandParameter.newType("amount", CommandParamType.INT) + }); + this.commandParameters.put("setTime", new CommandParameter[]{ + CommandParameter.newEnum("mode", new CommandEnum("TimeModeSet", "set")), + CommandParameter.newEnum("time", new CommandEnum("TimeSpec", "day", "night", "midnight", "noon", "sunrise", "sunset")) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/TimingsCommand.java b/src/main/java/cn/nukkit/command/defaults/TimingsCommand.java index e9b81c64f58..beb9354e2ba 100644 --- a/src/main/java/cn/nukkit/command/defaults/TimingsCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/TimingsCommand.java @@ -1,6 +1,7 @@ package cn.nukkit.command.defaults; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; import co.aikar.timings.Timings; @@ -17,7 +18,7 @@ public TimingsCommand(String name) { this.setPermission("nukkit.command.timings"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("on|off|paste") + CommandParameter.newEnum("action", new CommandEnum("TimingsAction", "on", "off", "paste", "verbon", "verboff", "reset", "report")) }); } @@ -71,4 +72,3 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) return true; } } - diff --git a/src/main/java/cn/nukkit/command/defaults/TitleCommand.java b/src/main/java/cn/nukkit/command/defaults/TitleCommand.java index 09e0fa7d870..2bdf150c741 100644 --- a/src/main/java/cn/nukkit/command/defaults/TitleCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/TitleCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.Server; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -18,34 +19,24 @@ public TitleCommand(String name) { this.commandParameters.clear(); this.commandParameters.put("clear", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("clear", new String[]{"clear"}) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("clear", new CommandEnum("TitleClear", "clear")) }); this.commandParameters.put("reset", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("reset", new String[]{"reset"}) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("reset", new CommandEnum("TitleReset", "reset")) }); - this.commandParameters.put("title", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("title", new String[]{"title"}), - new CommandParameter("titleText", CommandParamType.STRING, false) - }); - this.commandParameters.put("subtitle", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("subtitle", new String[]{"subtitle"}), - new CommandParameter("titleText", CommandParamType.STRING, false) - }); - this.commandParameters.put("actionbar", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("actionbar", new String[]{"actionbar"}), - new CommandParameter("titleText", CommandParamType.STRING, false) + this.commandParameters.put("set", new CommandParameter[]{ + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("titleLocation", new CommandEnum("TitleSet", "title", "subtitle", "actionbar")), + CommandParameter.newType("titleText", CommandParamType.MESSAGE) }); this.commandParameters.put("times", new CommandParameter[]{ - new CommandParameter("player", CommandParamType.TARGET, false), - new CommandParameter("times", new String[]{"times"}), - new CommandParameter("fadeIn", CommandParamType.INT, false), - new CommandParameter("stay", CommandParamType.INT, false), - new CommandParameter("fadeOut", CommandParamType.INT, false) + CommandParameter.newType("player", CommandParamType.TARGET), + CommandParameter.newEnum("times", new CommandEnum("TitleTimes", "times")), + CommandParameter.newType("fadeIn", CommandParamType.INT), + CommandParameter.newType("stay", CommandParamType.INT), + CommandParameter.newType("fadeOut", CommandParamType.INT) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/VersionCommand.java b/src/main/java/cn/nukkit/command/defaults/VersionCommand.java index 17d439c77fe..0fbfe8a6949 100644 --- a/src/main/java/cn/nukkit/command/defaults/VersionCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/VersionCommand.java @@ -1,6 +1,8 @@ package cn.nukkit.command.defaults; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandParamType; +import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; import cn.nukkit.network.protocol.ProtocolInfo; import cn.nukkit.plugin.Plugin; @@ -23,6 +25,9 @@ public VersionCommand(String name) { ); this.setPermission("nukkit.command.version"); this.commandParameters.clear(); + this.commandParameters.put("default", new CommandParameter[]{ + CommandParameter.newType("pluginName", true, CommandParamType.STRING) + }); } @Override diff --git a/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java b/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java index 24069cc7b3d..f85de3c6d19 100644 --- a/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -14,15 +15,13 @@ */ public class WeatherCommand extends VanillaCommand { - private java.util.Random rand = new java.util.Random(); - public WeatherCommand(String name) { super(name, "%nukkit.command.weather.description", "%commands.weather.usage"); this.setPermission("nukkit.command.weather"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("clear|rain|thunder", CommandParamType.STRING, false), - new CommandParameter("duration in seconds", CommandParamType.INT, true) + CommandParameter.newEnum("type", new CommandEnum("WeatherType", "clear", "rain", "thunder")), + CommandParameter.newType("duration", true, CommandParamType.INT) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java b/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java index 0e30f93987d..39b3a487e21 100644 --- a/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java @@ -2,6 +2,7 @@ import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; +import cn.nukkit.command.data.CommandEnum; import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.lang.TranslationContainer; @@ -25,15 +26,14 @@ public WhitelistCommand(String name) { ); this.commandParameters.clear(); this.commandParameters.put("1arg", new CommandParameter[]{ - new CommandParameter("on|off|list|reload", CommandParamType.STRING, false) + CommandParameter.newEnum("action", new CommandEnum("WhitelistAction", "on", "off", "list", "reload")) }); this.commandParameters.put("2args", new CommandParameter[]{ - new CommandParameter("add|remove", CommandParamType.STRING, false), - new CommandParameter("player", CommandParamType.TARGET, false) + CommandParameter.newEnum("action", new CommandEnum("WhitelistPlayerAction", "add", "remove")), + CommandParameter.newType("player", CommandParamType.TARGET) }); } - @Override public boolean execute(CommandSender sender, String commandLabel, String[] args) { if (!this.testPermission(sender)) { @@ -107,7 +107,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) } private boolean badPerm(CommandSender sender, String perm) { - if (!sender.hasPermission("nukkit.command.whitelist" + perm)) { + if (!sender.hasPermission("nukkit.command.whitelist." + perm)) { sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.permission")); return true; diff --git a/src/main/java/cn/nukkit/command/defaults/XpCommand.java b/src/main/java/cn/nukkit/command/defaults/XpCommand.java index 31ac7f49414..26a5a5f9323 100644 --- a/src/main/java/cn/nukkit/command/defaults/XpCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/XpCommand.java @@ -18,8 +18,12 @@ public XpCommand(String name) { this.setPermission("nukkit.command.xp"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("amount|level", CommandParamType.INT, false), - new CommandParameter("player", CommandParamType.TARGET, true) + CommandParameter.newType("amount", CommandParamType.INT), + CommandParameter.newType("player", true, CommandParamType.TARGET) + }); + this.commandParameters.put("level", new CommandParameter[]{ + CommandParameter.newPostfix("amount", "l"), + CommandParameter.newType("player", true, CommandParamType.TARGET) }); } diff --git a/src/main/java/cn/nukkit/entity/Entity.java b/src/main/java/cn/nukkit/entity/Entity.java index a64263dc204..41895e15623 100644 --- a/src/main/java/cn/nukkit/entity/Entity.java +++ b/src/main/java/cn/nukkit/entity/Entity.java @@ -118,66 +118,69 @@ public abstract class Entity extends Location implements Metadatable { public static final int DATA_RIDER_ROTATION_LOCKED = 57; //byte public static final int DATA_RIDER_MAX_ROTATION = 58; //float public static final int DATA_RIDER_MIN_ROTATION = 59; //float - public static final int DATA_AREA_EFFECT_CLOUD_RADIUS = 60; //float - public static final int DATA_AREA_EFFECT_CLOUD_WAITING = 61; //int - public static final int DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 62; //int - public static final int DATA_SHULKER_PEEK_ID = 63; //int - public static final int DATA_SHULKER_ATTACH_FACE = 64; //byte - public static final int DATA_SHULKER_ATTACHED = 65; //short - public static final int DATA_SHULKER_ATTACH_POS = 66; //block coords - public static final int DATA_TRADING_PLAYER_EID = 67; //long - public static final int DATA_TRADING_CAREER = 68; - public static final int DATA_HAS_COMMAND_BLOCK = 69; - public static final int DATA_COMMAND_BLOCK_COMMAND = 70; //string - public static final int DATA_COMMAND_BLOCK_LAST_OUTPUT = 71; //string - public static final int DATA_COMMAND_BLOCK_TRACK_OUTPUT = 72; //byte - public static final int DATA_CONTROLLING_RIDER_SEAT_NUMBER = 73; //byte - public static final int DATA_STRENGTH = 74; //int - public static final int DATA_MAX_STRENGTH = 75; //int - public static final int DATA_SPELL_CASTING_COLOR = 76; //int - public static final int DATA_LIMITED_LIFE = 77; - public static final int DATA_ARMOR_STAND_POSE_INDEX = 78; // int - public static final int DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; // int - public static final int DATA_ALWAYS_SHOW_NAMETAG = 80; // byte - public static final int DATA_COLOR_2 = 81; // byte - public static final int DATA_NAME_AUTHOR = 82; - public static final int DATA_SCORE_TAG = 83; //String - public static final int DATA_BALLOON_ATTACHED_ENTITY = 84; // long - public static final int DATA_PUFFERFISH_SIZE = 85; - public static final int DATA_BUBBLE_TIME = 86; - public static final int DATA_AGENT = 87; - public static final int DATA_SITTING_AMOUNT = 88; - public static final int DATA_SITTING_AMOUNT_PREVIOUS = 89; - public static final int DATA_EATING_COUNTER = 90; - public static final int DATA_FLAGS_EXTENDED = 91; - public static final int DATA_LAYING_AMOUNT = 92; - public static final int DATA_LAYING_AMOUNT_PREVIOUS = 93; - public static final int DATA_DURATION = 94; - public static final int DATA_SPAWN_TIME = 95; - public static final int DATA_CHANGE_RATE = 96; - public static final int DATA_CHANGE_ON_PICKUP = 97; - public static final int DATA_PICKUP_COUNT = 98; - public static final int DATA_INTERACT_TEXT = 99; - public static final int DATA_TRADE_TIER = 100; - public static final int DATA_MAX_TRADE_TIER = 101; - public static final int DATA_TRADE_EXPERIENCE = 102; - public static final int DATA_SKIN_ID = 103; // int ??? - public static final int DATA_SPAWNING_FRAMES = 104; - public static final int DATA_COMMAND_BLOCK_TICK_DELAY = 105; - public static final int DATA_COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK = 106; - public static final int DATA_AMBIENT_SOUND_INTERVAL = 107; - public static final int DATA_AMBIENT_SOUND_INTERVAL_RANGE = 108; - public static final int DATA_AMBIENT_SOUND_EVENT_NAME = 109; - public static final int DATA_FALL_DAMAGE_MULTIPLIER = 110; - public static final int DATA_NAME_RAW_TEXT = 111; - public static final int DATA_CAN_RIDE_TARGET = 112; - public static final int DATA_LOW_TIER_CURED_DISCOUNT = 113; - public static final int DATA_HIGH_TIER_CURED_DISCOUNT = 114; - public static final int DATA_NEARBY_CURED_DISCOUNT = 115; - public static final int DATA_NEARBY_CURED_DISCOUNT_TIMESTAMP = 116; - public static final int DATA_HITBOX = 117; - public static final int DATA_IS_BUOYANT = 118; - public static final int DATA_BUOYANCY_DATA = 119; + public static final int DATA_RIDER_ROTATION_OFFSET = 60; + public static final int DATA_AREA_EFFECT_CLOUD_RADIUS = 61; //float + public static final int DATA_AREA_EFFECT_CLOUD_WAITING = 62; //int + public static final int DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 63; //int + public static final int DATA_SHULKER_PEEK_ID = 64; //int + public static final int DATA_SHULKER_ATTACH_FACE = 65; //byte + public static final int DATA_SHULKER_ATTACHED = 66; //short + public static final int DATA_SHULKER_ATTACH_POS = 67; //block coords + public static final int DATA_TRADING_PLAYER_EID = 68; //long + public static final int DATA_TRADING_CAREER = 69; + public static final int DATA_HAS_COMMAND_BLOCK = 70; + public static final int DATA_COMMAND_BLOCK_COMMAND = 71; //string + public static final int DATA_COMMAND_BLOCK_LAST_OUTPUT = 72; //string + public static final int DATA_COMMAND_BLOCK_TRACK_OUTPUT = 73; //byte + public static final int DATA_CONTROLLING_RIDER_SEAT_NUMBER = 74; //byte + public static final int DATA_STRENGTH = 75; //int + public static final int DATA_MAX_STRENGTH = 76; //int + public static final int DATA_SPELL_CASTING_COLOR = 77; //int + public static final int DATA_LIMITED_LIFE = 78; + public static final int DATA_ARMOR_STAND_POSE_INDEX = 79; // int + public static final int DATA_ENDER_CRYSTAL_TIME_OFFSET = 80; // int + public static final int DATA_ALWAYS_SHOW_NAMETAG = 81; // byte + public static final int DATA_COLOR_2 = 82; // byte + public static final int DATA_NAME_AUTHOR = 83; + public static final int DATA_SCORE_TAG = 84; // String + public static final int DATA_BALLOON_ATTACHED_ENTITY = 85; // long + public static final int DATA_PUFFERFISH_SIZE = 86; + public static final int DATA_BUBBLE_TIME = 87; + public static final int DATA_AGENT = 88; + public static final int DATA_SITTING_AMOUNT = 89; + public static final int DATA_SITTING_AMOUNT_PREVIOUS = 90; + public static final int DATA_EATING_COUNTER = 91; + public static final int DATA_FLAGS_EXTENDED = 92; + public static final int DATA_LAYING_AMOUNT = 93; + public static final int DATA_LAYING_AMOUNT_PREVIOUS = 94; + public static final int DATA_DURATION = 95; + public static final int DATA_SPAWN_TIME = 96; + public static final int DATA_CHANGE_RATE = 97; + public static final int DATA_CHANGE_ON_PICKUP = 98; + public static final int DATA_PICKUP_COUNT = 99; + public static final int DATA_INTERACT_TEXT = 100; + public static final int DATA_TRADE_TIER = 101; + public static final int DATA_MAX_TRADE_TIER = 102; + public static final int DATA_TRADE_EXPERIENCE = 103; + public static final int DATA_SKIN_ID = 104; // int ??? + public static final int DATA_SPAWNING_FRAMES = 105; + public static final int DATA_COMMAND_BLOCK_TICK_DELAY = 106; + public static final int DATA_COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK = 107; + public static final int DATA_AMBIENT_SOUND_INTERVAL = 108; + public static final int DATA_AMBIENT_SOUND_INTERVAL_RANGE = 109; + public static final int DATA_AMBIENT_SOUND_EVENT_NAME = 110; + public static final int DATA_FALL_DAMAGE_MULTIPLIER = 111; + public static final int DATA_NAME_RAW_TEXT = 112; + public static final int DATA_CAN_RIDE_TARGET = 113; + public static final int DATA_LOW_TIER_CURED_DISCOUNT = 114; + public static final int DATA_HIGH_TIER_CURED_DISCOUNT = 115; + public static final int DATA_NEARBY_CURED_DISCOUNT = 116; + public static final int DATA_NEARBY_CURED_DISCOUNT_TIMESTAMP = 117; + public static final int DATA_HITBOX = 118; + public static final int DATA_IS_BUOYANT = 119; + public static final int DATA_FREEZING_EFFECT_STRENGTH = 120; + public static final int DATA_BUOYANCY_DATA = 121; + public static final int DATA_GOAT_HORN_COUNT = 122; // Flags public static final int DATA_FLAG_ONFIRE = 0; @@ -275,6 +278,7 @@ public abstract class Entity extends Location implements Metadatable { public static final int DATA_FLAG_CELEBRATING = 92; public static final int DATA_FLAG_ADMIRING = 93; public static final int DATA_FLAG_CELEBRATING_SPECIAL = 94; + public static final int DATA_FLAG_RAM_ATTACK = 96; public static long entityCount = 1; @@ -432,7 +436,7 @@ protected void initEntity() { continue; } - effect.setAmplifier(e.getByte("Amplifier")).setDuration(e.getInt("Duration")).setVisible(e.getBoolean("showParticles")); + effect.setAmplifier(e.getByte("Amplifier")).setDuration(e.getInt("Duration")).setVisible(e.getBoolean("ShowParticles")); this.addEffect(effect); } @@ -1303,7 +1307,7 @@ public boolean entityBaseTick(int tickDiff) { if (this.y <= -16 && this.isAlive()) { if (this instanceof Player) { Player player = (Player) this; - if (player.getGamemode() != 1) this.attack(new EntityDamageEvent(this, DamageCause.VOID, 10)); + if (!player.isCreative()) this.attack(new EntityDamageEvent(this, DamageCause.VOID, 10)); } else { this.attack(new EntityDamageEvent(this, DamageCause.VOID, 10)); hasUpdate = true; @@ -1646,6 +1650,13 @@ public void fall(float fallDistance) { } float damage = (float) Math.floor(fallDistance - 3 - (this.hasEffect(Effect.JUMP) ? this.getEffect(Effect.JUMP).getAmplifier() + 1 : 0)); + + Block down = this.level.getBlock(this.floor().down()); + + if(down instanceof BlockHayBale) { + damage -= (damage * 0.8f); + } + if (damage > 0) { if (!this.isPlayer || level.getGameRules().getBoolean(GameRule.FALL_DAMAGE)) { this.attack(new EntityDamageEvent(this, DamageCause.FALL, damage)); @@ -1653,7 +1664,6 @@ public void fall(float fallDistance) { } if (fallDistance > 0.75) { - Block down = this.level.getBlock(this.floor().down()); if (down.getId() == Item.FARMLAND) { Event ev; diff --git a/src/main/java/cn/nukkit/entity/EntityCreature.java b/src/main/java/cn/nukkit/entity/EntityCreature.java index 0c8ac3d46fc..a8518795fd3 100644 --- a/src/main/java/cn/nukkit/entity/EntityCreature.java +++ b/src/main/java/cn/nukkit/entity/EntityCreature.java @@ -1,6 +1,10 @@ package cn.nukkit.entity; +import cn.nukkit.Player; +import cn.nukkit.entity.mob.EntityEnderDragon; +import cn.nukkit.item.Item; import cn.nukkit.level.format.FullChunk; +import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; /** @@ -11,4 +15,30 @@ public abstract class EntityCreature extends EntityLiving { public EntityCreature(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); } + + // Armor stands, when implemented, should also check this. + @Override + public boolean onInteract(Player player, Item item, Vector3 clickedPos) { + if (item.getId() == Item.NAME_TAG) { + return applyNameTag(player, item); + } + return false; + } + + // Structured like this so I can override nametags in player and dragon classes + // without overriding onInteract. + protected boolean applyNameTag(Player player, Item item){ + if (item.hasCustomName()) { + this.setNameTag(item.getCustomName()); + this.setNameTagVisible(true); + + if(!player.isCreative()) { + player.getInventory().removeItem(item); + } + // Set entity as persistent. + return true; + } + return false; + } + } diff --git a/src/main/java/cn/nukkit/entity/EntityHuman.java b/src/main/java/cn/nukkit/entity/EntityHuman.java index 9209aa514c8..d271fe782b0 100644 --- a/src/main/java/cn/nukkit/entity/EntityHuman.java +++ b/src/main/java/cn/nukkit/entity/EntityHuman.java @@ -107,6 +107,11 @@ protected void initEntity() { if (skinTag.contains("ModelId")) { newSkin.setSkinId(skinTag.getString("ModelId")); } + + if (skinTag.contains("PlayFabID")) { + newSkin.setPlayFabId(skinTag.getString("PlayFabID")); + } + if (skinTag.contains("Data")) { byte[] data = skinTag.getByteArray("Data"); if (skinTag.contains("SkinImageWidth") && skinTag.contains("SkinImageHeight")) { @@ -159,7 +164,8 @@ protected void initEntity() { byte[] image = animationTag.getByteArray("Image"); int width = animationTag.getInt("ImageWidth"); int height = animationTag.getInt("ImageHeight"); - newSkin.getAnimations().add(new SkinAnimation(new SerializedImage(width, height, image), type, frames)); + int expression = animationTag.getInt("AnimationExpression"); + newSkin.getAnimations().add(new SkinAnimation(new SerializedImage(width, height, image), type, frames, expression)); } } if (skinTag.contains("ArmSize")) { @@ -240,6 +246,7 @@ public void saveNBT() { .putInt("Type", animation.type) .putInt("ImageWidth", animation.image.width) .putInt("ImageHeight", animation.image.height) + .putInt("AnimationExpression", animation.expression) .putByteArray("Image", animation.image.data)); } skinTag.putList(animationsTag); @@ -266,6 +273,10 @@ public void saveNBT() { .putList(colors)); } } + + if (!this.getSkin().getPlayFabId().isEmpty()) { + skinTag.putString("PlayFabID", this.getSkin().getPlayFabId()); + } this.namedTag.putCompound("Skin", skinTag); } } @@ -285,7 +296,7 @@ public void spawnTo(Player player) { } if (this instanceof Player) - this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getName(), this.skin, ((Player) this).getLoginChainData().getXUID(), new Player[]{player}); + this.server.updatePlayerListData(this.getUniqueId(), this.getId(), ((Player) this).getDisplayName(), this.skin, ((Player) this).getLoginChainData().getXUID(), new Player[]{player}); else this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getName(), this.skin, new Player[]{player}); @@ -320,7 +331,7 @@ public void spawnTo(Player player) { } if (!(this instanceof Player)) { - this.server.removePlayerListData(this.getUniqueId(), new Player[]{player}); + this.server.removePlayerListData(this.getUniqueId(), player); } } } diff --git a/src/main/java/cn/nukkit/entity/EntityHumanType.java b/src/main/java/cn/nukkit/entity/EntityHumanType.java index 32dc4aa4385..46445ac67c7 100644 --- a/src/main/java/cn/nukkit/entity/EntityHumanType.java +++ b/src/main/java/cn/nukkit/entity/EntityHumanType.java @@ -244,4 +244,9 @@ public void setOnFire(int seconds) { super.setOnFire(seconds); } + + @Override + protected boolean applyNameTag(Player player, Item item) { + return false; + } } diff --git a/src/main/java/cn/nukkit/entity/EntityLiving.java b/src/main/java/cn/nukkit/entity/EntityLiving.java index c086e2588fb..baae0c55054 100644 --- a/src/main/java/cn/nukkit/entity/EntityLiving.java +++ b/src/main/java/cn/nukkit/entity/EntityLiving.java @@ -56,7 +56,7 @@ protected float getDrag() { protected float movementSpeed = 0.1f; - protected int turtleTicks = 200; + protected int turtleTicks = 0; @Override protected void initEntity() { @@ -206,18 +206,17 @@ public boolean entityBaseTick() { public boolean entityBaseTick(int tickDiff) { Timings.livingEntityBaseTickTimer.startTiming(); boolean isBreathing = !this.isInsideOfWater(); - if (this instanceof Player && (((Player) this).isCreative() || ((Player) this).isSpectator())) { - isBreathing = true; - } if (this instanceof Player) { - if (!isBreathing && ((Player) this).getInventory().getHelmet() instanceof ItemTurtleShell) { - if (turtleTicks > 0) { - isBreathing = true; - turtleTicks--; - } - } else { + if (isBreathing && ((Player) this).getInventory().getHelmet() instanceof ItemTurtleShell) { turtleTicks = 200; + } else if (turtleTicks > 0) { + isBreathing = true; + turtleTicks--; + } + + if ((((Player) this).isCreative() || ((Player) this).isSpectator())) { + isBreathing = true; } } @@ -232,7 +231,7 @@ public boolean entityBaseTick(int tickDiff) { this.attack(new EntityDamageEvent(this, DamageCause.SUFFOCATION, 1)); } - if (this.isOnLadder() || this.hasEffect(Effect.LEVITATION)) { + if (this.isOnLadder() || this.hasEffect(Effect.LEVITATION) || this.hasEffect(Effect.SLOW_FALLING)) { this.resetFallDistance(); } diff --git a/src/main/java/cn/nukkit/entity/data/Skin.java b/src/main/java/cn/nukkit/entity/data/Skin.java index 94c7281a9af..5c15ea1f637 100644 --- a/src/main/java/cn/nukkit/entity/data/Skin.java +++ b/src/main/java/cn/nukkit/entity/data/Skin.java @@ -33,6 +33,7 @@ public class Skin { private final String fullSkinId = UUID.randomUUID().toString(); private String skinId; + private String playFabId = ""; private String skinResourcePatch = GEOMETRY_CUSTOM; private SerializedImage skinData; private final List animations = new ArrayList<>(); @@ -262,6 +263,14 @@ public String getFullSkinId() { return fullSkinId; } + public void setPlayFabId(String playFabId) { + this.playFabId = playFabId; + } + + public String getPlayFabId() { + return this.playFabId; + } + private static SerializedImage parseBufferedImage(BufferedImage image) { FastByteArrayOutputStream outputStream = new FastByteArrayOutputStream(); for (int y = 0; y < image.getHeight(); y++) { diff --git a/src/main/java/cn/nukkit/entity/item/EntityBoat.java b/src/main/java/cn/nukkit/entity/item/EntityBoat.java index cf01f678ce8..e4bbcf68264 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityBoat.java +++ b/src/main/java/cn/nukkit/entity/item/EntityBoat.java @@ -16,7 +16,6 @@ import cn.nukkit.level.GameRule; import cn.nukkit.level.Location; import cn.nukkit.level.format.FullChunk; -import cn.nukkit.level.particle.SmokeParticle; import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.NukkitMath; import cn.nukkit.math.Vector3; @@ -34,8 +33,6 @@ public class EntityBoat extends EntityVehicle { public static final int NETWORK_ID = 90; - public static final int DATA_WOOD_ID = 20; - public static final Vector3f RIDER_PLAYER_OFFSET = new Vector3f(0, 1.02001f, 0); public static final Vector3f RIDER_OFFSET = new Vector3f(0, -0.2f, 0); @@ -50,6 +47,7 @@ public class EntityBoat extends EntityVehicle { public static final double SINKING_MAX_SPEED = 0.005; protected boolean sinking = true; + public int woodID; public EntityBoat(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); @@ -62,7 +60,7 @@ public EntityBoat(FullChunk chunk, CompoundTag nbt) { protected void initEntity() { super.initEntity(); - this.dataProperties.putByte(DATA_WOOD_ID, this.namedTag.getByte("woodID")); + this.dataProperties.putInt(DATA_VARIANT, woodID = this.namedTag.getByte("woodID")); } @Override @@ -119,9 +117,6 @@ public void close() { for (Entity linkedEntity : this.passengers) { linkedEntity.riding = null; } - - SmokeParticle particle = new SmokeParticle(this); - this.level.addParticle(particle); } @Override @@ -374,7 +369,7 @@ public void onPaddle(AnimatePacket.Action animation, float value) { public void applyEntityCollision(Entity entity) { if (this.riding == null && entity.riding != this && !entity.passengers.contains(this)) { if (!entity.boundingBox.intersectsWith(this.boundingBox.grow(0.20000000298023224, -0.1, 0.20000000298023224)) - || entity instanceof Player && ((Player) entity).getGamemode() == Player.SPECTATOR) { + || entity instanceof Player && ((Player) entity).isSpectator()) { return; } @@ -414,7 +409,14 @@ public void kill() { super.kill(); if (level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) { - this.level.dropItem(this, new ItemBoat()); + this.level.dropItem(this, new ItemBoat(this.woodID)); } } + + @Override + public void saveNBT() { + super.saveNBT(); + + this.namedTag.putByte("woodID", this.woodID); + } } diff --git a/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java b/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java index 3d40871c72b..9ffe8bd3023 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java +++ b/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java @@ -1,6 +1,7 @@ package cn.nukkit.entity.item; import cn.nukkit.block.Block; +import cn.nukkit.block.BlockLiquid; import cn.nukkit.entity.Entity; import cn.nukkit.entity.data.IntEntityData; import cn.nukkit.event.entity.EntityBlockChangeEvent; @@ -133,9 +134,37 @@ public boolean onUpdate(int currentTick) { if (onGround) { close(); Block block = level.getBlock(pos); - if (block.getId() > 0 && block.isTransparent() && !block.canBeReplaced()) { - if (this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) { - getLevel().dropItem(this, Item.get(this.getBlock(), this.getDamage(), 1)); + + Vector3 floorPos = (new Vector3(x - 0.5, y, z - 0.5)).floor(); + Block floorBlock = this.level.getBlock(floorPos); + if (this.getBlock() == Block.SNOW_LAYER && floorBlock.getId() == Block.SNOW_LAYER && (floorBlock.getDamage() & 0x7) != 0x7) { + int mergedHeight = (floorBlock.getDamage() & 0x7) + 1 + (this.getDamage() & 0x7) + 1; + if (mergedHeight > 8) { + EntityBlockChangeEvent event = new EntityBlockChangeEvent(this, floorBlock, Block.get(Block.SNOW_LAYER, 0x7)); + this.server.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.level.setBlock(floorPos, event.getTo(), true); + + Vector3 abovePos = floorPos.up(); + Block aboveBlock = this.level.getBlock(abovePos); + if (aboveBlock.getId() == Block.AIR) { + EntityBlockChangeEvent event2 = new EntityBlockChangeEvent(this, aboveBlock, Block.get(Block.SNOW_LAYER, mergedHeight - 8 - 1)); + this.server.getPluginManager().callEvent(event2); + if (!event2.isCancelled()) { + this.level.setBlock(abovePos, event2.getTo(), true); + } + } + } + } else { + EntityBlockChangeEvent event = new EntityBlockChangeEvent(this, floorBlock, Block.get(Block.SNOW_LAYER, mergedHeight - 1)); + this.server.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.level.setBlock(floorPos, event.getTo(), true); + } + } + } else if (block.getId() > 0 && block.isTransparent() && !block.canBeReplaced() || this.getBlock() == Block.SNOW_LAYER && block instanceof BlockLiquid) { + if (this.getBlock() != Block.SNOW_LAYER ? this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS) : this.level.getGameRules().getBoolean(GameRule.DO_TILE_DROPS)) { + getLevel().dropItem(this, Block.get(this.getBlock(), this.getDamage()).toItem()); } } else { EntityBlockChangeEvent event = new EntityBlockChangeEvent(this, block, Block.get(getBlock(), getDamage())); diff --git a/src/main/java/cn/nukkit/entity/item/EntityFishingHook.java b/src/main/java/cn/nukkit/entity/item/EntityFishingHook.java index 57c1fdac81b..d535ead7113 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityFishingHook.java +++ b/src/main/java/cn/nukkit/entity/item/EntityFishingHook.java @@ -19,9 +19,6 @@ import cn.nukkit.math.Vector3; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; -import cn.nukkit.nbt.tag.DoubleTag; -import cn.nukkit.nbt.tag.FloatTag; -import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.network.protocol.AddEntityPacket; import cn.nukkit.network.protocol.EntityEventPacket; @@ -159,17 +156,17 @@ public void fishBites() { EntityEventPacket pk = new EntityEventPacket(); pk.eid = this.getId(); pk.event = EntityEventPacket.FISH_HOOK_HOOK; - Server.broadcastPacket(this.level.getPlayers().values(), pk); + Server.broadcastPacket(this.getViewers().values(), pk); EntityEventPacket bubblePk = new EntityEventPacket(); bubblePk.eid = this.getId(); bubblePk.event = EntityEventPacket.FISH_HOOK_BUBBLE; - Server.broadcastPacket(this.level.getPlayers().values(), bubblePk); + Server.broadcastPacket(this.getViewers().values(), bubblePk); EntityEventPacket teasePk = new EntityEventPacket(); teasePk.eid = this.getId(); teasePk.event = EntityEventPacket.FISH_HOOK_TEASE; - Server.broadcastPacket(this.level.getPlayers().values(), teasePk); + Server.broadcastPacket(this.getViewers().values(), teasePk); Random random = new Random(); for (int i = 0; i < 5; i++) { @@ -238,7 +235,7 @@ public void reelLine() { EntityEventPacket pk = new EntityEventPacket(); pk.eid = this.getId(); pk.event = EntityEventPacket.FISH_HOOK_TEASE; - Server.broadcastPacket(this.level.getPlayers().values(), pk); + Server.broadcastPacket(this.getViewers().values(), pk); } if (!this.closed) { this.kill(); diff --git a/src/main/java/cn/nukkit/entity/item/EntityItem.java b/src/main/java/cn/nukkit/entity/item/EntityItem.java index 4f375f7a41e..4f2234c403a 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityItem.java +++ b/src/main/java/cn/nukkit/entity/item/EntityItem.java @@ -105,6 +105,11 @@ protected void initEntity() { this.item = NBTIO.getItemHelper(this.namedTag.getCompound("Item")); this.setDataFlag(DATA_FLAGS, DATA_FLAG_GRAVITY, true); + int id = this.item.getId(); + if (id >= Item.NETHERITE_INGOT && id <= Item.NETHERITE_SCRAP) { + this.fireProof = true; // Netherite items are fireproof + } + this.server.getPluginManager().callEvent(new ItemSpawnEvent(this)); } @@ -159,7 +164,7 @@ public boolean onUpdate(int currentTick) { packet.eid = getId(); packet.data = newAmount; packet.event = EntityEventPacket.MERGE_ITEMS; - Server.broadcastPacket(this.getLevel().getPlayers().values(), packet); + Server.broadcastPacket(this.getViewers().values(), packet); } } } @@ -167,7 +172,7 @@ public boolean onUpdate(int currentTick) { boolean hasUpdate = this.entityBaseTick(tickDiff); - if (isInsideOfFire()) { + if (!this.fireProof && this.isInsideOfFire()) { this.kill(); } diff --git a/src/main/java/cn/nukkit/entity/item/EntityMinecartAbstract.java b/src/main/java/cn/nukkit/entity/item/EntityMinecartAbstract.java index 3341469cbea..6eb7a70ecaa 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityMinecartAbstract.java +++ b/src/main/java/cn/nukkit/entity/item/EntityMinecartAbstract.java @@ -20,7 +20,6 @@ import cn.nukkit.level.GameRule; import cn.nukkit.level.Location; import cn.nukkit.level.format.FullChunk; -import cn.nukkit.level.particle.SmokeParticle; import cn.nukkit.math.MathHelper; import cn.nukkit.math.NukkitMath; import cn.nukkit.math.Vector3; @@ -261,9 +260,6 @@ public void close() { entity.riding = null; } } - - SmokeParticle particle = new SmokeParticle(this); - level.addParticle(particle); } @Override @@ -281,7 +277,7 @@ public boolean onInteract(Player p, Item item, Vector3 clickedPos) { @Override public void applyEntityCollision(cn.nukkit.entity.Entity entity) { - if (entity != riding && !(entity instanceof Player && ((Player) entity).getGamemode() == Player.SPECTATOR)) { + if (entity != riding && !(entity instanceof Player && ((Player) entity).isSpectator())) { if (entity instanceof EntityLiving && !(entity instanceof EntityHuman) && motionX * motionX + motionZ * motionZ > 0.01D diff --git a/src/main/java/cn/nukkit/entity/item/EntityPainting.java b/src/main/java/cn/nukkit/entity/item/EntityPainting.java index 63344a6da46..25aba1f81a3 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityPainting.java +++ b/src/main/java/cn/nukkit/entity/item/EntityPainting.java @@ -117,7 +117,7 @@ public boolean attack(EntityDamageEvent source) { if (super.attack(source)) { if (source instanceof EntityDamageByEntityEvent) { Entity damager = ((EntityDamageByEntityEvent) source).getDamager(); - if (damager instanceof Player && ((Player) damager).isSurvival() && this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) { + if (damager instanceof Player && (((Player) damager).isAdventure() || ((Player) damager).isSurvival()) && this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) { this.level.dropItem(this, new ItemPainting()); } } diff --git a/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java b/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java index 31320312d8f..0a681dd3ef6 100644 --- a/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java +++ b/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java @@ -1,6 +1,9 @@ package cn.nukkit.entity.mob; +import cn.nukkit.Player; +import cn.nukkit.item.Item; import cn.nukkit.level.format.FullChunk; +import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; /** @@ -35,6 +38,11 @@ public void initEntity() { this.setMaxHealth(200); } + @Override + protected boolean applyNameTag(Player player, Item item) { + return false; + } + @Override public String getName() { return "EnderDragon"; diff --git a/src/main/java/cn/nukkit/entity/mob/EntityMob.java b/src/main/java/cn/nukkit/entity/mob/EntityMob.java index 1a953a33857..4b8497ac238 100644 --- a/src/main/java/cn/nukkit/entity/mob/EntityMob.java +++ b/src/main/java/cn/nukkit/entity/mob/EntityMob.java @@ -17,16 +17,4 @@ public EntityMob(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); } - @Override - public boolean onInteract(Player player, Item item, Vector3 clickedPos) { - if (item.getId() == Item.NAME_TAG) { - if (item.hasCustomName()) { - this.setNameTag(item.getCustomName()); - this.setNameTagVisible(true); - player.getInventory().removeItem(item); - return true; - } - } - return false; - } } diff --git a/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java b/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java index aa735018517..764434a8ee0 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java @@ -27,16 +27,4 @@ public boolean isBreedingItem(Item item) { return item.getId() == Item.WHEAT; //default } - @Override - public boolean onInteract(Player player, Item item, Vector3 clickedPos) { - if (item.getId() == Item.NAME_TAG) { - if (item.hasCustomName()) { - this.setNameTag(item.getCustomName()); - this.setNameTagVisible(true); - player.getInventory().removeItem(item); - return true; - } - } - return false; - } } diff --git a/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java b/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java index 6db5998d149..3f38522a2cf 100644 --- a/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java +++ b/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java @@ -4,6 +4,7 @@ import cn.nukkit.entity.EntityLiving; import cn.nukkit.entity.data.LongEntityData; import cn.nukkit.entity.item.EntityEndCrystal; +import cn.nukkit.entity.mob.EntityBlaze; import cn.nukkit.event.entity.*; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; import cn.nukkit.level.MovingObjectPosition; @@ -62,7 +63,7 @@ public boolean attack(EntityDamageEvent source) { public void onCollideWithEntity(Entity entity) { this.server.getPluginManager().callEvent(new ProjectileHitEvent(this, MovingObjectPosition.fromEntity(entity))); - float damage = this.getResultDamage(); + float damage = this instanceof EntitySnowball && entity instanceof EntityBlaze ? 3 : this.getResultDamage(); EntityDamageEvent ev; if (this.shootingEntity == null) { diff --git a/src/main/java/cn/nukkit/entity/projectile/EntityThrownTrident.java b/src/main/java/cn/nukkit/entity/projectile/EntityThrownTrident.java index 6830b186e03..c012fc6251f 100644 --- a/src/main/java/cn/nukkit/entity/projectile/EntityThrownTrident.java +++ b/src/main/java/cn/nukkit/entity/projectile/EntityThrownTrident.java @@ -14,9 +14,6 @@ import cn.nukkit.math.Vector3; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; -import cn.nukkit.nbt.tag.DoubleTag; -import cn.nukkit.nbt.tag.FloatTag; -import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.network.protocol.AddEntityPacket; import cn.nukkit.network.protocol.LevelSoundEventPacket; @@ -150,11 +147,6 @@ public boolean onUpdate(int currentTick) { this.setCritical(false); } - if (this.age > 1200) { - this.close(); - hasUpdate = true; - } - this.timing.stopTiming(); return hasUpdate; diff --git a/src/main/java/cn/nukkit/event/block/AnvilDamageEvent.java b/src/main/java/cn/nukkit/event/block/AnvilDamageEvent.java new file mode 100644 index 00000000000..aee8e00eefe --- /dev/null +++ b/src/main/java/cn/nukkit/event/block/AnvilDamageEvent.java @@ -0,0 +1,53 @@ +package cn.nukkit.event.block; + +import cn.nukkit.Player; +import cn.nukkit.block.Block; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class AnvilDamageEvent extends BlockEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final int oldDamage; + private int newDamage; + private DamageCause cause; + private final Player player; + + public AnvilDamageEvent(Block block, int oldDamage, int newDamage, DamageCause cause, Player player) { + super(block); + this.oldDamage = oldDamage; + this.newDamage = newDamage; + this.cause = cause; + this.player = player; + } + + public int getOldDamage() { + return this.oldDamage; + } + + public int getNewDamage() { + return this.newDamage; + } + + public void setNewDamage(int newDamage) { + this.newDamage = newDamage; + } + + public DamageCause getCause() { + return this.cause; + } + + public Player getPlayer() { + return this.player; + } + + public enum DamageCause { + USE, + FALL + } +} diff --git a/src/main/java/cn/nukkit/event/inventory/InventoryPickupTridentEvent.java b/src/main/java/cn/nukkit/event/inventory/InventoryPickupTridentEvent.java new file mode 100644 index 00000000000..8514d04c890 --- /dev/null +++ b/src/main/java/cn/nukkit/event/inventory/InventoryPickupTridentEvent.java @@ -0,0 +1,26 @@ +package cn.nukkit.event.inventory; + +import cn.nukkit.entity.projectile.EntityThrownTrident; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.inventory.Inventory; + +public class InventoryPickupTridentEvent extends InventoryEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final EntityThrownTrident trident; + + public InventoryPickupTridentEvent(Inventory inventory, EntityThrownTrident trident) { + super(inventory); + this.trident = trident; + } + + public EntityThrownTrident getTrident() { + return trident; + } +} diff --git a/src/main/java/cn/nukkit/event/inventory/RepairItemEvent.java b/src/main/java/cn/nukkit/event/inventory/RepairItemEvent.java new file mode 100644 index 00000000000..dd7acdc3df3 --- /dev/null +++ b/src/main/java/cn/nukkit/event/inventory/RepairItemEvent.java @@ -0,0 +1,55 @@ +package cn.nukkit.event.inventory; + +import cn.nukkit.Player; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.inventory.AnvilInventory; +import cn.nukkit.item.Item; + +public class RepairItemEvent extends InventoryEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private Item oldItem; + private Item newItem; + private Item materialItem; + private int cost; + private Player player; + + public RepairItemEvent(AnvilInventory inventory, Item oldItem, Item newItem, Item materialItem, int cost, Player player) { + super(inventory); + this.oldItem = oldItem; + this.newItem = newItem; + this.materialItem = materialItem; + this.cost = cost; + this.player = player; + } + + public Item getOldItem() { + return this.oldItem; + } + + public Item getNewItem() { + return this.newItem; + } + + public Item getMaterialItem() { + return this.materialItem; + } + + public int getCost() { + return this.cost; + } + + public void setCost(int cost) { + this.cost = cost; + } + + public Player getPlayer() { + return this.player; + } +} diff --git a/src/main/java/cn/nukkit/event/level/StructureGrowEvent.java b/src/main/java/cn/nukkit/event/level/StructureGrowEvent.java new file mode 100644 index 00000000000..1bea3eeb218 --- /dev/null +++ b/src/main/java/cn/nukkit/event/level/StructureGrowEvent.java @@ -0,0 +1,44 @@ +package cn.nukkit.event.level; + +import cn.nukkit.block.Block; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +import java.util.List; +import java.util.Objects; + +/** + * @author KCodeYT (Nukkit Project) + */ +public class StructureGrowEvent extends LevelEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final Block block; + private final List blocks; + + public StructureGrowEvent(Block block, List blocks) { + super(Objects.requireNonNull(block.getLevel())); + this.block = block; + this.blocks = blocks; + } + + public Block getBlock() { + return this.block; + } + + public List getBlockList() { + return this.blocks; + } + + public void setBlockList(List blocks) { + this.blocks.clear(); + if(blocks != null) + this.blocks.addAll(blocks); + } + +} diff --git a/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java b/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java index e483f49c13e..9bc11dfa819 100644 --- a/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java +++ b/src/main/java/cn/nukkit/event/player/PlayerAsyncPreLoginEvent.java @@ -2,7 +2,9 @@ import cn.nukkit.Player; import cn.nukkit.Server; +import cn.nukkit.entity.data.Skin; import cn.nukkit.event.HandlerList; +import cn.nukkit.utils.LoginChainData; import java.util.ArrayList; import java.util.List; @@ -24,6 +26,8 @@ public static HandlerList getHandlers() { private final String name; private final UUID uuid; + private final LoginChainData chainData; + private Skin skin; private final String address; private final int port; @@ -32,9 +36,11 @@ public static HandlerList getHandlers() { private final List> scheduledActions = new ArrayList<>(); - public PlayerAsyncPreLoginEvent(String name, UUID uuid, String address, int port) { + public PlayerAsyncPreLoginEvent(String name, UUID uuid, LoginChainData chainData, Skin skin, String address, int port) { this.name = name; this.uuid = uuid; + this.chainData = chainData; + this.skin = skin; this.address = address; this.port = port; } @@ -45,23 +51,39 @@ public Player getPlayer() { } public String getName() { - return name; + return this.name; } public UUID getUuid() { - return uuid; + return this.uuid; + } + + public LoginChainData getChainData() { + return this.chainData; + } + + public String getXuid() { + return this.chainData.getXUID(); + } + + public Skin getSkin() { + return this.skin; + } + + public void setSkin(Skin skin) { + this.skin = skin; } public String getAddress() { - return address; + return this.address; } public int getPort() { - return port; + return this.port; } public LoginResult getLoginResult() { - return loginResult; + return this.loginResult; } public void setLoginResult(LoginResult loginResult) { diff --git a/src/main/java/cn/nukkit/event/server/ServerStopEvent.java b/src/main/java/cn/nukkit/event/server/ServerStopEvent.java new file mode 100644 index 00000000000..f0938dada84 --- /dev/null +++ b/src/main/java/cn/nukkit/event/server/ServerStopEvent.java @@ -0,0 +1,17 @@ +package cn.nukkit.event.server; + +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +/** + * author: NycuRO + * NukkitX Project + */ +public class ServerStopEvent extends ServerEvent { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } +} diff --git a/src/main/java/cn/nukkit/inventory/AnvilInventory.java b/src/main/java/cn/nukkit/inventory/AnvilInventory.java index 78b8e26a1ac..b359a10ab47 100644 --- a/src/main/java/cn/nukkit/inventory/AnvilInventory.java +++ b/src/main/java/cn/nukkit/inventory/AnvilInventory.java @@ -2,12 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.item.Item; -import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.level.Position; -import cn.nukkit.network.protocol.LevelSoundEventPacket; - -import java.util.ArrayList; -import java.util.Arrays; /** * author: MagicDroidX @@ -15,111 +10,20 @@ */ public class AnvilInventory extends FakeBlockUIComponent { + public static final int ANVIL_INPUT_UI_SLOT = 1; + public static final int ANVIL_MATERIAL_UI_SLOT = 2; + public static final int ANVIL_OUTPUT_UI_SLOT = CREATED_ITEM_OUTPUT_UI_SLOT; + public static final int TARGET = 0; public static final int SACRIFICE = 1; - public static final int RESULT = 50; + public static final int RESULT = ANVIL_OUTPUT_UI_SLOT - 1; //1: offset + + private int cost; public AnvilInventory(PlayerUIInventory playerUI, Position position) { super(playerUI, InventoryType.ANVIL, 1, position); } - public boolean onRename(Player player, Item resultItem) { - Item local = getItem(TARGET); - Item second = getItem(SACRIFICE); - - if (!resultItem.equals(local, true, false) || resultItem.getCount() != local.getCount()) { - //Item does not match target item. Everything must match except the tags. - return false; - } - - if (local.equals(resultItem)) { - //just item transaction - return true; - } - - if (local.getId() != 0 && second.getId() == 0) { //only rename - local.setCustomName(resultItem.getCustomName()); - setItem(RESULT, local); - player.getInventory().addItem(local); - clearAll(); - player.getInventory().sendContents(player); - sendContents(player); - - player.getLevel().addLevelSoundEvent(player, LevelSoundEventPacket.SOUND_RANDOM_ANVIL_USE); - return true; - } else if (local.getId() != 0 && second.getId() != 0) { //enchants combining - if (!local.equals(second, true, false)) { - return false; - } - - if (local.getId() != 0 && second.getId() != 0) { - Item result = local.clone(); - int enchants = 0; - - ArrayList enchantments = new ArrayList<>(Arrays.asList(second.getEnchantments())); - - ArrayList baseEnchants = new ArrayList<>(); - - for (Enchantment ench : local.getEnchantments()) { - if (ench.isMajor()) { - baseEnchants.add(ench); - } - } - - for (Enchantment enchantment : enchantments) { - if (enchantment.getLevel() < 0 || enchantment.getId() < 0) { - continue; - } - - if (enchantment.isMajor()) { - boolean same = false; - boolean another = false; - - for (Enchantment baseEnchant : baseEnchants) { - if (baseEnchant.getId() == enchantment.getId()) - same = true; - else { - another = true; - } - } - - if (!same && another) { - continue; - } - } - - Enchantment localEnchantment = local.getEnchantment(enchantment.getId()); - - if (localEnchantment != null) { - int level = Math.max(localEnchantment.getLevel(), enchantment.getLevel()); - - if (localEnchantment.getLevel() == enchantment.getLevel()) - level++; - - enchantment.setLevel(level); - result.addEnchantment(enchantment); - continue; - } - - result.addEnchantment(enchantment); - enchants++; - } - - result.setCustomName(resultItem.getCustomName()); - - player.getInventory().addItem(result); - player.getInventory().sendContents(player); - clearAll(); - sendContents(player); - - player.getLevel().addLevelSoundEvent(player, LevelSoundEventPacket.SOUND_RANDOM_ANVIL_USE); - return true; - } - } - - return false; - } - @Override public void onClose(Player who) { super.onClose(who); @@ -137,4 +41,24 @@ public void onOpen(Player who) { super.onOpen(who); who.craftingType = Player.CRAFTING_ANVIL; } + + public Item getInputSlot() { + return this.getItem(TARGET); + } + + public Item getMaterialSlot() { + return this.getItem(SACRIFICE); + } + + public Item getOutputSlot() { + return this.getItem(RESULT); + } + + public int getCost() { + return this.cost; + } + + public void setCost(int cost) { + this.cost = cost; + } } diff --git a/src/main/java/cn/nukkit/inventory/ContainerInventory.java b/src/main/java/cn/nukkit/inventory/ContainerInventory.java index 5bb977844cd..0b8af477475 100644 --- a/src/main/java/cn/nukkit/inventory/ContainerInventory.java +++ b/src/main/java/cn/nukkit/inventory/ContainerInventory.java @@ -58,6 +58,7 @@ public void onOpen(Player who) { public void onClose(Player who) { ContainerClosePacket pk = new ContainerClosePacket(); pk.windowId = who.getWindowId(this); + pk.wasServerInitiated = who.getClosingWindowId() != pk.windowId; who.dataPacket(pk); super.onClose(who); } diff --git a/src/main/java/cn/nukkit/inventory/CraftingManager.java b/src/main/java/cn/nukkit/inventory/CraftingManager.java index 00580ff33a0..e7cc23e9ace 100644 --- a/src/main/java/cn/nukkit/inventory/CraftingManager.java +++ b/src/main/java/cn/nukkit/inventory/CraftingManager.java @@ -2,8 +2,8 @@ import cn.nukkit.Server; import cn.nukkit.item.Item; -import cn.nukkit.network.protocol.BatchPacket; import cn.nukkit.network.protocol.CraftingDataPacket; +import cn.nukkit.network.protocol.DataPacket; import cn.nukkit.utils.BinaryStream; import cn.nukkit.utils.Config; import cn.nukkit.utils.MainLogger; @@ -15,7 +15,6 @@ import java.io.File; import java.io.InputStream; import java.util.*; -import java.util.zip.Deflater; /** * author: MagicDroidX @@ -26,11 +25,13 @@ public class CraftingManager { public final Collection recipes = new ArrayDeque<>(); - public static BatchPacket packet = null; + public static DataPacket packet = null; protected final Map> shapedRecipes = new Int2ObjectOpenHashMap<>(); public final Map furnaceRecipes = new Int2ObjectOpenHashMap<>(); + public final Map multiRecipes = new HashMap<>(); + public final Map brewingRecipes = new Int2ObjectOpenHashMap<>(); public final Map containerRecipes = new Int2ObjectOpenHashMap<>(); @@ -152,6 +153,9 @@ private void loadRecipes(Config config) { } this.registerRecipe(new FurnaceRecipe(resultItem, inputItem)); break; + case 4: + this.registerRecipe(new MultiRecipe(UUID.fromString((String) recipe.get("uuid")))); + break; default: break; } @@ -188,7 +192,6 @@ private void loadRecipes(Config config) { public void rebuildPacket() { CraftingDataPacket pk = new CraftingDataPacket(); pk.cleanRecipes = true; - for (Recipe recipe : this.getRecipes()) { if (recipe instanceof ShapedRecipe) { pk.addShapedRecipe((ShapedRecipe) recipe); @@ -201,6 +204,10 @@ public void rebuildPacket() { pk.addFurnaceRecipe(recipe); } + for (MultiRecipe recipe : this.multiRecipes.values()) { + pk.addMultiRecipe(recipe); + } + for (BrewingRecipe recipe : brewingRecipes.values()) { pk.addBrewingRecipe(recipe); } @@ -209,9 +216,10 @@ public void rebuildPacket() { pk.addContainerRecipe(recipe); } - pk.encode(); - - packet = pk.compress(Deflater.BEST_COMPRESSION); + pk.tryEncode(); + // TODO: find out whats wrong with compression + // packet = pk.compress(Deflater.BEST_COMPRESSION); + packet = pk; } public Collection getRecipes() { @@ -380,6 +388,10 @@ private boolean matchItemsAccumulation(CraftingRecipe recipe, List inputLi return false; } + public void registerMultiRecipe(MultiRecipe recipe) { + this.multiRecipes.put(recipe.getId(), recipe); + } + public static class Entry { final int resultItemId; final int resultMeta; diff --git a/src/main/java/cn/nukkit/inventory/FakeBlockUIComponent.java b/src/main/java/cn/nukkit/inventory/FakeBlockUIComponent.java index 6a04f160be2..0c802b22460 100644 --- a/src/main/java/cn/nukkit/inventory/FakeBlockUIComponent.java +++ b/src/main/java/cn/nukkit/inventory/FakeBlockUIComponent.java @@ -67,6 +67,7 @@ public void onOpen(Player who) { public void onClose(Player who) { ContainerClosePacket pk = new ContainerClosePacket(); pk.windowId = who.getWindowId(this); + pk.wasServerInitiated = who.getClosingWindowId() != pk.windowId; who.dataPacket(pk); super.onClose(who); } diff --git a/src/main/java/cn/nukkit/inventory/Fuel.java b/src/main/java/cn/nukkit/inventory/Fuel.java index 376b2503af2..87bffb2c581 100644 --- a/src/main/java/cn/nukkit/inventory/Fuel.java +++ b/src/main/java/cn/nukkit/inventory/Fuel.java @@ -59,7 +59,7 @@ public abstract class Fuel { duration.put(Item.BROWN_MUSHROOM_BLOCK, (short) 300); duration.put(Item.RED_MUSHROOM_BLOCK, (short) 300); duration.put(Item.FISHING_ROD, (short) 300); - duration.put(Item.WOODEN_BUTTON, (short) 100); + duration.put(Item.WOODEN_BUTTON, (short) 300); duration.put(Item.WOODEN_DOOR, (short) 200); duration.put(Item.SPRUCE_DOOR, (short) 200); duration.put(Item.BIRCH_DOOR, (short) 200); @@ -67,5 +67,7 @@ public abstract class Fuel { duration.put(Item.ACACIA_DOOR, (short) 200); duration.put(Item.DARK_OAK_DOOR, (short) 200); duration.put(Item.BANNER, (short) 300); + duration.put(Item.DEAD_BUSH, (short) 100); + duration.put(Item.SIGN, (short) 200); } } diff --git a/src/main/java/cn/nukkit/inventory/MultiRecipe.java b/src/main/java/cn/nukkit/inventory/MultiRecipe.java new file mode 100644 index 00000000000..adf791436b9 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/MultiRecipe.java @@ -0,0 +1,33 @@ +package cn.nukkit.inventory; + +import cn.nukkit.item.Item; + +import java.util.UUID; + +public class MultiRecipe implements Recipe { + + private final UUID id; + + public MultiRecipe(UUID id) { + this.id = id; + } + + @Override + public Item getResult() { + throw new UnsupportedOperationException(); + } + + @Override + public void registerToCraftingManager(CraftingManager manager) { + manager.registerMultiRecipe(this); + } + + @Override + public RecipeType getType() { + return RecipeType.MULTI; + } + + public UUID getId() { + return this.id; + } +} diff --git a/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java b/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java index 459c04c2f33..faf894b73f6 100755 --- a/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java +++ b/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java @@ -63,6 +63,7 @@ public void onOpen(Player who) { public void onClose(Player who) { ContainerClosePacket containerClosePacket = new ContainerClosePacket(); containerClosePacket.windowId = who.getWindowId(this); + containerClosePacket.wasServerInitiated = who.getClosingWindowId() != containerClosePacket.windowId; who.dataPacket(containerClosePacket); super.onClose(who); diff --git a/src/main/java/cn/nukkit/inventory/PlayerInventory.java b/src/main/java/cn/nukkit/inventory/PlayerInventory.java index 73fcc1da0aa..6febe54ea13 100644 --- a/src/main/java/cn/nukkit/inventory/PlayerInventory.java +++ b/src/main/java/cn/nukkit/inventory/PlayerInventory.java @@ -338,8 +338,7 @@ public void sendArmorContents(Player[] players) { MobArmorEquipmentPacket pk = new MobArmorEquipmentPacket(); pk.eid = this.getHolder().getId(); pk.slots = armor; - pk.encode(); - pk.isEncoded = true; + pk.tryEncode(); for (Player player : players) { if (player.equals(this.getHolder())) { @@ -387,8 +386,7 @@ public void sendArmorSlot(int index, Player[] players) { MobArmorEquipmentPacket pk = new MobArmorEquipmentPacket(); pk.eid = this.getHolder().getId(); pk.slots = armor; - pk.encode(); - pk.isEncoded = true; + pk.tryEncode(); for (Player player : players) { if (player.equals(this.getHolder())) { @@ -482,7 +480,6 @@ public void sendCreativeContents() { } Player p = (Player) this.getHolder(); - //InventoryContentPacket pk = new InventoryContentPacket(); CreativeContentPacket pk = new CreativeContentPacket(); if (!p.isSpectator()) { //fill it for all gamemodes except spectator @@ -514,6 +511,7 @@ public void onOpen(Player who) { public void onClose(Player who) { ContainerClosePacket pk = new ContainerClosePacket(); pk.windowId = who.getWindowId(this); + pk.wasServerInitiated = who.getClosingWindowId() != pk.windowId; who.dataPacket(pk); // player can never stop viewing their own inventory if (who != holder) { diff --git a/src/main/java/cn/nukkit/inventory/PlayerOffhandInventory.java b/src/main/java/cn/nukkit/inventory/PlayerOffhandInventory.java index dd17cbda0fb..7e2d3e5ae43 100644 --- a/src/main/java/cn/nukkit/inventory/PlayerOffhandInventory.java +++ b/src/main/java/cn/nukkit/inventory/PlayerOffhandInventory.java @@ -71,8 +71,7 @@ private MobEquipmentPacket createMobEquipmentPacket(Item item) { pk.item = item; pk.inventorySlot = 1; pk.windowId = ContainerIds.OFFHAND; - pk.encode(); - pk.isEncoded = true; + pk.tryEncode(); return pk; } diff --git a/src/main/java/cn/nukkit/inventory/PlayerUIComponent.java b/src/main/java/cn/nukkit/inventory/PlayerUIComponent.java index 0b272324dde..41bc32d7a5f 100644 --- a/src/main/java/cn/nukkit/inventory/PlayerUIComponent.java +++ b/src/main/java/cn/nukkit/inventory/PlayerUIComponent.java @@ -8,6 +8,9 @@ import java.util.Set; public class PlayerUIComponent extends BaseInventory { + + public static final int CREATED_ITEM_OUTPUT_UI_SLOT = 50; + private final PlayerUIInventory playerUI; private final int offset; private final int size; diff --git a/src/main/java/cn/nukkit/inventory/transaction/RepairItemTransaction.java b/src/main/java/cn/nukkit/inventory/transaction/RepairItemTransaction.java new file mode 100644 index 00000000000..8dbdba1e6f5 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/RepairItemTransaction.java @@ -0,0 +1,396 @@ +package cn.nukkit.inventory.transaction; + +import cn.nukkit.Player; +import cn.nukkit.block.Block; +import cn.nukkit.event.block.AnvilDamageEvent; +import cn.nukkit.event.block.AnvilDamageEvent.DamageCause; +import cn.nukkit.event.inventory.RepairItemEvent; +import cn.nukkit.inventory.AnvilInventory; +import cn.nukkit.inventory.FakeBlockMenu; +import cn.nukkit.inventory.Inventory; +import cn.nukkit.inventory.transaction.action.RepairItemAction; +import cn.nukkit.inventory.transaction.action.InventoryAction; +import cn.nukkit.item.Item; +import cn.nukkit.item.enchantment.Enchantment; +import cn.nukkit.network.protocol.LevelEventPacket; +import cn.nukkit.network.protocol.types.NetworkInventoryAction; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class RepairItemTransaction extends InventoryTransaction { + + private Item inputItem; + private Item materialItem; + private Item outputItem; + + private int cost; + + public RepairItemTransaction(Player source, List actions) { + super(source, actions); + } + + @Override + public boolean canExecute() { + Inventory inventory = getSource().getWindowById(Player.ANVIL_WINDOW_ID); + if (inventory == null) { + return false; + } + AnvilInventory anvilInventory = (AnvilInventory) inventory; + return this.inputItem != null && this.outputItem != null && this.inputItem.equals(anvilInventory.getInputSlot(), true, true) + && (!this.hasMaterial() || this.materialItem.equals(anvilInventory.getMaterialSlot(), true, true)) + && this.checkRecipeValid(); + } + + @Override + public boolean execute() { + if (this.hasExecuted() || !this.canExecute()) { + this.source.removeAllWindows(false); + this.sendInventories(); + return false; + } + AnvilInventory inventory = (AnvilInventory) getSource().getWindowById(Player.ANVIL_WINDOW_ID); + + if (inventory.getCost() != this.cost && !this.source.isCreative()) { + this.source.getServer().getLogger().debug("Got unexpected cost " + inventory.getCost() + " from " + this.source.getName() + "(expected " + this.cost + ")"); + } + + RepairItemEvent event = new RepairItemEvent(inventory, this.inputItem, this.outputItem, this.materialItem, this.cost, this.source); + this.source.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.source.removeAllWindows(false); + this.sendInventories(); + return true; + } + + for (InventoryAction action : this.actions) { + if (action.execute(this.source)) { + action.onExecuteSuccess(this.source); + } else { + action.onExecuteFail(this.source); + } + } + + FakeBlockMenu holder = inventory.getHolder(); + Block block = this.source.level.getBlock(holder.getFloorX(), holder.getFloorY(), holder.getFloorZ()); + if (block.getId() == Block.ANVIL) { + int oldDamage = block.getDamage() >= 8 ? 2 : block.getDamage() >= 4 ? 1 : 0; + int newDamage = !this.source.isCreative() && ThreadLocalRandom.current().nextInt(100) < 12 ? oldDamage + 1 : oldDamage; + + AnvilDamageEvent ev = new AnvilDamageEvent(block, oldDamage, newDamage, DamageCause.USE, this.source); + ev.setCancelled(oldDamage == newDamage); + this.source.getServer().getPluginManager().callEvent(ev); + if (!ev.isCancelled()) { + newDamage = ev.getNewDamage(); + if (newDamage > 2) { + this.source.level.setBlock(block, Block.get(Block.AIR), true); + this.source.level.addLevelEvent(block, LevelEventPacket.EVENT_SOUND_ANVIL_BREAK); + } else { + if (newDamage < 0) { + newDamage = 0; + } + if (newDamage != oldDamage) { + block.setDamage((newDamage << 2) | (block.getDamage() & 0x3)); + this.source.level.setBlock(block, block, true); + } + this.source.level.addLevelEvent(block, LevelEventPacket.EVENT_SOUND_ANVIL_USE); + } + } else { + this.source.level.addLevelEvent(block, LevelEventPacket.EVENT_SOUND_ANVIL_USE); + } + } + + if (!this.source.isCreative()) { + this.source.setExperience(this.source.getExperience(), this.source.getExperienceLevel() - event.getCost()); + } + return true; + } + + @Override + public void addAction(InventoryAction action) { + super.addAction(action); + if (action instanceof RepairItemAction) { + switch (((RepairItemAction) action).getType()) { + case NetworkInventoryAction.SOURCE_TYPE_ANVIL_INPUT: + this.inputItem = action.getTargetItem(); + break; + case NetworkInventoryAction.SOURCE_TYPE_ANVIL_RESULT: + this.outputItem = action.getSourceItem(); + break; + case NetworkInventoryAction.SOURCE_TYPE_ANVIL_MATERIAL: + this.materialItem = action.getTargetItem(); + break; + } + } + } + + private boolean checkRecipeValid() { + int cost = 0; + int baseRepairCost = this.inputItem.getRepairCost(); + + if (this.isMapRecipe()) { + if (!this.matchMapRecipe()) { + return false; + } + baseRepairCost = 0; + } else if (this.hasMaterial()) { + baseRepairCost += this.materialItem.getRepairCost(); + + if (this.inputItem.getMaxDurability() != -1 && this.matchRepairItem()) { + int maxRepairDamage = this.inputItem.getMaxDurability() / 4; + int repairDamage = Math.min(this.inputItem.getDamage(), maxRepairDamage); + if (repairDamage <= 0) { + return false; + } + + int damage = this.inputItem.getDamage(); + for (; repairDamage > 0 && cost < this.materialItem.getCount(); cost++) { + damage = damage - repairDamage; + repairDamage = Math.min(damage, maxRepairDamage); + } + if (this.outputItem.getDamage() != damage) { + return false; + } + } else { + boolean consumeEnchantedBook = this.materialItem.getId() == Item.ENCHANTED_BOOK && this.materialItem.hasEnchantments(); + if (!consumeEnchantedBook && (this.inputItem.getMaxDurability() == -1 || this.inputItem.getId() != this.materialItem.getId())) { + return false; + } + + if (!consumeEnchantedBook && this.inputItem.getMaxDurability() != -1) { + int damage = this.inputItem.getDamage() - this.inputItem.getMaxDurability() + this.materialItem.getDamage() - this.inputItem.getMaxDurability() * 12 / 100 + 1; + if (damage < 0) { + damage = 0; + } + + if (damage < this.inputItem.getDamage()) { + if (this.outputItem.getDamage() != damage) { + return false; + } + cost += 2; + } + } + + Int2IntMap enchantments = new Int2IntOpenHashMap(); + enchantments.defaultReturnValue(-1); + for (Enchantment enchantment : this.inputItem.getEnchantments()) { + enchantments.put(enchantment.getId(), enchantment.getLevel()); + } + + boolean hasCompatibleEnchantments = false; + boolean hasIncompatibleEnchantments = false; + for (Enchantment materialEnchantment : this.materialItem.getEnchantments()) { + Enchantment enchantment = this.inputItem.getEnchantment(materialEnchantment.getId()); + int inputLevel = enchantment != null ? enchantment.getLevel() : 0; + int materialLevel = materialEnchantment.getLevel(); + int outputLevel = inputLevel == materialLevel ? materialLevel + 1 : Math.max(materialLevel, inputLevel); + + boolean canEnchant = materialEnchantment.canEnchant(this.inputItem) || this.inputItem.getId() == Item.ENCHANTED_BOOK; + for (Enchantment inputEnchantment : this.inputItem.getEnchantments()) { + if (inputEnchantment.getId() != materialEnchantment.getId() && !materialEnchantment.isCompatibleWith(inputEnchantment)) { + canEnchant = false; + cost++; + } + } + + if (!canEnchant) { + hasIncompatibleEnchantments = true; + } else { + hasCompatibleEnchantments = true; + if (outputLevel > materialEnchantment.getMaxLevel()) { + outputLevel = materialEnchantment.getMaxLevel(); + } + + enchantments.put(materialEnchantment.getId(), outputLevel); + int rarityFactor; + switch (materialEnchantment.getRarity()) { + case COMMON: + rarityFactor = 1; + break; + case UNCOMMON: + rarityFactor = 2; + break; + case RARE: + rarityFactor = 4; + break; + case VERY_RARE: + default: + rarityFactor = 8; + break; + } + + if (consumeEnchantedBook) { + rarityFactor = Math.max(1, rarityFactor / 2); + } + + cost += rarityFactor * Math.max(0, outputLevel - inputLevel); + if (this.inputItem.getCount() > 1) { + cost = 40; + } + } + } + + Enchantment[] outputEnchantments = this.outputItem.getEnchantments(); + if (hasIncompatibleEnchantments && !hasCompatibleEnchantments || enchantments.size() != outputEnchantments.length) { + return false; + } + + for (Enchantment enchantment : outputEnchantments) { + if (enchantments.get(enchantment.getId()) != enchantment.getLevel()) { + return false; + } + } + } + } + + int renameCost = 0; + if (!this.inputItem.getCustomName().equals(this.outputItem.getCustomName())) { + if (this.outputItem.getCustomName().length() > 30) { + return false; + } + renameCost = 1; + cost += renameCost; + } + + this.cost = baseRepairCost + cost; + if (renameCost == cost && renameCost > 0 && this.cost >= 40) { + this.cost = 39; + } + if (baseRepairCost < 0 || cost < 0 || cost == 0 && !this.isMapRecipe() || this.cost > 39 && !this.source.isCreative()) { + return false; + } + + int nextBaseRepairCost = this.inputItem.getRepairCost(); + if (!this.isMapRecipe()) { + if (this.hasMaterial() && nextBaseRepairCost < this.materialItem.getRepairCost()) { + nextBaseRepairCost = this.materialItem.getRepairCost(); + } + if (renameCost == 0 || renameCost != cost) { + nextBaseRepairCost = 2 * nextBaseRepairCost + 1; + } + } + if (this.outputItem.getRepairCost() != nextBaseRepairCost) { + this.source.getServer().getLogger().debug("Got unexpected base cost " + this.outputItem.getRepairCost() + " from " + this.source.getName() + "(expected " + nextBaseRepairCost + ")"); + return false; + } + + return true; + } + + private boolean hasMaterial() { + return this.materialItem != null && !this.materialItem.isNull(); + } + + private boolean isMapRecipe() { + return this.hasMaterial() && (this.inputItem.getId() == Item.MAP || this.inputItem.getId() == Item.EMPTY_MAP) + && (this.materialItem.getId() == Item.EMPTY_MAP || this.materialItem.getId() == Item.PAPER || this.materialItem.getId() == Item.COMPASS); + } + + private boolean matchMapRecipe() { + if (this.inputItem.getId() == Item.EMPTY_MAP) { + return this.inputItem.getDamage() != 2 && this.materialItem.getId() == Item.COMPASS // locator + && this.outputItem.getId() == Item.EMPTY_MAP && this.outputItem.getDamage() == 2 && this.outputItem.getCount() == 1; + } else if (this.inputItem.getId() == Item.MAP && this.outputItem.getDamage() == this.inputItem.getDamage()) { + if (this.materialItem.getId() == Item.COMPASS) { // locator + return this.inputItem.getDamage() != 2 && this.outputItem.getId() == Item.MAP && this.outputItem.getCount() == 1; + } else if (this.materialItem.getId() == Item.EMPTY_MAP) { // clone + return this.outputItem.getId() == Item.MAP && this.outputItem.getCount() == 2; + } else if (this.materialItem.getId() == Item.PAPER && this.materialItem.getCount() >= 8) { // zoom out + return this.inputItem.getDamage() < 3 && this.outputItem.getId() == Item.MAP && this.outputItem.getCount() == 1; + } + } + return false; + } + + private boolean matchRepairItem() { + switch (this.inputItem.getId()) { + case Item.LEATHER_CAP: + case Item.LEATHER_TUNIC: + case Item.LEATHER_PANTS: + case Item.LEATHER_BOOTS: + return this.materialItem.getId() == Item.LEATHER; + case Item.WOODEN_SWORD: + case Item.WOODEN_PICKAXE: + case Item.WOODEN_SHOVEL: + case Item.WOODEN_AXE: + case Item.WOODEN_HOE: + return this.materialItem.getId() == Item.PLANKS; + case Item.IRON_SWORD: + case Item.IRON_PICKAXE: + case Item.IRON_SHOVEL: + case Item.IRON_AXE: + case Item.IRON_HOE: + case Item.IRON_HELMET: + case Item.IRON_CHESTPLATE: + case Item.IRON_LEGGINGS: + case Item.IRON_BOOTS: + case Item.CHAIN_HELMET: + case Item.CHAIN_CHESTPLATE: + case Item.CHAIN_LEGGINGS: + case Item.CHAIN_BOOTS: + return this.materialItem.getId() == Item.IRON_INGOT; + case Item.GOLD_SWORD: + case Item.GOLD_PICKAXE: + case Item.GOLD_SHOVEL: + case Item.GOLD_AXE: + case Item.GOLD_HOE: + case Item.GOLD_HELMET: + case Item.GOLD_CHESTPLATE: + case Item.GOLD_LEGGINGS: + case Item.GOLD_BOOTS: + return this.materialItem.getId() == Item.GOLD_INGOT; + case Item.DIAMOND_SWORD: + case Item.DIAMOND_PICKAXE: + case Item.DIAMOND_SHOVEL: + case Item.DIAMOND_AXE: + case Item.DIAMOND_HOE: + case Item.DIAMOND_HELMET: + case Item.DIAMOND_CHESTPLATE: + case Item.DIAMOND_LEGGINGS: + case Item.DIAMOND_BOOTS: + return this.materialItem.getId() == Item.DIAMOND; + case Item.NETHERITE_SWORD: + case Item.NETHERITE_PICKAXE: + case Item.NETHERITE_SHOVEL: + case Item.NETHERITE_AXE: + case Item.NETHERITE_HOE: + case Item.NETHERITE_HELMET: + case Item.NETHERITE_CHESTPLATE: + case Item.NETHERITE_LEGGINGS: + case Item.NETHERITE_BOOTS: + return this.materialItem.getId() == Item.NETHERITE_INGOT; + case Item.TURTLE_SHELL: + return this.materialItem.getId() == Item.SCUTE; + case Item.ELYTRA: + return this.materialItem.getId() == Item.PHANTOM_MEMBRANE; + } + return false; + } + + public Item getInputItem() { + return this.inputItem; + } + + public Item getMaterialItem() { + return this.materialItem; + } + + public Item getOutputItem() { + return this.outputItem; + } + + public int getCost() { + return this.cost; + } + + public static boolean checkForRepairItemPart(List actions) { + for (InventoryAction action : actions) { + if (action instanceof RepairItemAction) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/action/RepairItemAction.java b/src/main/java/cn/nukkit/inventory/transaction/action/RepairItemAction.java new file mode 100644 index 00000000000..b126b2b2552 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/action/RepairItemAction.java @@ -0,0 +1,38 @@ +package cn.nukkit.inventory.transaction.action; + +import cn.nukkit.Player; +import cn.nukkit.item.Item; + +public class RepairItemAction extends InventoryAction { + + private int type; + + public RepairItemAction(Item sourceItem, Item targetItem, int type) { + super(sourceItem, targetItem); + this.type = type; + } + + @Override + public boolean isValid(Player source) { + return source.getWindowById(Player.ANVIL_WINDOW_ID) != null; + } + + @Override + public boolean execute(Player source) { + return true; + } + + @Override + public void onExecuteSuccess(Player source) { + + } + + @Override + public void onExecuteFail(Player source) { + + } + + public int getType() { + return this.type; + } +} diff --git a/src/main/java/cn/nukkit/item/Item.java b/src/main/java/cn/nukkit/item/Item.java index f6357b44a18..1aee75237f6 100644 --- a/src/main/java/cn/nukkit/item/Item.java +++ b/src/main/java/cn/nukkit/item/Item.java @@ -12,6 +12,7 @@ import cn.nukkit.math.Vector3; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.IntTag; import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.nbt.tag.StringTag; import cn.nukkit.nbt.tag.Tag; @@ -247,7 +248,7 @@ public static void init() { list[GOLD_HORSE_ARMOR] = ItemHorseArmorGold.class; //418 list[DIAMOND_HORSE_ARMOR] = ItemHorseArmorDiamond.class; //419 //TODO: list[LEAD] = ItemLead.class; //420 - //TODO: list[NAME_TAG] = ItemNameTag.class; //421 + list[NAME_TAG] = ItemNameTag.class; //421 list[PRISMARINE_CRYSTALS] = ItemPrismarineCrystals.class; //422 list[RAW_MUTTON] = ItemMuttonRaw.class; //423 list[COOKED_MUTTON] = ItemMuttonCooked.class; //424 @@ -288,6 +289,8 @@ public static void init() { list[TURTLE_SHELL] = ItemTurtleShell.class; //469 + list[CROSSBOW] = ItemCrossbow.class; //471 + list[SWEET_BERRIES] = ItemSweetBerries.class; //477 list[RECORD_11] = ItemRecord11.class; @@ -308,6 +311,18 @@ public static void init() { list[HONEYCOMB] = ItemHoneycomb.class; //736 list[HONEY_BOTTLE] = ItemHoneyBottle.class; //737 + list[NETHERITE_INGOT] = ItemIngotNetherite.class; //742 + list[NETHERITE_SWORD] = ItemSwordNetherite.class; //743 + list[NETHERITE_SHOVEL] = ItemShovelNetherite.class; //744 + list[NETHERITE_PICKAXE] = ItemPickaxeNetherite.class; //745 + list[NETHERITE_AXE] = ItemAxeNetherite.class; //746 + list[NETHERITE_HOE] = ItemHoeNetherite.class; //747 + list[NETHERITE_HELMET] = ItemHelmetNetherite.class; //748 + list[NETHERITE_CHESTPLATE] = ItemChestplateNetherite.class; //749 + list[NETHERITE_LEGGINGS] = ItemLeggingsNetherite.class; //750 + list[NETHERITE_BOOTS] = ItemBootsNetherite.class; //751 + list[NETHERITE_SCRAP] = ItemScrapNetherite.class; //752 + list[RECORD_PIGSTEP] = ItemRecordPigstep.class; //759 for (int i = 0; i < 256; ++i) { @@ -642,6 +657,37 @@ public Enchantment[] getEnchantments() { return enchantments.toArray(new Enchantment[0]); } + public boolean hasEnchantment(int id) { + return this.getEnchantment(id) != null; + } + + public int getRepairCost() { + if (this.hasCompoundTag()) { + CompoundTag tag = this.getNamedTag(); + if (tag.contains("RepairCost")) { + Tag repairCost = tag.get("RepairCost"); + if (repairCost instanceof IntTag) { + return ((IntTag) repairCost).data; + } + } + } + return 0; + } + + public Item setRepairCost(int cost) { + if (cost <= 0 && this.hasCompoundTag()) { + return this.setNamedTag(this.getNamedTag().remove("RepairCost")); + } + + CompoundTag tag; + if (!this.hasCompoundTag()) { + tag = new CompoundTag(); + } else { + tag = this.getNamedTag(); + } + return this.setNamedTag(tag.putInt("RepairCost", cost)); + } + public boolean hasCustomName() { if (!this.hasCompoundTag()) { return false; diff --git a/src/main/java/cn/nukkit/item/ItemArmor.java b/src/main/java/cn/nukkit/item/ItemArmor.java index 696a4c5049e..de91f4e626f 100644 --- a/src/main/java/cn/nukkit/item/ItemArmor.java +++ b/src/main/java/cn/nukkit/item/ItemArmor.java @@ -1,6 +1,7 @@ package cn.nukkit.item; import cn.nukkit.Player; +import cn.nukkit.level.Sound; import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.ByteTag; import cn.nukkit.nbt.tag.Tag; @@ -17,7 +18,8 @@ abstract public class ItemArmor extends Item implements ItemDurable { public static final int TIER_CHAIN = 3; public static final int TIER_GOLD = 4; public static final int TIER_DIAMOND = 5; - public static final int TIER_OTHER = 6; + public static final int TIER_NETHERITE = 6; + public static final int TIER_OTHER = 7; public ItemArmor(int id) { super(id); @@ -48,25 +50,30 @@ public boolean isArmor() { @Override public boolean onClickAir(Player player, Vector3 directionVector) { boolean equip = false; - if (this.isHelmet() && player.getInventory().getHelmet().isNull()) { + Item oldSlotItem = Item.get(AIR); + if (this.isHelmet()) { + oldSlotItem = player.getInventory().getHelmet(); if (player.getInventory().setHelmet(this)) { equip = true; } - } else if (this.isChestplate() && player.getInventory().getChestplate().isNull()) { + } else if (this.isChestplate()) { + oldSlotItem = player.getInventory().getChestplate(); if (player.getInventory().setChestplate(this)) { equip = true; } - } else if (this.isLeggings() && player.getInventory().getLeggings().isNull()) { + } else if (this.isLeggings()) { + oldSlotItem = player.getInventory().getLeggings(); if (player.getInventory().setLeggings(this)) { equip = true; } - } else if (this.isBoots() && player.getInventory().getBoots().isNull()) { + } else if (this.isBoots()) { + oldSlotItem = player.getInventory().getBoots(); if (player.getInventory().setBoots(this)) { equip = true; } } if (equip) { - player.getInventory().clear(player.getInventory().getHeldItemIndex()); + player.getInventory().setItem(player.getInventory().getHeldItemIndex(), oldSlotItem); switch (this.getTier()) { case TIER_CHAIN: player.getLevel().addLevelSoundEvent(player, LevelSoundEventPacket.SOUND_ARMOR_EQUIP_CHAIN); @@ -83,6 +90,8 @@ public boolean onClickAir(Player player, Vector3 directionVector) { case TIER_LEATHER: player.getLevel().addLevelSoundEvent(player, LevelSoundEventPacket.SOUND_ARMOR_EQUIP_LEATHER); break; + case TIER_NETHERITE: + player.getLevel().addSound(player, Sound.ARMOR_EQUIP_NETHERITE); case TIER_OTHER: default: player.getLevel().addLevelSoundEvent(player, LevelSoundEventPacket.SOUND_ARMOR_EQUIP_GENERIC); diff --git a/src/main/java/cn/nukkit/item/ItemAxeNetherite.java b/src/main/java/cn/nukkit/item/ItemAxeNetherite.java new file mode 100644 index 00000000000..d1060ad868e --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemAxeNetherite.java @@ -0,0 +1,36 @@ +package cn.nukkit.item; + +public class ItemAxeNetherite extends ItemTool { + + public ItemAxeNetherite() { + this(0, 1); + } + + public ItemAxeNetherite(Integer meta) { + this(meta, 1); + } + + public ItemAxeNetherite(Integer meta, int count) { + super(NETHERITE_AXE, meta, count, "Netherite Axe"); + } + + @Override + public boolean isAxe() { + return true; + } + + @Override + public int getAttackDamage() { + return 7; + } + + @Override + public int getTier() { + return ItemTool.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return ItemTool.DURABILITY_NETHERITE; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemBoat.java b/src/main/java/cn/nukkit/item/ItemBoat.java index 8afcdabcb2e..d7c81ef1607 100644 --- a/src/main/java/cn/nukkit/item/ItemBoat.java +++ b/src/main/java/cn/nukkit/item/ItemBoat.java @@ -57,7 +57,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, return false; } - if (player.isSurvival()) { + if (player.isAdventure() || player.isSurvival()) { Item item = player.getInventory().getItemInHand(); item.setCount(item.getCount() - 1); player.getInventory().setItemInHand(item); diff --git a/src/main/java/cn/nukkit/item/ItemBootsNetherite.java b/src/main/java/cn/nukkit/item/ItemBootsNetherite.java new file mode 100644 index 00000000000..6761c3a26bb --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemBootsNetherite.java @@ -0,0 +1,41 @@ +package cn.nukkit.item; + +public class ItemBootsNetherite extends ItemArmor { + + public ItemBootsNetherite() { + this(0, 1); + } + + public ItemBootsNetherite(Integer meta) { + this(meta, 1); + } + + public ItemBootsNetherite(Integer meta, int count) { + super(NETHERITE_BOOTS, meta, count, "Netherite Boots"); + } + + @Override + public boolean isBoots() { + return true; + } + + @Override + public int getTier() { + return ItemArmor.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return 481; + } + + @Override + public int getArmorPoints() { + return 3; + } + + @Override + public int getToughness() { + return 2; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemBow.java b/src/main/java/cn/nukkit/item/ItemBow.java index af5f230612b..dc953ec9ab1 100644 --- a/src/main/java/cn/nukkit/item/ItemBow.java +++ b/src/main/java/cn/nukkit/item/ItemBow.java @@ -57,7 +57,7 @@ public boolean onRelease(Player player, int ticksUsed) { Inventory inventory = player.getOffhandInventory(); - if (!inventory.contains(itemArrow) && !(inventory = player.getInventory()).contains(itemArrow) && player.isSurvival()) { + if (!inventory.contains(itemArrow) && !(inventory = player.getInventory()).contains(itemArrow) && (player.isAdventure() || player.isSurvival())) { player.getOffhandInventory().sendContents(player); inventory.sendContents(player); return false; @@ -116,7 +116,7 @@ public boolean onRelease(Player player, int ticksUsed) { if (infinity && (projectile = entityShootBowEvent.getProjectile()) instanceof EntityArrow) { ((EntityArrow) projectile).setPickupMode(EntityArrow.PICKUP_CREATIVE); } - if (player.isSurvival()) { + if (player.isAdventure() || player.isSurvival()) { if (!infinity) { inventory.removeItem(itemArrow); } diff --git a/src/main/java/cn/nukkit/item/ItemBucket.java b/src/main/java/cn/nukkit/item/ItemBucket.java index 2b899ca0965..ad76a0c8a7d 100644 --- a/src/main/java/cn/nukkit/item/ItemBucket.java +++ b/src/main/java/cn/nukkit/item/ItemBucket.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.block.*; +import cn.nukkit.entity.Entity; import cn.nukkit.event.player.PlayerBucketEmptyEvent; import cn.nukkit.event.player.PlayerBucketFillEvent; import cn.nukkit.event.player.PlayerItemConsumeEvent; @@ -80,6 +81,10 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { + if (player.isAdventure()) { + return false; + } + Block targetBlock = Block.get(getDamageByTarget(this.meta)); if (targetBlock instanceof BlockAir) { @@ -103,7 +108,11 @@ public boolean onActivate(Level level, Player player, Block block, Block target, Item clone = this.clone(); clone.setCount(this.getCount() - 1); player.getInventory().setItemInHand(clone); - player.getInventory().addItem(ev.getItem()); + if (player.getInventory().canAddItem(ev.getItem())) { + player.getInventory().addItem(ev.getItem()); + } else { + player.dropItem(ev.getItem()); + } } if (target instanceof BlockLava) { @@ -120,7 +129,9 @@ public boolean onActivate(Level level, Player player, Block block, Block target, } else if (targetBlock instanceof BlockLiquid) { Item result = Item.get(BUCKET, 0, 1); PlayerBucketEmptyEvent ev = new PlayerBucketEmptyEvent(player, block, face, this, result); - ev.setCancelled(!block.canBeFlowedInto()); + if (!block.canBeFlowedInto()) { + ev.setCancelled(true); + } if (player.getLevel().getDimension() == Level.DIMENSION_NETHER && this.getDamage() != 10) { ev.setCancelled(true); @@ -134,7 +145,11 @@ public boolean onActivate(Level level, Player player, Block block, Block target, Item clone = this.clone(); clone.setCount(this.getCount() - 1); player.getInventory().setItemInHand(clone); - player.getInventory().addItem(ev.getItem()); + if (player.getInventory().canAddItem(ev.getItem())) { + player.getInventory().addItem(ev.getItem()); + } else { + player.dropItem(ev.getItem()); + } } if (this.getDamage() == 10) { @@ -143,6 +158,25 @@ public boolean onActivate(Level level, Player player, Block block, Block target, level.addLevelSoundEvent(block, LevelSoundEventPacket.SOUND_BUCKET_EMPTY_WATER); } + switch (this.getDamage()) { + case 2: + Entity e2 = Entity.createEntity("Cod", block); + if (e2 != null) e2.spawnToAll(); + break; + case 3: + Entity e3 = Entity.createEntity("Salmon", block); + if (e3 != null) e3.spawnToAll(); + break; + case 4: + Entity e4 = Entity.createEntity("TropicalFish", block); + if (e4 != null) e4.spawnToAll(); + break; + case 5: + Entity e5 = Entity.createEntity("Pufferfish", block); + if (e5 != null) e5.spawnToAll(); + break; + } + return true; } else { player.getLevel().sendBlocks(new Player[]{player}, new Block[]{Block.get(Block.AIR, 0, block)}, UpdateBlockPacket.FLAG_ALL_PRIORITY, 1); @@ -160,6 +194,10 @@ public boolean onClickAir(Player player, Vector3 directionVector) { @Override public boolean onUse(Player player, int ticksUsed) { + if (player.isSpectator()) { + return false; + } + PlayerItemConsumeEvent consumeEvent = new PlayerItemConsumeEvent(player, this); player.getServer().getPluginManager().callEvent(consumeEvent); @@ -168,7 +206,7 @@ public boolean onUse(Player player, int ticksUsed) { return false; } - if (player.isSurvival()) { + if (!player.isCreative()) { this.count--; player.getInventory().setItemInHand(this); player.getInventory().addItem(new ItemBucket()); diff --git a/src/main/java/cn/nukkit/item/ItemCake.java b/src/main/java/cn/nukkit/item/ItemCake.java index efce9bca8c4..d3f0021ed00 100644 --- a/src/main/java/cn/nukkit/item/ItemCake.java +++ b/src/main/java/cn/nukkit/item/ItemCake.java @@ -24,6 +24,6 @@ public ItemCake(Integer meta, int count) { @Override public int getMaxStackSize() { - return 1; + return 64; } } diff --git a/src/main/java/cn/nukkit/item/ItemChestplateNetherite.java b/src/main/java/cn/nukkit/item/ItemChestplateNetherite.java new file mode 100644 index 00000000000..09674778ac9 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemChestplateNetherite.java @@ -0,0 +1,41 @@ +package cn.nukkit.item; + +public class ItemChestplateNetherite extends ItemArmor { + + public ItemChestplateNetherite() { + this(0, 1); + } + + public ItemChestplateNetherite(Integer meta) { + this(meta, 1); + } + + public ItemChestplateNetherite(Integer meta, int count) { + super(NETHERITE_CHESTPLATE, meta, count, "Netherite Chestplate"); + } + + @Override + public boolean isChestplate() { + return true; + } + + @Override + public int getTier() { + return ItemArmor.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return 592; + } + + @Override + public int getArmorPoints() { + return 8; + } + + @Override + public int getToughness() { + return 2; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemCrossbow.java b/src/main/java/cn/nukkit/item/ItemCrossbow.java new file mode 100644 index 00000000000..6abd3526271 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemCrossbow.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +public class ItemCrossbow extends ItemTool { + + public ItemCrossbow() { + this(0, 1); + } + + public ItemCrossbow(Integer meta) { + this(meta, 1); + } + + public ItemCrossbow(Integer meta, int count) { + super(CROSSBOW, meta, count, "Crossbow"); + } + + @Override + public int getMaxDurability() { + return ItemTool.DURABILITY_CROSSBOW; + } + + @Override + public int getEnchantAbility() { + return 1; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemEdible.java b/src/main/java/cn/nukkit/item/ItemEdible.java index 26abaaae27e..fd59d149e6d 100644 --- a/src/main/java/cn/nukkit/item/ItemEdible.java +++ b/src/main/java/cn/nukkit/item/ItemEdible.java @@ -46,7 +46,7 @@ public boolean onUse(Player player, int ticksUsed) { } Food food = Food.getByRelative(this); - if (player.isSurvival() && food != null && food.eatenBy(player)) { + if ((player.isAdventure() || player.isSurvival()) && food != null && food.eatenBy(player)) { --this.count; player.getInventory().setItemInHand(this); } diff --git a/src/main/java/cn/nukkit/item/ItemEndCrystal.java b/src/main/java/cn/nukkit/item/ItemEndCrystal.java index 2bd2f3d55f2..47a717a8ff0 100644 --- a/src/main/java/cn/nukkit/item/ItemEndCrystal.java +++ b/src/main/java/cn/nukkit/item/ItemEndCrystal.java @@ -62,7 +62,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, Entity entity = Entity.createEntity("EndCrystal", chunk, nbt); if (entity != null) { - if (player.isSurvival()) { + if (player.isAdventure() || player.isSurvival()) { Item item = player.getInventory().getItemInHand(); item.setCount(item.getCount() - 1); player.getInventory().setItemInHand(item); diff --git a/src/main/java/cn/nukkit/item/ItemFireCharge.java b/src/main/java/cn/nukkit/item/ItemFireCharge.java index 7c529f2fc72..a06735bbe9f 100644 --- a/src/main/java/cn/nukkit/item/ItemFireCharge.java +++ b/src/main/java/cn/nukkit/item/ItemFireCharge.java @@ -33,6 +33,10 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { + if (player.isAdventure()) { + return false; + } + if (block.getId() == AIR && (target instanceof BlockSolid || target instanceof BlockSolidMeta)) { if (target.getId() == OBSIDIAN) { if (level.createPortal(target)) { diff --git a/src/main/java/cn/nukkit/item/ItemFirework.java b/src/main/java/cn/nukkit/item/ItemFirework.java index 69c1931e7f7..2bc7e4ca8f5 100644 --- a/src/main/java/cn/nukkit/item/ItemFirework.java +++ b/src/main/java/cn/nukkit/item/ItemFirework.java @@ -62,6 +62,10 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { + if (player.isAdventure()) { + return false; + } + if (block.canPassThrough()) { this.spawnFirework(level, block); diff --git a/src/main/java/cn/nukkit/item/ItemFlintSteel.java b/src/main/java/cn/nukkit/item/ItemFlintSteel.java index c6b3b957a7d..c214b903a2b 100644 --- a/src/main/java/cn/nukkit/item/ItemFlintSteel.java +++ b/src/main/java/cn/nukkit/item/ItemFlintSteel.java @@ -34,6 +34,10 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { + if (player.isAdventure()) { + return false; + } + if (block.getId() == AIR && (target instanceof BlockSolid || target instanceof BlockSolidMeta)) { if (target.getId() == OBSIDIAN) { if (level.createPortal(target)) { diff --git a/src/main/java/cn/nukkit/item/ItemHelmetNetherite.java b/src/main/java/cn/nukkit/item/ItemHelmetNetherite.java new file mode 100644 index 00000000000..4f6b45146fb --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemHelmetNetherite.java @@ -0,0 +1,41 @@ +package cn.nukkit.item; + +public class ItemHelmetNetherite extends ItemArmor { + + public ItemHelmetNetherite() { + this(0, 1); + } + + public ItemHelmetNetherite(Integer meta) { + this(meta, 1); + } + + public ItemHelmetNetherite(Integer meta, int count) { + super(NETHERITE_HELMET, meta, count, "Netherite Helmet"); + } + + @Override + public boolean isHelmet() { + return true; + } + + @Override + public int getTier() { + return ItemArmor.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return 407; + } + + @Override + public int getArmorPoints() { + return 3; + } + + @Override + public int getToughness() { + return 2; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemHoeNetherite.java b/src/main/java/cn/nukkit/item/ItemHoeNetherite.java new file mode 100644 index 00000000000..a808c6c80b5 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemHoeNetherite.java @@ -0,0 +1,36 @@ +package cn.nukkit.item; + +public class ItemHoeNetherite extends ItemTool { + + public ItemHoeNetherite() { + this(0, 1); + } + + public ItemHoeNetherite(Integer meta) { + this(meta, 1); + } + + public ItemHoeNetherite(Integer meta, int count) { + super(NETHERITE_HOE, meta, count, "Netherite Hoe"); + } + + @Override + public boolean isHoe() { + return true; + } + + @Override + public int getAttackDamage() { + return 6; + } + + @Override + public int getTier() { + return ItemTool.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return ItemTool.DURABILITY_NETHERITE; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemID.java b/src/main/java/cn/nukkit/item/ItemID.java index 53e0314ca71..f4e7b379cfe 100644 --- a/src/main/java/cn/nukkit/item/ItemID.java +++ b/src/main/java/cn/nukkit/item/ItemID.java @@ -229,6 +229,7 @@ public interface ItemID { int SCUTE = 468; int TURTLE_SHELL = 469; int PHANTOM_MEMBRANE = 470; + int CROSSBOW = 471; int SWEET_BERRIES = 477; @@ -251,6 +252,17 @@ public interface ItemID { int HONEY_BOTTLE = 737; int LODESTONECOMPASS = 741; + int NETHERITE_INGOT = 742; + int NETHERITE_SWORD = 743; + int NETHERITE_SHOVEL = 744; + int NETHERITE_PICKAXE = 745; + int NETHERITE_AXE = 746; + int NETHERITE_HOE = 747; + int NETHERITE_HELMET = 748; + int NETHERITE_CHESTPLATE = 749; + int NETHERITE_LEGGINGS = 750; + int NETHERITE_BOOTS = 751; + int NETHERITE_SCRAP = 752; int WARPED_FUNGUS_ON_A_STICK = 757; diff --git a/src/main/java/cn/nukkit/item/ItemIngotNetherite.java b/src/main/java/cn/nukkit/item/ItemIngotNetherite.java new file mode 100644 index 00000000000..e7bf8de4c70 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemIngotNetherite.java @@ -0,0 +1,16 @@ +package cn.nukkit.item; + +public class ItemIngotNetherite extends Item { + + public ItemIngotNetherite() { + this(0, 1); + } + + public ItemIngotNetherite(Integer meta) { + this(meta, 1); + } + + public ItemIngotNetherite(Integer meta, int count) { + super(NETHERITE_INGOT, 0, count, "Netherite Ingot"); + } +} diff --git a/src/main/java/cn/nukkit/item/ItemLeggingsNetherite.java b/src/main/java/cn/nukkit/item/ItemLeggingsNetherite.java new file mode 100644 index 00000000000..a7e0298fec9 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemLeggingsNetherite.java @@ -0,0 +1,41 @@ +package cn.nukkit.item; + +public class ItemLeggingsNetherite extends ItemArmor { + + public ItemLeggingsNetherite() { + this(0, 1); + } + + public ItemLeggingsNetherite(Integer meta) { + this(meta, 1); + } + + public ItemLeggingsNetherite(Integer meta, int count) { + super(NETHERITE_LEGGINGS, meta, count, "Netherite Leggings"); + } + + @Override + public boolean isLeggings() { + return true; + } + + @Override + public int getTier() { + return ItemArmor.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return 555; + } + + @Override + public int getArmorPoints() { + return 6; + } + + @Override + public int getToughness() { + return 2; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemMap.java b/src/main/java/cn/nukkit/item/ItemMap.java index f4d03343ba1..5ed327ea55b 100644 --- a/src/main/java/cn/nukkit/item/ItemMap.java +++ b/src/main/java/cn/nukkit/item/ItemMap.java @@ -32,7 +32,7 @@ public ItemMap(Integer meta) { } public ItemMap(Integer meta, int count) { - super(MAP, 0, count, "Map"); + super(MAP, meta, count, "Map"); if (!hasCompoundTag() || !getNamedTag().contains("map_uuid")) { CompoundTag tag = new CompoundTag(); diff --git a/src/main/java/cn/nukkit/item/ItemMinecart.java b/src/main/java/cn/nukkit/item/ItemMinecart.java index 122b59b516e..5a523cd88cd 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecart.java +++ b/src/main/java/cn/nukkit/item/ItemMinecart.java @@ -63,7 +63,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, return false; } - if (player.isSurvival()) { + if (player.isAdventure() || player.isSurvival()) { Item item = player.getInventory().getItemInHand(); item.setCount(item.getCount() - 1); player.getInventory().setItemInHand(item); diff --git a/src/main/java/cn/nukkit/item/ItemMinecartChest.java b/src/main/java/cn/nukkit/item/ItemMinecartChest.java index 37d2503e1d4..847a0feb6d6 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecartChest.java +++ b/src/main/java/cn/nukkit/item/ItemMinecartChest.java @@ -59,7 +59,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, return false; } - if (player.isSurvival()) { + if (player.isAdventure() || player.isSurvival()) { Item item = player.getInventory().getItemInHand(); item.setCount(item.getCount() - 1); player.getInventory().setItemInHand(item); diff --git a/src/main/java/cn/nukkit/item/ItemMinecartHopper.java b/src/main/java/cn/nukkit/item/ItemMinecartHopper.java index b7534975ab7..84c052b3376 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecartHopper.java +++ b/src/main/java/cn/nukkit/item/ItemMinecartHopper.java @@ -59,7 +59,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, return false; } - if (player.isSurvival()) { + if (player.isAdventure() || player.isSurvival()) { Item item = player.getInventory().getItemInHand(); item.setCount(item.getCount() - 1); player.getInventory().setItemInHand(item); diff --git a/src/main/java/cn/nukkit/item/ItemMinecartTNT.java b/src/main/java/cn/nukkit/item/ItemMinecartTNT.java index db854023e4f..20f0e6eb2fe 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecartTNT.java +++ b/src/main/java/cn/nukkit/item/ItemMinecartTNT.java @@ -59,7 +59,7 @@ public boolean onActivate(Level level, Player player, Block block, Block target, return false; } - if (player.isSurvival()) { + if (player.isAdventure() || player.isSurvival()) { Item item = player.getInventory().getItemInHand(); item.setCount(item.getCount() - 1); player.getInventory().setItemInHand(item); diff --git a/src/main/java/cn/nukkit/item/ItemNameTag.java b/src/main/java/cn/nukkit/item/ItemNameTag.java new file mode 100644 index 00000000000..ef898d71a5c --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemNameTag.java @@ -0,0 +1,17 @@ +package cn.nukkit.item; + +public class ItemNameTag extends Item { + + public ItemNameTag() { + this(0, 1); + } + + public ItemNameTag(Integer meta) { + this(meta, 1); + } + + public ItemNameTag(Integer meta, int count) { + super(NAME_TAG, meta, count, "Name Tag"); + } + +} diff --git a/src/main/java/cn/nukkit/item/ItemPainting.java b/src/main/java/cn/nukkit/item/ItemPainting.java index 550b95e7fb8..3419bfc2086 100644 --- a/src/main/java/cn/nukkit/item/ItemPainting.java +++ b/src/main/java/cn/nukkit/item/ItemPainting.java @@ -45,6 +45,10 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { + if (player.isAdventure()) { + return false; + } + FullChunk chunk = level.getChunk((int) block.getX() >> 4, (int) block.getZ() >> 4); if (chunk == null || target.isTransparent() || face.getHorizontalIndex() == -1 || block.isSolid()) { diff --git a/src/main/java/cn/nukkit/item/ItemPickaxeNetherite.java b/src/main/java/cn/nukkit/item/ItemPickaxeNetherite.java new file mode 100644 index 00000000000..7c58e92b989 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemPickaxeNetherite.java @@ -0,0 +1,36 @@ +package cn.nukkit.item; + +public class ItemPickaxeNetherite extends ItemTool { + + public ItemPickaxeNetherite() { + this(0, 1); + } + + public ItemPickaxeNetherite(Integer meta) { + this(meta, 1); + } + + public ItemPickaxeNetherite(Integer meta, int count) { + super(NETHERITE_PICKAXE, meta, count, "Netherite Pickaxe"); + } + + @Override + public boolean isPickaxe() { + return true; + } + + @Override + public int getAttackDamage() { + return 6; + } + + @Override + public int getTier() { + return ItemTool.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return ItemTool.DURABILITY_NETHERITE; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemPotion.java b/src/main/java/cn/nukkit/item/ItemPotion.java index debcae3fe5a..2ad50c51983 100644 --- a/src/main/java/cn/nukkit/item/ItemPotion.java +++ b/src/main/java/cn/nukkit/item/ItemPotion.java @@ -78,7 +78,7 @@ public boolean onUse(Player player, int ticksUsed) { } Potion potion = Potion.getPotion(this.getDamage()).setSplash(false); - if (player.getGamemode() == SURVIVAL) { + if (player.isAdventure() || player.isSurvival()) { --this.count; player.getInventory().setItemInHand(this); player.getInventory().addItem(new ItemGlassBottle()); diff --git a/src/main/java/cn/nukkit/item/ItemScrapNetherite.java b/src/main/java/cn/nukkit/item/ItemScrapNetherite.java new file mode 100644 index 00000000000..3f5426fc0b0 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemScrapNetherite.java @@ -0,0 +1,16 @@ +package cn.nukkit.item; + +public class ItemScrapNetherite extends Item { + + public ItemScrapNetherite() { + this(0, 1); + } + + public ItemScrapNetherite(Integer meta) { + this(meta, 1); + } + + public ItemScrapNetherite(Integer meta, int count) { + super(NETHERITE_SCRAP, 0, count, "Netherite Scrap"); + } +} diff --git a/src/main/java/cn/nukkit/item/ItemShovelNetherite.java b/src/main/java/cn/nukkit/item/ItemShovelNetherite.java new file mode 100644 index 00000000000..99e5dd766af --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemShovelNetherite.java @@ -0,0 +1,36 @@ +package cn.nukkit.item; + +public class ItemShovelNetherite extends ItemTool { + + public ItemShovelNetherite() { + this(0, 1); + } + + public ItemShovelNetherite(Integer meta) { + this(meta, 1); + } + + public ItemShovelNetherite(Integer meta, int count) { + super(NETHERITE_SHOVEL, meta, count, "Netherite Shovel"); + } + + @Override + public boolean isShovel() { + return true; + } + + @Override + public int getAttackDamage() { + return 5; + } + + @Override + public int getTier() { + return ItemTool.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return ItemTool.DURABILITY_NETHERITE; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemSpawnEgg.java b/src/main/java/cn/nukkit/item/ItemSpawnEgg.java index a9d34e79369..3afd9eb6440 100644 --- a/src/main/java/cn/nukkit/item/ItemSpawnEgg.java +++ b/src/main/java/cn/nukkit/item/ItemSpawnEgg.java @@ -40,6 +40,10 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { + if (player.isAdventure()) { + return false; + } + FullChunk chunk = level.getChunk((int) block.getX() >> 4, (int) block.getZ() >> 4); if (chunk == null) { diff --git a/src/main/java/cn/nukkit/item/ItemSwordNetherite.java b/src/main/java/cn/nukkit/item/ItemSwordNetherite.java new file mode 100644 index 00000000000..b1c6c21266a --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemSwordNetherite.java @@ -0,0 +1,36 @@ +package cn.nukkit.item; + +public class ItemSwordNetherite extends ItemTool { + + public ItemSwordNetherite() { + this(0, 1); + } + + public ItemSwordNetherite(Integer meta) { + this(meta, 1); + } + + public ItemSwordNetherite(Integer meta, int count) { + super(NETHERITE_SWORD, meta, count, "Netherite Sword"); + } + + @Override + public boolean isSword() { + return true; + } + + @Override + public int getAttackDamage() { + return 8; + } + + @Override + public int getTier() { + return ItemTool.TIER_NETHERITE; + } + + @Override + public int getMaxDurability() { + return ItemTool.DURABILITY_NETHERITE; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemTool.java b/src/main/java/cn/nukkit/item/ItemTool.java index 072576ff02f..cad0e61332d 100644 --- a/src/main/java/cn/nukkit/item/ItemTool.java +++ b/src/main/java/cn/nukkit/item/ItemTool.java @@ -18,6 +18,7 @@ public abstract class ItemTool extends Item implements ItemDurable { public static final int TIER_STONE = 3; public static final int TIER_IRON = 4; public static final int TIER_DIAMOND = 5; + public static final int TIER_NETHERITE = 6; public static final int TYPE_NONE = 0; public static final int TYPE_SWORD = 1; @@ -25,17 +26,20 @@ public abstract class ItemTool extends Item implements ItemDurable { public static final int TYPE_PICKAXE = 3; public static final int TYPE_AXE = 4; public static final int TYPE_SHEARS = 5; + public static final int TYPE_HOE = 6; public static final int DURABILITY_WOODEN = 60; public static final int DURABILITY_GOLD = 33; public static final int DURABILITY_STONE = 132; public static final int DURABILITY_IRON = 251; public static final int DURABILITY_DIAMOND = 1562; + public static final int DURABILITY_NETHERITE = 2032; public static final int DURABILITY_FLINT_STEEL = 65; public static final int DURABILITY_SHEARS = 239; public static final int DURABILITY_BOW = 385; public static final int DURABILITY_TRIDENT = 251; public static final int DURABILITY_FISHING_ROD = 65; + public static final int DURABILITY_CROSSBOW = 465; public ItemTool(int id) { this(id, 0, 1, UNKNOWN_STR); @@ -67,8 +71,9 @@ public boolean useOn(Block block) { if (block.getToolType() == ItemTool.TYPE_PICKAXE && this.isPickaxe() || block.getToolType() == ItemTool.TYPE_SHOVEL && this.isShovel() || block.getToolType() == ItemTool.TYPE_AXE && this.isAxe() || + block.getToolType() == ItemTool.TYPE_HOE && this.isHoe() || block.getToolType() == ItemTool.TYPE_SWORD && this.isSword() || - block.getToolType() == ItemTool.SHEARS && this.isShears() + block.getToolType() == ItemTool.TYPE_SHEARS && this.isShears() ) { this.meta++; } else if (!this.isShears() && block.getBreakTime(this) > 0) { @@ -145,7 +150,7 @@ public boolean isShears() { @Override public boolean isTool() { - return (this.id == FLINT_STEEL || this.id == SHEARS || this.id == BOW || this.isPickaxe() || this.isAxe() || this.isShovel() || this.isSword() || this.isHoe()); + return (this.id == FLINT_STEEL || this.id == SHEARS || this.id == BOW || this.id == CROSSBOW || this.isPickaxe() || this.isAxe() || this.isShovel() || this.isSword() || this.isHoe()); } @Override diff --git a/src/main/java/cn/nukkit/item/RuntimeItemMapping.java b/src/main/java/cn/nukkit/item/RuntimeItemMapping.java new file mode 100644 index 00000000000..20ca3fd00fa --- /dev/null +++ b/src/main/java/cn/nukkit/item/RuntimeItemMapping.java @@ -0,0 +1,43 @@ +package cn.nukkit.item; + +import it.unimi.dsi.fastutil.ints.Int2IntMap; + +public class RuntimeItemMapping { + + private final Int2IntMap legacyNetworkMap; + private final Int2IntMap networkLegacyMap; + private final byte[] itemDataPalette; + + public RuntimeItemMapping(byte[] itemDataPalette, Int2IntMap legacyNetworkMap, Int2IntMap networkLegacyMap) { + this.itemDataPalette = itemDataPalette; + this.legacyNetworkMap = legacyNetworkMap; + this.networkLegacyMap = networkLegacyMap; + this.legacyNetworkMap.defaultReturnValue(-1); + this.networkLegacyMap.defaultReturnValue(-1); + } + + public int getNetworkFullId(Item item) { + int fullId = RuntimeItems.getFullId(item.getId(), item.hasMeta() ? item.getDamage() : -1); + int networkId = this.legacyNetworkMap.get(fullId); + if (networkId == -1) { + networkId = this.legacyNetworkMap.get(RuntimeItems.getFullId(item.getId(), 0)); + } + if (networkId == -1) { + throw new IllegalArgumentException("Unknown item mapping " + item); + } + + return networkId; + } + + public int getLegacyFullId(int networkId) { + int fullId = networkLegacyMap.get(networkId); + if (fullId == -1) { + throw new IllegalArgumentException("Unknown network mapping: " + networkId); + } + return fullId; + } + + public byte[] getItemDataPalette() { + return this.itemDataPalette; + } +} diff --git a/src/main/java/cn/nukkit/item/RuntimeItems.java b/src/main/java/cn/nukkit/item/RuntimeItems.java new file mode 100644 index 00000000000..3aca8e84078 --- /dev/null +++ b/src/main/java/cn/nukkit/item/RuntimeItems.java @@ -0,0 +1,91 @@ +package cn.nukkit.item; + +import cn.nukkit.Server; +import cn.nukkit.utils.BinaryStream; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.experimental.UtilityClass; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; + +@UtilityClass +public class RuntimeItems { + + private static final Gson GSON = new Gson(); + private static final Type ENTRY_TYPE = new TypeToken>(){}.getType(); + + private static final RuntimeItemMapping itemPalette; + + static { + Server.getInstance().getLogger().debug("Loading runtime items..."); + InputStream stream = Server.class.getClassLoader().getResourceAsStream("runtime_item_ids.json"); + if (stream == null) { + throw new AssertionError("Unable to load runtime_item_ids.json"); + } + + InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); + Collection entries = GSON.fromJson(reader, ENTRY_TYPE); + + BinaryStream paletteBuffer = new BinaryStream(); + paletteBuffer.putUnsignedVarInt(entries.size()); + + Int2IntMap legacyNetworkMap = new Int2IntOpenHashMap(); + Int2IntMap networkLegacyMap = new Int2IntOpenHashMap(); + for (Entry entry : entries) { + paletteBuffer.putString(entry.name); + paletteBuffer.putLShort(entry.id); + paletteBuffer.putBoolean(false); // Component item + if (entry.oldId != null) { + boolean hasData = entry.oldData != null; + int fullId = getFullId(entry.oldId, hasData ? entry.oldData : 0); + legacyNetworkMap.put(fullId, (entry.id << 1) | (hasData ? 1 : 0)); + networkLegacyMap.put(entry.id, fullId | (hasData ? 1 : 0)); + } + } + + byte[] itemDataPalette = paletteBuffer.getBuffer(); + itemPalette = new RuntimeItemMapping(itemDataPalette, legacyNetworkMap, networkLegacyMap); + } + + public static RuntimeItemMapping getRuntimeMapping() { + return itemPalette; + } + + public static int getId(int fullId) { + return (short) (fullId >> 16); + } + + public static int getData(int fullId) { + return ((fullId >> 1) & 0x7fff); + } + + public static int getFullId(int id, int data) { + return (((short) id) << 16) | ((data & 0x7fff) << 1); + } + + public static int getNetworkId(int networkFullId) { + return networkFullId >> 1; + } + + public static boolean hasData(int id) { + return (id & 0x1) != 0; + } + + @ToString + @RequiredArgsConstructor + static class Entry { + String name; + int id; + Integer oldId; + Integer oldData; + } +} diff --git a/src/main/java/cn/nukkit/item/enchantment/Enchantment.java b/src/main/java/cn/nukkit/item/enchantment/Enchantment.java index 8d2e9b6bba0..bcfc27b492b 100644 --- a/src/main/java/cn/nukkit/item/enchantment/Enchantment.java +++ b/src/main/java/cn/nukkit/item/enchantment/Enchantment.java @@ -7,6 +7,9 @@ import cn.nukkit.item.enchantment.bow.EnchantmentBowInfinity; import cn.nukkit.item.enchantment.bow.EnchantmentBowKnockback; import cn.nukkit.item.enchantment.bow.EnchantmentBowPower; +import cn.nukkit.item.enchantment.crossbow.EnchantmentCrossbowMultishot; +import cn.nukkit.item.enchantment.crossbow.EnchantmentCrossbowPiercing; +import cn.nukkit.item.enchantment.crossbow.EnchantmentCrossbowQuickCharge; import cn.nukkit.item.enchantment.damage.EnchantmentDamageAll; import cn.nukkit.item.enchantment.damage.EnchantmentDamageArthropods; import cn.nukkit.item.enchantment.damage.EnchantmentDamageSmite; @@ -67,6 +70,10 @@ public abstract class Enchantment implements Cloneable { public static final int ID_TRIDENT_RIPTIDE = 30; public static final int ID_TRIDENT_LOYALTY = 31; public static final int ID_TRIDENT_CHANNELING = 32; + public static final int ID_CROSSBOW_MULTISHOT = 33; + public static final int ID_CROSSBOW_PIERCING = 34; + public static final int ID_CROSSBOW_QUICK_CHARGE = 35; + public static final int ID_SOUL_SPEED = 36; public static void init() { enchantments = new Enchantment[256]; @@ -104,6 +111,10 @@ public static void init() { enchantments[ID_TRIDENT_RIPTIDE] = new EnchantmentTridentRiptide(); enchantments[ID_TRIDENT_LOYALTY] = new EnchantmentTridentLoyalty(); enchantments[ID_TRIDENT_CHANNELING] = new EnchantmentTridentChanneling(); + enchantments[ID_CROSSBOW_MULTISHOT] = new EnchantmentCrossbowMultishot(); + enchantments[ID_CROSSBOW_PIERCING] = new EnchantmentCrossbowPiercing(); + enchantments[ID_CROSSBOW_QUICK_CHARGE] = new EnchantmentCrossbowQuickCharge(); + enchantments[ID_SOUL_SPEED] = new EnchantmentSoulSpeed(); } public static Enchantment get(int id) { @@ -135,16 +146,16 @@ public static Enchantment[] getEnchantments() { } public final int id; - private final int weight; + private final Rarity rarity; public EnchantmentType type; protected int level = 1; protected final String name; - protected Enchantment(int id, String name, int weight, EnchantmentType type) { + protected Enchantment(int id, String name, Rarity rarity, EnchantmentType type) { this.id = id; - this.weight = weight; + this.rarity = rarity; this.type = type; this.name = name; @@ -173,8 +184,16 @@ public int getId() { return id; } + public Rarity getRarity() { + return this.rarity; + } + + /** + * @deprecated use {@link Rarity#getWeight()} instead + */ + @Deprecated public int getWeight() { - return weight; + return this.rarity.getWeight(); } public int getMinLevel() { @@ -213,7 +232,11 @@ public void doPostHurt(Entity attacker, Entity entity) { } - public boolean isCompatibleWith(Enchantment enchantment) { + public final boolean isCompatibleWith(Enchantment enchantment) { + return this.checkCompatibility(enchantment) && enchantment.checkCompatibility(this); + } + + protected boolean checkCompatibility(Enchantment enchantment) { return this != enchantment; } @@ -254,7 +277,35 @@ public static String getRandomName() { private static class UnknownEnchantment extends Enchantment { protected UnknownEnchantment(int id) { - super(id, "unknown", 0, EnchantmentType.ALL); + super(id, "unknown", Rarity.VERY_RARE, EnchantmentType.ALL); + } + } + + public enum Rarity { + COMMON(10), + UNCOMMON(5), + RARE(2), + VERY_RARE(1); + + private final int weight; + + Rarity(int weight) { + this.weight = weight; + } + + public int getWeight() { + return this.weight; + } + + public static Rarity fromWeight(int weight) { + if (weight < 2) { + return VERY_RARE; + } else if (weight < 5) { + return RARE; + } else if (weight < 10) { + return UNCOMMON; + } + return COMMON; } } } diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentBindingCurse.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentBindingCurse.java index 6ede1d48b6d..7d511415002 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentBindingCurse.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentBindingCurse.java @@ -2,7 +2,7 @@ public class EnchantmentBindingCurse extends Enchantment { protected EnchantmentBindingCurse() { - super(ID_BINDING_CURSE, "bindingCurse", 1, EnchantmentType.WEARABLE); + super(ID_BINDING_CURSE, "curse.binding", Rarity.VERY_RARE, EnchantmentType.WEARABLE); } @Override @@ -12,11 +12,6 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return 50; - } - - @Override - public int getMaxLevel() { - return 1; + return 30; } } diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentDurability.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentDurability.java index eb6ed37b3ef..8cd4140069a 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentDurability.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentDurability.java @@ -10,7 +10,7 @@ */ public class EnchantmentDurability extends Enchantment { protected EnchantmentDurability() { - super(ID_DURABILITY, "durability", 5, EnchantmentType.BREAKABLE); + super(ID_DURABILITY, "durability", Rarity.UNCOMMON, EnchantmentType.BREAKABLE); } @Override @@ -20,7 +20,7 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return super.getMinEnchantAbility(level) + 50; } @Override @@ -29,8 +29,8 @@ public int getMaxLevel() { } @Override - public boolean isCompatibleWith(Enchantment enchantment) { - return super.isCompatibleWith(enchantment) && enchantment.id != ID_FORTUNE_DIGGING; + public boolean checkCompatibility(Enchantment enchantment) { + return super.checkCompatibility(enchantment) && enchantment.id != ID_FORTUNE_DIGGING; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentEfficiency.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentEfficiency.java index 9274f92509c..35581c0cf3f 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentEfficiency.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentEfficiency.java @@ -8,7 +8,7 @@ */ public class EnchantmentEfficiency extends Enchantment { protected EnchantmentEfficiency() { - super(ID_EFFICIENCY, "digging", 10, EnchantmentType.DIGGER); + super(ID_EFFICIENCY, "digging", Rarity.COMMON, EnchantmentType.DIGGER); } @Override @@ -18,7 +18,7 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return super.getMinEnchantAbility(level) + 50; } @Override @@ -30,5 +30,4 @@ public int getMaxLevel() { public boolean canEnchant(Item item) { return item.isShears() || super.canEnchant(item); } - } diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentFireAspect.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentFireAspect.java index 55f831d2536..2b72e7f91d6 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentFireAspect.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentFireAspect.java @@ -9,7 +9,7 @@ */ public class EnchantmentFireAspect extends Enchantment { protected EnchantmentFireAspect() { - super(ID_FIRE_ASPECT, "fire", 2, EnchantmentType.SWORD); + super(ID_FIRE_ASPECT, "fire", Rarity.RARE, EnchantmentType.SWORD); } @Override @@ -19,7 +19,7 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return super.getMinEnchantAbility(level) + 50; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentFrostWalker.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentFrostWalker.java index ab0a22848ca..7f75d0e2d35 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentFrostWalker.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentFrostWalker.java @@ -2,12 +2,7 @@ public class EnchantmentFrostWalker extends Enchantment { protected EnchantmentFrostWalker() { - super(ID_FROST_WALKER, "frostWalker", 2, EnchantmentType.ARMOR_FEET); - } - - @Override - public int getMinEnchantAbility(int level) { - return level * 10; + super(ID_FROST_WALKER, "frostwalker", Rarity.RARE, EnchantmentType.ARMOR_FEET); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentKnockback.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentKnockback.java index 99fd7523f54..e920eb91f67 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentKnockback.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentKnockback.java @@ -6,7 +6,7 @@ */ public class EnchantmentKnockback extends Enchantment { protected EnchantmentKnockback() { - super(ID_KNOCKBACK, "knockback", 5, EnchantmentType.SWORD); + super(ID_KNOCKBACK, "knockback", Rarity.UNCOMMON, EnchantmentType.SWORD); } @Override @@ -16,7 +16,7 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return super.getMinEnchantAbility(level) + 50; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentLure.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentLure.java index fd74ac0cc67..d8890a6e053 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentLure.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentLure.java @@ -6,22 +6,16 @@ */ public class EnchantmentLure extends Enchantment { protected EnchantmentLure() { - super(ID_LURE, "fishingSpeed", 2, EnchantmentType.FISHING_ROD); + super(ID_LURE, "fishingSpeed", Rarity.RARE, EnchantmentType.FISHING_ROD); } @Override public int getMinEnchantAbility(int level) { - return 15 + (level - 1) * 9; - } - - @Override - public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return level + 8 * level + 6; } @Override public int getMaxLevel() { return 3; } - } diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentMending.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentMending.java index acc024e6673..2b9e9fc3b48 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentMending.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentMending.java @@ -5,13 +5,12 @@ */ public class EnchantmentMending extends Enchantment { protected EnchantmentMending() { - - super(ID_MENDING, "mending", 2, EnchantmentType.ALL); + super(ID_MENDING, "mending", Rarity.RARE, EnchantmentType.BREAKABLE); } @Override public int getMinEnchantAbility(int level) { - return 25 + (level - 1) * 9; + return 25 * level; } @Override @@ -20,10 +19,7 @@ public int getMaxEnchantAbility(int level) { } @Override - public int getMaxLevel() { return 1; } - - @Override - public boolean isCompatibleWith(Enchantment enchantment) { - return super.isCompatibleWith(enchantment) && enchantment.id != ID_BOW_INFINITY; + public boolean checkCompatibility(Enchantment enchantment) { + return super.checkCompatibility(enchantment) && enchantment.id != ID_BOW_INFINITY; } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentSilkTouch.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentSilkTouch.java index 481f8c16ccd..550dea32eba 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentSilkTouch.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentSilkTouch.java @@ -8,7 +8,7 @@ */ public class EnchantmentSilkTouch extends Enchantment { protected EnchantmentSilkTouch() { - super(ID_SILK_TOUCH, "untouching", 1, EnchantmentType.DIGGER); + super(ID_SILK_TOUCH, "untouching", Rarity.VERY_RARE, EnchantmentType.DIGGER); } @Override @@ -18,17 +18,12 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return super.getMinEnchantAbility(level) + 50; } @Override - public int getMaxLevel() { - return 1; - } - - @Override - public boolean isCompatibleWith(Enchantment enchantment) { - return super.isCompatibleWith(enchantment) && enchantment.id != ID_FORTUNE_DIGGING; + public boolean checkCompatibility(Enchantment enchantment) { + return super.checkCompatibility(enchantment) && enchantment.id != ID_FORTUNE_DIGGING; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentSoulSpeed.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentSoulSpeed.java new file mode 100644 index 00000000000..e5d8e1356b2 --- /dev/null +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentSoulSpeed.java @@ -0,0 +1,18 @@ +package cn.nukkit.item.enchantment; + +public class EnchantmentSoulSpeed extends Enchantment { + + protected EnchantmentSoulSpeed() { + super(ID_SOUL_SPEED, "soul_speed", Rarity.VERY_RARE, EnchantmentType.ARMOR_FEET); + } + + @Override + public int getMinEnchantAbility(int level) { + return 10 * level; + } + + @Override + public int getMaxLevel() { + return 3; + } +} diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentThorns.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentThorns.java index 34ce2926557..cf85ed2cb97 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentThorns.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentThorns.java @@ -14,7 +14,7 @@ */ public class EnchantmentThorns extends Enchantment { protected EnchantmentThorns() { - super(ID_THORNS, "thorns", 2, EnchantmentType.ARMOR); + super(ID_THORNS, "thorns", Rarity.RARE, EnchantmentType.ARMOR); } @Override @@ -24,7 +24,7 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return super.getMinEnchantAbility(level) + 50; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentType.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentType.java index 37aa44fbd3c..cf5eeee9477 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentType.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentType.java @@ -1,10 +1,9 @@ package cn.nukkit.item.enchantment; -import cn.nukkit.block.BlockPumpkin; import cn.nukkit.item.Item; import cn.nukkit.item.ItemArmor; import cn.nukkit.item.ItemBow; -import cn.nukkit.item.ItemElytra; +import cn.nukkit.item.ItemCrossbow; import cn.nukkit.item.ItemFishingRod; import cn.nukkit.item.ItemSkull; import cn.nukkit.item.ItemTrident; @@ -26,7 +25,8 @@ public enum EnchantmentType { BREAKABLE, BOW, WEARABLE, - TRIDENT; + TRIDENT, + CROSSBOW; public boolean canEnchantItem(Item item) { if (this == ALL) { @@ -36,7 +36,7 @@ public boolean canEnchantItem(Item item) { return true; } else if (item instanceof ItemArmor) { - if (this == ARMOR) { + if (this == ARMOR || this == WEARABLE) { return true; } @@ -58,15 +58,17 @@ public boolean canEnchantItem(Item item) { case SWORD: return item.isSword(); case DIGGER: - return item.isPickaxe() || item.isShovel() || item.isAxe(); + return item.isPickaxe() || item.isShovel() || item.isAxe() || item.isHoe(); case BOW: return item instanceof ItemBow; case FISHING_ROD: return item instanceof ItemFishingRod; case WEARABLE: - return item instanceof ItemArmor || item instanceof ItemElytra || item instanceof ItemSkull || item.getBlock() instanceof BlockPumpkin; + return item instanceof ItemSkull; case TRIDENT: return item instanceof ItemTrident; + case CROSSBOW: + return item instanceof ItemCrossbow; default: return false; } diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentVanishingCurse.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentVanishingCurse.java index ec8d8aca14e..928398ee5fb 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentVanishingCurse.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentVanishingCurse.java @@ -1,22 +1,14 @@ package cn.nukkit.item.enchantment; +import cn.nukkit.item.Item; + public class EnchantmentVanishingCurse extends Enchantment { protected EnchantmentVanishingCurse() { - super(ID_VANISHING_CURSE, "vanishingCurse", 1, EnchantmentType.ALL); - } - - @Override - public int getMinEnchantAbility(int level) { - return 25; - } - - @Override - public int getMaxEnchantAbility(int level) { - return 50; + super(ID_VANISHING_CURSE, "curse.vanishing", Rarity.VERY_RARE, EnchantmentType.BREAKABLE); } @Override - public int getMaxLevel() { - return 1; + public boolean canEnchant(Item item) { + return item.getId() == Item.SKULL || item.getId() == Item.COMPASS || super.canEnchant(item); } } diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterBreath.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterBreath.java index 2374fa718a1..694b59ee54d 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterBreath.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterBreath.java @@ -6,17 +6,17 @@ */ public class EnchantmentWaterBreath extends Enchantment { protected EnchantmentWaterBreath() { - super(ID_WATER_BREATHING, "oxygen", 2, EnchantmentType.ARMOR_HEAD); + super(ID_WATER_BREATHING, "oxygen", Rarity.RARE, EnchantmentType.ARMOR_HEAD); } @Override public int getMinEnchantAbility(int level) { - return 10 + (level - 1) * 20; + return 10 * level; } @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return this.getMinEnchantAbility(level) + 30; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWalker.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWalker.java index a17e7f9548b..4a6afdba2fd 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWalker.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWalker.java @@ -6,7 +6,7 @@ */ public class EnchantmentWaterWalker extends Enchantment { protected EnchantmentWaterWalker() { - super(ID_WATER_WALKER, "waterWalker", 2, EnchantmentType.ARMOR_FEET); + super(ID_WATER_WALKER, "waterWalker", Rarity.RARE, EnchantmentType.ARMOR_FEET); } @Override @@ -16,7 +16,7 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 15; + return this.getMinEnchantAbility(level) + 10; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWorker.java b/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWorker.java index 364fb2dcc67..baa9e4542c0 100644 --- a/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWorker.java +++ b/src/main/java/cn/nukkit/item/enchantment/EnchantmentWaterWorker.java @@ -6,7 +6,7 @@ */ public class EnchantmentWaterWorker extends Enchantment { protected EnchantmentWaterWorker() { - super(ID_WATER_WORKER, "waterWorker", 2, EnchantmentType.ARMOR_HEAD); + super(ID_WATER_WORKER, "waterWorker", Rarity.RARE, EnchantmentType.ARMOR_HEAD); } @Override @@ -18,9 +18,4 @@ public int getMinEnchantAbility(int level) { public int getMaxEnchantAbility(int level) { return this.getMinEnchantAbility(level) + 40; } - - @Override - public int getMaxLevel() { - return 1; - } } diff --git a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBow.java b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBow.java index 15cccf0ed6a..062eaf35df2 100644 --- a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBow.java +++ b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBow.java @@ -8,8 +8,7 @@ * Nukkit Project */ public abstract class EnchantmentBow extends Enchantment { - protected EnchantmentBow(int id, String name, int weight) { - super(id, name, weight, EnchantmentType.BOW); + protected EnchantmentBow(int id, String name, Rarity rarity) { + super(id, name, rarity, EnchantmentType.BOW); } - } diff --git a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowFlame.java b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowFlame.java index 904795064d7..62b9f48d54a 100644 --- a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowFlame.java +++ b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowFlame.java @@ -8,7 +8,7 @@ */ public class EnchantmentBowFlame extends EnchantmentBow { public EnchantmentBowFlame() { - super(Enchantment.ID_BOW_FLAME, "arrowFire", 2); + super(Enchantment.ID_BOW_FLAME, "arrowFire", Rarity.RARE); } @Override @@ -20,9 +20,4 @@ public int getMinEnchantAbility(int level) { public int getMaxEnchantAbility(int level) { return 50; } - - @Override - public int getMaxLevel() { - return 1; - } } diff --git a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowInfinity.java b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowInfinity.java index 53e2e0597b6..f630c80af95 100644 --- a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowInfinity.java +++ b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowInfinity.java @@ -8,7 +8,7 @@ */ public class EnchantmentBowInfinity extends EnchantmentBow { public EnchantmentBowInfinity() { - super(Enchantment.ID_BOW_INFINITY, "arrowInfinite", 1); + super(Enchantment.ID_BOW_INFINITY, "arrowInfinite", Rarity.VERY_RARE); } @Override @@ -20,9 +20,4 @@ public int getMinEnchantAbility(int level) { public int getMaxEnchantAbility(int level) { return 50; } - - @Override - public int getMaxLevel() { - return 1; - } } diff --git a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowKnockback.java b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowKnockback.java index 9ef2359788a..457e14715f3 100644 --- a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowKnockback.java +++ b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowKnockback.java @@ -8,7 +8,7 @@ */ public class EnchantmentBowKnockback extends EnchantmentBow { public EnchantmentBowKnockback() { - super(Enchantment.ID_BOW_KNOCKBACK, "arrowKnockback", 2); + super(Enchantment.ID_BOW_KNOCKBACK, "arrowKnockback", Rarity.RARE); } @Override @@ -18,7 +18,7 @@ public int getMinEnchantAbility(int level) { @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; + return this.getMinEnchantAbility(level) + 25; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowPower.java b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowPower.java index cb15c3a5863..77b25b7172b 100644 --- a/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowPower.java +++ b/src/main/java/cn/nukkit/item/enchantment/bow/EnchantmentBowPower.java @@ -8,12 +8,12 @@ */ public class EnchantmentBowPower extends EnchantmentBow { public EnchantmentBowPower() { - super(Enchantment.ID_BOW_POWER, "arrowDamage", 10); + super(Enchantment.ID_BOW_POWER, "arrowDamage", Rarity.COMMON); } @Override public int getMinEnchantAbility(int level) { - return 1 + (level - 1) * 20; + return 1 + (level - 1) * 10; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbow.java b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbow.java new file mode 100644 index 00000000000..9afc026ef60 --- /dev/null +++ b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbow.java @@ -0,0 +1,11 @@ +package cn.nukkit.item.enchantment.crossbow; + +import cn.nukkit.item.enchantment.Enchantment; +import cn.nukkit.item.enchantment.EnchantmentType; + +public abstract class EnchantmentCrossbow extends Enchantment { + + protected EnchantmentCrossbow(int id, String name, Rarity rarity) { + super(id, name, rarity, EnchantmentType.CROSSBOW); + } +} diff --git a/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowMultishot.java b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowMultishot.java new file mode 100644 index 00000000000..939c8f4caf3 --- /dev/null +++ b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowMultishot.java @@ -0,0 +1,30 @@ +package cn.nukkit.item.enchantment.crossbow; + +import cn.nukkit.item.enchantment.Enchantment; + +public class EnchantmentCrossbowMultishot extends EnchantmentCrossbow { + + public EnchantmentCrossbowMultishot() { + super(Enchantment.ID_CROSSBOW_MULTISHOT, "crossbowMultishot", Rarity.RARE); + } + + @Override + public int getMinEnchantAbility(int level) { + return 20; + } + + @Override + public int getMaxEnchantAbility(int level) { + return 50 + this.getMinEnchantAbility(level); + } + + @Override + public int getMaxLevel() { + return 1; + } + + @Override + public boolean checkCompatibility(Enchantment enchantment) { + return super.checkCompatibility(enchantment) && enchantment.id != ID_CROSSBOW_PIERCING; + } +} diff --git a/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowPiercing.java b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowPiercing.java new file mode 100644 index 00000000000..83853f3f5a3 --- /dev/null +++ b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowPiercing.java @@ -0,0 +1,30 @@ +package cn.nukkit.item.enchantment.crossbow; + +import cn.nukkit.item.enchantment.Enchantment; + +public class EnchantmentCrossbowPiercing extends EnchantmentCrossbow { + + public EnchantmentCrossbowPiercing() { + super(Enchantment.ID_CROSSBOW_PIERCING, "crossbowPiercing", Rarity.COMMON); + } + + @Override + public int getMinEnchantAbility(int level) { + return 1 + 10 * (level - 1); + } + + @Override + public int getMaxEnchantAbility(int level) { + return 50 + this.getMinEnchantAbility(level); + } + + @Override + public int getMaxLevel() { + return 4; + } + + @Override + public boolean checkCompatibility(Enchantment enchantment) { + return super.checkCompatibility(enchantment) && enchantment.id != ID_CROSSBOW_MULTISHOT; + } +} diff --git a/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowQuickCharge.java b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowQuickCharge.java new file mode 100644 index 00000000000..c0ff904d2b3 --- /dev/null +++ b/src/main/java/cn/nukkit/item/enchantment/crossbow/EnchantmentCrossbowQuickCharge.java @@ -0,0 +1,25 @@ +package cn.nukkit.item.enchantment.crossbow; + +import cn.nukkit.item.enchantment.Enchantment; + +public class EnchantmentCrossbowQuickCharge extends EnchantmentCrossbow { + + public EnchantmentCrossbowQuickCharge() { + super(Enchantment.ID_CROSSBOW_QUICK_CHARGE, "crossbowQuickCharge", Rarity.UNCOMMON); + } + + @Override + public int getMinEnchantAbility(int level) { + return 12 + 20 * (level - 1); + } + + @Override + public int getMaxEnchantAbility(int level) { + return 50 + this.getMinEnchantAbility(level); + } + + @Override + public int getMaxLevel() { + return 3; + } +} diff --git a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamage.java b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamage.java index b5e965a1ed3..a2a1e5a612b 100644 --- a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamage.java +++ b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamage.java @@ -18,13 +18,13 @@ public enum TYPE { protected final TYPE damageType; - protected EnchantmentDamage(int id, String name, int weight, TYPE type) { - super(id, name, weight, EnchantmentType.SWORD); + protected EnchantmentDamage(int id, String name, Rarity rarity, TYPE type) { + super(id, name, rarity, EnchantmentType.SWORD); this.damageType = type; } @Override - public boolean isCompatibleWith(Enchantment enchantment) { + public boolean checkCompatibility(Enchantment enchantment) { return !(enchantment instanceof EnchantmentDamage); } diff --git a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageAll.java b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageAll.java index 91eb677c0ca..903eb864825 100644 --- a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageAll.java +++ b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageAll.java @@ -9,7 +9,7 @@ public class EnchantmentDamageAll extends EnchantmentDamage { public EnchantmentDamageAll() { - super(ID_DAMAGE_ALL, "all", 10, TYPE.ALL); + super(ID_DAMAGE_ALL, "all", Rarity.COMMON, TYPE.ALL); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java index 7612a8da99f..3966e261d23 100644 --- a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java +++ b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageArthropods.java @@ -13,7 +13,7 @@ public class EnchantmentDamageArthropods extends EnchantmentDamage { public EnchantmentDamageArthropods() { - super(ID_DAMAGE_ARTHROPODS, "arthropods", 5, TYPE.SMITE); + super(ID_DAMAGE_ARTHROPODS, "arthropods", Rarity.UNCOMMON, TYPE.SMITE); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageSmite.java b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageSmite.java index 9b0365f8e2e..ddc155a6cae 100644 --- a/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageSmite.java +++ b/src/main/java/cn/nukkit/item/enchantment/damage/EnchantmentDamageSmite.java @@ -10,7 +10,7 @@ public class EnchantmentDamageSmite extends EnchantmentDamage { public EnchantmentDamageSmite() { - super(ID_DAMAGE_SMITE, "undead", 5, TYPE.SMITE); + super(ID_DAMAGE_SMITE, "undead", Rarity.UNCOMMON, TYPE.SMITE); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLoot.java b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLoot.java index 8312de6bab5..3be23fe9686 100644 --- a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLoot.java +++ b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLoot.java @@ -9,8 +9,8 @@ */ public abstract class EnchantmentLoot extends Enchantment { - protected EnchantmentLoot(int id, String name, int weight, EnchantmentType type) { - super(id, name, weight, type); + protected EnchantmentLoot(int id, String name, Rarity rarity, EnchantmentType type) { + super(id, name, rarity, type); } @Override @@ -29,7 +29,7 @@ public int getMaxLevel() { } @Override - public boolean isCompatibleWith(Enchantment enchantment) { - return super.isCompatibleWith(enchantment) && enchantment.id != Enchantment.ID_SILK_TOUCH; + public boolean checkCompatibility(Enchantment enchantment) { + return super.checkCompatibility(enchantment) && enchantment.id != Enchantment.ID_SILK_TOUCH; } } diff --git a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootDigging.java b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootDigging.java index be2b4a4fecc..83d116a69e0 100644 --- a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootDigging.java +++ b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootDigging.java @@ -9,6 +9,6 @@ */ public class EnchantmentLootDigging extends EnchantmentLoot { public EnchantmentLootDigging() { - super(Enchantment.ID_FORTUNE_DIGGING, "lootBonusDigger", 2, EnchantmentType.DIGGER); + super(Enchantment.ID_FORTUNE_DIGGING, "lootBonusDigger", Rarity.RARE, EnchantmentType.DIGGER); } } diff --git a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootFishing.java b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootFishing.java index 4005a0f0c01..1016b6f3137 100644 --- a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootFishing.java +++ b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootFishing.java @@ -9,6 +9,6 @@ */ public class EnchantmentLootFishing extends EnchantmentLoot { public EnchantmentLootFishing() { - super(Enchantment.ID_FORTUNE_FISHING, "lootBonusFishing", 2, EnchantmentType.FISHING_ROD); + super(Enchantment.ID_FORTUNE_FISHING, "lootBonusFishing", Rarity.RARE, EnchantmentType.FISHING_ROD); } } diff --git a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootWeapon.java b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootWeapon.java index 758f073186e..d0f438b2794 100644 --- a/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootWeapon.java +++ b/src/main/java/cn/nukkit/item/enchantment/loot/EnchantmentLootWeapon.java @@ -9,6 +9,6 @@ */ public class EnchantmentLootWeapon extends EnchantmentLoot { public EnchantmentLootWeapon() { - super(Enchantment.ID_LOOTING, "lootBonus", 2, EnchantmentType.SWORD); + super(Enchantment.ID_LOOTING, "lootBonus", Rarity.RARE, EnchantmentType.SWORD); } } diff --git a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtection.java b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtection.java index 9729464dcdd..57949b75d1c 100644 --- a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtection.java +++ b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtection.java @@ -19,8 +19,8 @@ public enum TYPE { protected final TYPE protectionType; - protected EnchantmentProtection(int id, String name, int weight, EnchantmentProtection.TYPE type) { - super(id, name, weight, EnchantmentType.ARMOR); + protected EnchantmentProtection(int id, String name, Rarity rarity, EnchantmentProtection.TYPE type) { + super(id, name, rarity, EnchantmentType.ARMOR); this.protectionType = type; if (protectionType == TYPE.FALL) { this.type = EnchantmentType.ARMOR_FEET; @@ -28,14 +28,14 @@ protected EnchantmentProtection(int id, String name, int weight, EnchantmentProt } @Override - public boolean isCompatibleWith(Enchantment enchantment) { + public boolean checkCompatibility(Enchantment enchantment) { if (enchantment instanceof EnchantmentProtection) { if (((EnchantmentProtection) enchantment).protectionType == this.protectionType) { return false; } return ((EnchantmentProtection) enchantment).protectionType == TYPE.FALL || this.protectionType == TYPE.FALL; } - return super.isCompatibleWith(enchantment); + return super.checkCompatibility(enchantment); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionAll.java b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionAll.java index a4769cf1fc9..06575f7f03d 100644 --- a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionAll.java +++ b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionAll.java @@ -11,7 +11,7 @@ public class EnchantmentProtectionAll extends EnchantmentProtection { public EnchantmentProtectionAll() { - super(Enchantment.ID_PROTECTION_ALL, "all", 10, TYPE.ALL); + super(Enchantment.ID_PROTECTION_ALL, "all", Rarity.COMMON, TYPE.ALL); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionExplosion.java b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionExplosion.java index 8d290474ae3..ca2d74693e2 100644 --- a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionExplosion.java +++ b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionExplosion.java @@ -10,7 +10,7 @@ public class EnchantmentProtectionExplosion extends EnchantmentProtection { public EnchantmentProtectionExplosion() { - super(ID_PROTECTION_EXPLOSION, "explosion", 2, TYPE.EXPLOSION); + super(ID_PROTECTION_EXPLOSION, "explosion", Rarity.RARE, TYPE.EXPLOSION); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFall.java b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFall.java index bae3ce2be73..2fc1d91803d 100644 --- a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFall.java +++ b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFall.java @@ -10,7 +10,7 @@ public class EnchantmentProtectionFall extends EnchantmentProtection { public EnchantmentProtectionFall() { - super(ID_PROTECTION_FALL, "fall", 5, TYPE.FALL); + super(ID_PROTECTION_FALL, "fall", Rarity.UNCOMMON, TYPE.FALL); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFire.java b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFire.java index 793a259f680..f5aeac6a93b 100644 --- a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFire.java +++ b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionFire.java @@ -10,7 +10,7 @@ public class EnchantmentProtectionFire extends EnchantmentProtection { public EnchantmentProtectionFire() { - super(ID_PROTECTION_FIRE, "fire", 5, TYPE.FIRE); + super(ID_PROTECTION_FIRE, "fire", Rarity.UNCOMMON, TYPE.FIRE); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionProjectile.java b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionProjectile.java index 24c2100256b..5a05696bd7e 100644 --- a/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionProjectile.java +++ b/src/main/java/cn/nukkit/item/enchantment/protection/EnchantmentProtectionProjectile.java @@ -10,7 +10,7 @@ public class EnchantmentProtectionProjectile extends EnchantmentProtection { public EnchantmentProtectionProjectile() { - super(ID_PROTECTION_PROJECTILE, "projectile", 5, TYPE.PROJECTILE); + super(ID_PROTECTION_PROJECTILE, "projectile", Rarity.UNCOMMON, TYPE.PROJECTILE); } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTrident.java b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTrident.java index 72be2e52509..4e9b8e066a4 100644 --- a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTrident.java +++ b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTrident.java @@ -4,8 +4,7 @@ import cn.nukkit.item.enchantment.EnchantmentType; public abstract class EnchantmentTrident extends Enchantment { - protected EnchantmentTrident(int id, String name, int weight) { - super(id, name, weight, EnchantmentType.TRIDENT); + protected EnchantmentTrident(int id, String name, Rarity rarity) { + super(id, name, rarity, EnchantmentType.TRIDENT); } - } diff --git a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentChanneling.java b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentChanneling.java index 0a09f65d9fa..61956f8db4f 100644 --- a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentChanneling.java +++ b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentChanneling.java @@ -4,21 +4,6 @@ public class EnchantmentTridentChanneling extends EnchantmentTrident { public EnchantmentTridentChanneling() { - super(Enchantment.ID_TRIDENT_CHANNELING, "channeling", 1); - } - - @Override - public int getMinEnchantAbility(int level) { - return 20; - } - - @Override - public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 50; - } - - @Override - public int getMaxLevel() { - return 1; + super(Enchantment.ID_TRIDENT_CHANNELING, "tridentChanneling", Rarity.VERY_RARE); } } diff --git a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentImpaling.java b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentImpaling.java index 69d09cf6b5b..384ed212cd2 100644 --- a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentImpaling.java +++ b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentImpaling.java @@ -1,24 +1,34 @@ package cn.nukkit.item.enchantment.trident; +import cn.nukkit.entity.Entity; import cn.nukkit.item.enchantment.Enchantment; public class EnchantmentTridentImpaling extends EnchantmentTrident { public EnchantmentTridentImpaling() { - super(Enchantment.ID_TRIDENT_IMPALING, "impaling", 2); + super(Enchantment.ID_TRIDENT_IMPALING, "tridentImpaling", Rarity.RARE); } @Override public int getMinEnchantAbility(int level) { - return 1 + (level - 1) * 10; + return 8 * level - 7; } @Override public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 15; + return this.getMinEnchantAbility(level) + 20; } @Override public int getMaxLevel() { return 5; } + + @Override + public double getDamageBonus(Entity entity) { + if (entity.isInsideOfWater() || (entity.getLevel().isRaining() && entity.getLevel().canBlockSeeSky(entity))) { + return 2.5 * getLevel(); + } + + return 0; + } } diff --git a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentLoyalty.java b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentLoyalty.java index 812d982f515..be37f596a15 100644 --- a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentLoyalty.java +++ b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentLoyalty.java @@ -4,17 +4,12 @@ public class EnchantmentTridentLoyalty extends EnchantmentTrident { public EnchantmentTridentLoyalty() { - super(Enchantment.ID_TRIDENT_LOYALTY, "loyalty", 5); + super(Enchantment.ID_TRIDENT_LOYALTY, "tridentLoyalty", Rarity.UNCOMMON); } @Override public int getMinEnchantAbility(int level) { - return level * 10; - } - - @Override - public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 15; + return 7 * level + 5; } @Override diff --git a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentRiptide.java b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentRiptide.java index f964859b81b..e226fb7d766 100644 --- a/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentRiptide.java +++ b/src/main/java/cn/nukkit/item/enchantment/trident/EnchantmentTridentRiptide.java @@ -4,17 +4,12 @@ public class EnchantmentTridentRiptide extends EnchantmentTrident { public EnchantmentTridentRiptide() { - super(Enchantment.ID_TRIDENT_RIPTIDE, "riptide", 2); + super(Enchantment.ID_TRIDENT_RIPTIDE, "tridentRiptide", Rarity.RARE); } @Override public int getMinEnchantAbility(int level) { - return level * 10; - } - - @Override - public int getMaxEnchantAbility(int level) { - return this.getMinEnchantAbility(level) + 15; + return 7 * level + 10; } @Override diff --git a/src/main/java/cn/nukkit/level/EnumLevel.java b/src/main/java/cn/nukkit/level/EnumLevel.java index ea03b054d15..3423d84c9bd 100644 --- a/src/main/java/cn/nukkit/level/EnumLevel.java +++ b/src/main/java/cn/nukkit/level/EnumLevel.java @@ -2,6 +2,7 @@ import cn.nukkit.Server; import cn.nukkit.level.generator.Generator; +import cn.nukkit.math.NukkitMath; public enum EnumLevel { OVERWORLD, @@ -61,9 +62,9 @@ public static Position moveToNether(Position current) { return null; } else { if (current.level == OVERWORLD.level) { - return new Position(mRound(current.getFloorX() >> 3, 128), mRound(current.getFloorY(), 32), mRound(current.getFloorZ() >> 3, 128), NETHER.level); + return new Position(mRound(current.getFloorX() >> 3, 128), NukkitMath.clamp(mRound(current.getFloorY(), 32), 70, 128 - 10), mRound(current.getFloorZ() >> 3, 128), NETHER.level); } else if (current.level == NETHER.level) { - return new Position(mRound(current.getFloorX() << 3, 1024), mRound(current.getFloorY(), 32), mRound(current.getFloorZ() << 3, 1024), OVERWORLD.level); + return new Position(mRound(current.getFloorX() << 3, 1024), NukkitMath.clamp(mRound(current.getFloorY(), 32), 70, 256 - 10), mRound(current.getFloorZ() << 3, 1024), OVERWORLD.level); } else { throw new IllegalArgumentException("Neither overworld nor nether given!"); } diff --git a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java index cd9b14a7760..e78e79255c0 100644 --- a/src/main/java/cn/nukkit/level/GlobalBlockPalette.java +++ b/src/main/java/cn/nukkit/level/GlobalBlockPalette.java @@ -28,7 +28,6 @@ public class GlobalBlockPalette { private static final Int2ObjectMap legacyIdToString = new Int2ObjectOpenHashMap<>(); private static final Map stringToLegacyId = new HashMap<>(); private static final AtomicInteger runtimeIdAllocator = new AtomicInteger(0); - public static final byte[] BLOCK_PALETTE; static { legacyToRuntimeId.defaultReturnValue(-1); @@ -63,13 +62,6 @@ public class GlobalBlockPalette { int legacyId = legacyState.getInt("id") << 6 | legacyState.getShort("val"); legacyToRuntimeId.put(legacyId, runtimeId); } - state.remove("meta"); // No point in sending this since the client doesn't use it. - } - - try { - BLOCK_PALETTE = NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN, true); - } catch (IOException e) { - throw new AssertionError("Unable to write block palette", e); } } diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index ea138388a8d..292216bd25b 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -132,6 +132,7 @@ public class Level implements ChunkManager, Metadatable { randomTickBlocks[Block.FIRE] = true; randomTickBlocks[Block.GLOWING_REDSTONE_ORE] = true; randomTickBlocks[Block.COCOA_BLOCK] = true; + randomTickBlocks[Block.VINE] = true; } private final Long2ObjectOpenHashMap blockEntities = new Long2ObjectOpenHashMap<>(); @@ -410,6 +411,10 @@ public void initLevel() { Generator generator = generators.get(); this.dimension = generator.getDimension(); this.gameRules = this.provider.getGamerules(); + + this.server.getLogger().info("Preparing start region for level \"" + this.getFolderName() + "\""); + Position spawn = this.getSpawnLocation(); + this.populateChunk(spawn.getChunkX(), spawn.getChunkZ(), true); } public Generator getGenerator() { @@ -759,7 +764,7 @@ public void doTick(int currentTick) { } // Tick Weather - if (gameRules.getBoolean(GameRule.DO_WEATHER_CYCLE)) { + if (this.dimension != DIMENSION_NETHER && this.dimension != DIMENSION_THE_END && gameRules.getBoolean(GameRule.DO_WEATHER_CYCLE)) { this.rainTime--; if (this.rainTime <= 0) { if (!this.setRaining(!this.raining)) { @@ -2908,8 +2913,8 @@ public Position getSafeSpawn(Vector3 spawn) { FullChunk chunk = this.getChunk((int) v.x >> 4, (int) v.z >> 4, false); int x = (int) v.x & 0x0f; int z = (int) v.z & 0x0f; - if (chunk != null) { - int y = (int) NukkitMath.clamp(v.y, 0, 254); + if (chunk != null && chunk.isGenerated()) { + int y = (int) NukkitMath.clamp(v.y, 1, 254); boolean wasAir = chunk.getBlockId(x, y - 1, z) == 0; for (; y > 0; --y) { int b = chunk.getFullBlock(x, y, z); diff --git a/src/main/java/cn/nukkit/level/ListChunkManager.java b/src/main/java/cn/nukkit/level/ListChunkManager.java new file mode 100644 index 00000000000..1df86a9a595 --- /dev/null +++ b/src/main/java/cn/nukkit/level/ListChunkManager.java @@ -0,0 +1,85 @@ +package cn.nukkit.level; + +import cn.nukkit.block.Block; +import cn.nukkit.level.format.generic.BaseFullChunk; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ListChunkManager implements ChunkManager { + + private ChunkManager parent; + private List blocks; + + public ListChunkManager(ChunkManager parent) { + this.parent = parent; + this.blocks = new ArrayList<>(); + } + + @Override + public int getBlockIdAt(int x, int y, int z) { + Optional optionalBlock = this.blocks.stream().filter(block -> block.getFloorX() == x && block.getFloorY() == y && block.getFloorZ() == z).findAny(); + return optionalBlock.map(Block::getId).orElseGet(() -> this.parent.getBlockIdAt(x, y, z)); + } + + @Override + public void setBlockFullIdAt(int x, int y, int z, int fullId) { + this.blocks.removeIf(block -> block.getFloorX() == x && block.getFloorY() == y && block.getFloorZ() == z); + this.blocks.add(Block.get(fullId, null, x, y, z)); + } + + @Override + public void setBlockIdAt(int x, int y, int z, int id) { + Optional optionalBlock = this.blocks.stream().filter(block -> block.getFloorX() == x && block.getFloorY() == y && block.getFloorZ() == z).findAny(); + Block block = optionalBlock.orElse(Block.get(this.getBlockIdAt(x, y, z), this.getBlockDataAt(x, y, z), new Position(x, y, z))); + this.blocks.remove(block); + this.blocks.add(Block.get(this.getBlockIdAt(x, y, z), this.getBlockDataAt(x, y, z), new Position(x, y, z))); + } + + @Override + public void setBlockAt(int x, int y, int z, int id, int data) { + this.blocks.removeIf(block -> block.getFloorX() == x && block.getFloorY() == y && block.getFloorZ() == z); + this.blocks.add(Block.get(id, data, new Position(x, y, z))); + } + + @Override + public int getBlockDataAt(int x, int y, int z) { + Optional optionalBlock = this.blocks.stream().filter(block -> block.getFloorX() == x && block.getFloorY() == y && block.getFloorZ() == z).findAny(); + return optionalBlock.map(Block::getDamage).orElseGet(() -> this.parent.getBlockDataAt(x, y, z)); + } + + @Override + public void setBlockDataAt(int x, int y, int z, int data) { + Optional optionalBlock = this.blocks.stream().filter(block -> block.getFloorX() == x && block.getFloorY() == y && block.getFloorZ() == z).findAny(); + Block block = optionalBlock.orElse(Block.get(this.getBlockIdAt(x, y, z), this.getBlockDataAt(x, y, z), new Position(x, y, z))); + this.blocks.remove(block); + block.setDamage(data); + this.blocks.add(block); + } + + @Override + public BaseFullChunk getChunk(int chunkX, int chunkZ) { + return this.parent.getChunk(chunkX, chunkZ); + } + + @Override + public void setChunk(int chunkX, int chunkZ) { + this.parent.setChunk(chunkX, chunkZ); + } + + @Override + public void setChunk(int chunkX, int chunkZ, BaseFullChunk chunk) { + this.parent.setChunk(chunkX, chunkZ, chunk); + } + + @Override + public long getSeed() { + return this.parent.getSeed(); + } + + public List getBlocks() { + return this.blocks; + } + +} diff --git a/src/main/java/cn/nukkit/level/Sound.java b/src/main/java/cn/nukkit/level/Sound.java index c6e39757dd2..abf72ac738c 100644 --- a/src/main/java/cn/nukkit/level/Sound.java +++ b/src/main/java/cn/nukkit/level/Sound.java @@ -4,22 +4,32 @@ * @author CreeperFace */ public enum Sound { + AMBIENT_BASALT_DELTAS_ADDITIONS("ambient.basalt_deltas.additions"), + AMBIENT_BASALT_DELTAS_LOOP("ambient.basalt_deltas.loop"), AMBIENT_BASALT_DELTAS_MOOD("ambient.basalt_deltas.mood"), AMBIENT_CAVE("ambient.cave"), + AMBIENT_CRIMSON_FOREST_ADDITIONS("ambient.crimson_forest.additions"), + AMBIENT_CRIMSON_FOREST_LOOP("ambient.crimson_forest.loop"), AMBIENT_CRIMSON_FOREST_MOOD("ambient.crimson_forest.mood"), + AMBIENT_NETHER_WASTES_ADDITIONS("ambient.nether_wastes.additions"), + AMBIENT_NETHER_WASTES_LOOP("ambient.nether_wastes.loop"), AMBIENT_NETHER_WASTES_MOOD("ambient.nether_wastes.mood"), + AMBIENT_SOULSAND_VALLEY_ADDITIONS("ambient.soulsand_valley.additions"), + AMBIENT_SOULSAND_VALLEY_LOOP("ambient.soulsand_valley.loop"), AMBIENT_SOULSAND_VALLEY_MOOD("ambient.soulsand_valley.mood"), + AMBIENT_WARPED_FOREST_ADDITIONS("ambient.warped_forest.additions"), + AMBIENT_WARPED_FOREST_LOOP("ambient.warped_forest.loop"), AMBIENT_WARPED_FOREST_MOOD("ambient.warped_forest.mood"), AMBIENT_WEATHER_LIGHTNING_IMPACT("ambient.weather.lightning.impact"), AMBIENT_WEATHER_RAIN("ambient.weather.rain"), AMBIENT_WEATHER_THUNDER("ambient.weather.thunder"), - ARMOR_EQUIP_NETHERITE("armor.equip_netherite"), ARMOR_EQUIP_CHAIN("armor.equip_chain"), ARMOR_EQUIP_DIAMOND("armor.equip_diamond"), ARMOR_EQUIP_GENERIC("armor.equip_generic"), ARMOR_EQUIP_GOLD("armor.equip_gold"), ARMOR_EQUIP_IRON("armor.equip_iron"), ARMOR_EQUIP_LEATHER("armor.equip_leather"), + ARMOR_EQUIP_NETHERITE("armor.equip_netherite"), BEACON_ACTIVATE("beacon.activate"), BEACON_AMBIENT("beacon.ambient"), BEACON_DEACTIVATE("beacon.deactivate"), @@ -125,6 +135,7 @@ public enum Sound { DIG_GRASS("dig.grass"), DIG_GRAVEL("dig.gravel"), DIG_HONEY_BLOCK("dig.honey_block"), + DIG_LODESTONE("dig.lodestone"), DIG_NETHER_BRICK("dig.nether_brick"), DIG_NETHER_GOLD_ORE("dig.nether_gold_ore"), DIG_NETHER_SPROUTS("dig.nether_sprouts"), @@ -284,7 +295,6 @@ public enum Sound { LIQUID_LAVAPOP("liquid.lavapop"), LIQUID_WATER("liquid.water"), LODESTONE_COMPASS_LINK_COMPASS_TO_LODESTONE("lodestone_compass.link_compass_to_lodestone"), - DIG_LODESTONE("dig.lodestone"), MINECART_BASE("minecart.base"), MINECART_INSIDE("minecart.inside"), MOB_AGENT_SPAWN("mob.agent.spawn"), @@ -473,6 +483,7 @@ public enum Sound { MOB_PARROT_STEP("mob.parrot.step"), MOB_PHANTOM_BITE("mob.phantom.bite"), MOB_PHANTOM_DEATH("mob.phantom.death"), + MOB_PHANTOM_FLAP("mob.phantom.flap"), MOB_PHANTOM_HURT("mob.phantom.hurt"), MOB_PHANTOM_IDLE("mob.phantom.idle"), MOB_PHANTOM_SWOOP("mob.phantom.swoop"), @@ -493,8 +504,8 @@ public enum Sound { MOB_PIGLIN_BRUTE_AMBIENT("mob.piglin_brute.ambient"), MOB_PIGLIN_BRUTE_ANGRY("mob.piglin_brute.angry"), MOB_PIGLIN_BRUTE_CONVERTED_TO_ZOMBIFIED("mob.piglin_brute.converted_to_zombified"), - MOB_PIGLIN_BRUTE_HURT("mob.piglin_brute.hurt"), MOB_PIGLIN_BRUTE_DEATH("mob.piglin_brute.death"), + MOB_PIGLIN_BRUTE_HURT("mob.piglin_brute.hurt"), MOB_PIGLIN_BRUTE_STEP("mob.piglin_brute.step"), MOB_PILLAGER_CELEBRATE("mob.pillager.celebrate"), MOB_PILLAGER_DEATH("mob.pillager.death"), @@ -620,11 +631,11 @@ public enum Sound { MOB_WOLF_STEP("mob.wolf.step"), MOB_WOLF_WHINE("mob.wolf.whine"), MOB_ZOGLIN_ANGRY("mob.zoglin.angry"), + MOB_ZOGLIN_ATTACK("mob.zoglin.attack"), MOB_ZOGLIN_DEATH("mob.zoglin.death"), - MOB_ZOGLIN_IDLE("mob.zoglin.idle"), MOB_ZOGLIN_HURT("mob.zoglin.hurt"), + MOB_ZOGLIN_IDLE("mob.zoglin.idle"), MOB_ZOGLIN_STEP("mob.zoglin.step"), - MOB_ZOGLIN_ATTACK("mob.zoglin.attack"), MOB_ZOMBIE_DEATH("mob.zombie.death"), MOB_ZOMBIE_HURT("mob.zombie.hurt"), MOB_ZOMBIE_REMEDY("mob.zombie.remedy"), @@ -728,8 +739,8 @@ public enum Sound { STEP_ANCIENT_DEBRIS("step.ancient_debris"), STEP_BASALT("step.basalt"), STEP_BONE_BLOCK("step.bone_block"), - STEP_CLOTH("step.cloth"), STEP_CHAIN("step.chain"), + STEP_CLOTH("step.cloth"), STEP_CORAL("step.coral"), STEP_GRASS("step.grass"), STEP_GRAVEL("step.gravel"), diff --git a/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java b/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java index b9e3706034b..a859e7414eb 100644 --- a/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java +++ b/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java @@ -1,6 +1,5 @@ package cn.nukkit.level.biome.impl.mushroom; -import cn.nukkit.block.Block; import cn.nukkit.level.biome.type.GrassyBiome; import cn.nukkit.level.generator.populator.impl.MushroomPopulator; diff --git a/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java b/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java index e7b854e0ae8..751e5e6b99f 100644 --- a/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java +++ b/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java @@ -1,7 +1,5 @@ package cn.nukkit.level.biome.type; -import cn.nukkit.block.Block; - /** * author: MagicDroidX * Nukkit Project diff --git a/src/main/java/cn/nukkit/level/format/anvil/Anvil.java b/src/main/java/cn/nukkit/level/format/anvil/Anvil.java index 71d1a9c5e78..2a1e8af6494 100644 --- a/src/main/java/cn/nukkit/level/format/anvil/Anvil.java +++ b/src/main/java/cn/nukkit/level/format/anvil/Anvil.java @@ -131,19 +131,6 @@ public AsyncTask requestChunkTask(int x, int z) throws ChunkException { } } - Map extra = chunk.getBlockExtraDataArray(); - BinaryStream extraData; - if (!extra.isEmpty()) { - extraData = new BinaryStream(); - extraData.putVarInt(extra.size()); - for (Map.Entry entry : extra.entrySet()) { - extraData.putVarInt(entry.getKey()); - extraData.putLShort(entry.getValue()); - } - } else { - extraData = null; - } - BinaryStream stream = ThreadCache.binaryStream.get().reset(); int count = 0; cn.nukkit.level.format.ChunkSection[] sections = chunk.getSections(); @@ -153,20 +140,13 @@ public AsyncTask requestChunkTask(int x, int z) throws ChunkException { break; } } -// stream.putByte((byte) count); count is now sent in packet + for (int i = 0; i < count; i++) { sections[i].writeTo(stream); } -// for (byte height : chunk.getHeightMapArray()) { -// stream.putByte(height); -// } computed client side? + stream.put(chunk.getBiomeIdArray()); - stream.putByte((byte) 0); - if (extraData != null) { - stream.put(extraData.getBuffer()); - } else { - stream.putVarInt(0); - } + stream.putByte((byte) 0); // Border blocks stream.put(blockEntities); this.getLevel().chunkRequestCallback(timestamp, x, z, count, stream.getBuffer()); diff --git a/src/main/java/cn/nukkit/level/generator/object/tree/NewJungleTree.java b/src/main/java/cn/nukkit/level/generator/object/tree/NewJungleTree.java index 7ccddbedc51..f9b1cfc579e 100644 --- a/src/main/java/cn/nukkit/level/generator/object/tree/NewJungleTree.java +++ b/src/main/java/cn/nukkit/level/generator/object/tree/NewJungleTree.java @@ -210,9 +210,7 @@ private boolean isAirBlock(ChunkManager level, BlockVector3 v) { } private int getCocoaMeta(int age, int side) { - int meta = 0; - - meta *= age; + int meta = age * 4; //3 4 2 5 switch (side) { diff --git a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java index b34290591ca..3feec7fa3b1 100644 --- a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java +++ b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java @@ -1,12 +1,10 @@ package cn.nukkit.level.generator.populator.impl; -import cn.nukkit.block.BlockID; import cn.nukkit.level.ChunkManager; import cn.nukkit.level.biome.EnumBiome; import cn.nukkit.level.format.FullChunk; import cn.nukkit.level.biome.Biome; import cn.nukkit.level.biome.type.CoveredBiome; -import cn.nukkit.level.generator.Normal; import cn.nukkit.level.generator.populator.type.Populator; import cn.nukkit.math.NukkitRandom; diff --git a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java index 472d587354e..0bbaec8dac6 100644 --- a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java +++ b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java @@ -1,6 +1,5 @@ package cn.nukkit.level.generator.populator.impl; -import cn.nukkit.block.Block; import cn.nukkit.level.ChunkManager; import cn.nukkit.level.format.FullChunk; import cn.nukkit.level.generator.object.ore.OreType; diff --git a/src/main/java/cn/nukkit/level/particle/ItemBreakParticle.java b/src/main/java/cn/nukkit/level/particle/ItemBreakParticle.java index 2932c326299..458e2debf30 100644 --- a/src/main/java/cn/nukkit/level/particle/ItemBreakParticle.java +++ b/src/main/java/cn/nukkit/level/particle/ItemBreakParticle.java @@ -1,14 +1,34 @@ package cn.nukkit.level.particle; import cn.nukkit.item.Item; +import cn.nukkit.item.RuntimeItems; import cn.nukkit.math.Vector3; +import cn.nukkit.network.protocol.DataPacket; +import cn.nukkit.network.protocol.LevelEventPacket; /** * Created on 2015/11/21 by xtypr. * Package cn.nukkit.level.particle in project Nukkit . */ -public class ItemBreakParticle extends GenericParticle { +public class ItemBreakParticle extends Particle { + + private final int data; + public ItemBreakParticle(Vector3 pos, Item item) { - super(pos, Particle.TYPE_ITEM_BREAK, (item.getId() << 16) | item.getDamage()); + super(pos.x, pos.y, pos.z); + int networkFullId = RuntimeItems.getRuntimeMapping().getNetworkFullId(item); + int networkId = RuntimeItems.getNetworkId(networkFullId); + this.data = (networkId << 16 | item.getDamage()); + } + + @Override + public DataPacket[] encode() { + LevelEventPacket packet = new LevelEventPacket(); + packet.evid = (short) (LevelEventPacket.EVENT_ADD_PARTICLE_MASK | Particle.TYPE_ITEM_BREAK); + packet.x = (float) this.x; + packet.y = (float) this.y; + packet.z = (float) this.z; + packet.data = this.data; + return new DataPacket[]{packet}; } } diff --git a/src/main/java/cn/nukkit/level/particle/Particle.java b/src/main/java/cn/nukkit/level/particle/Particle.java index dc9b8cf4a8a..870c253fab6 100644 --- a/src/main/java/cn/nukkit/level/particle/Particle.java +++ b/src/main/java/cn/nukkit/level/particle/Particle.java @@ -3,6 +3,8 @@ import cn.nukkit.math.Vector3; import cn.nukkit.network.protocol.DataPacket; +import java.lang.reflect.Field; + /** * author: MagicDroidX * Nukkit Project @@ -80,7 +82,28 @@ public abstract class Particle extends Vector3 { //65 same as 64 public static final int TYPE_FALLING_DRAGONS_BREATH = 66; public static final int TYPE_DRAGONS_BREATH = 67; + + public static final Integer getParticleIdByName(String name) { + name = name.toUpperCase(); + + try { + Field field = Particle.class.getField((name.startsWith("TYPE_") == true ? name : ("TYPE_" + name))); + + Class type = field.getType(); + if(type==int.class) { + return field.getInt(null); + } + } catch(NoSuchFieldException | IllegalAccessException e) { + // ignore + } + return null; + } + + public static final boolean particleExists(String name) { + return getParticleIdByName(name) != null; + } + public Particle() { super(0, 0, 0); } diff --git a/src/main/java/cn/nukkit/level/util/PalettedBlockStorage.java b/src/main/java/cn/nukkit/level/util/PalettedBlockStorage.java index 964f43b1e96..5e3ad0e62cf 100644 --- a/src/main/java/cn/nukkit/level/util/PalettedBlockStorage.java +++ b/src/main/java/cn/nukkit/level/util/PalettedBlockStorage.java @@ -1,5 +1,6 @@ package cn.nukkit.level.util; +import cn.nukkit.level.GlobalBlockPalette; import cn.nukkit.utils.BinaryStream; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -20,7 +21,7 @@ public PalettedBlockStorage() { public PalettedBlockStorage(BitArrayVersion version) { this.bitArray = version.createPalette(SIZE); this.palette = new IntArrayList(16); - this.palette.add(0); // Air is at the start of every palette. + this.palette.add(GlobalBlockPalette.getOrCreateRuntimeId(0)); // Air is at the start of every palette. } private PalettedBlockStorage(BitArray bitArray, IntList palette) { diff --git a/src/main/java/cn/nukkit/metrics/Metrics.java b/src/main/java/cn/nukkit/metrics/Metrics.java new file mode 100644 index 00000000000..b7122e3baa3 --- /dev/null +++ b/src/main/java/cn/nukkit/metrics/Metrics.java @@ -0,0 +1,533 @@ +package cn.nukkit.metrics; + +import cn.nukkit.utils.MainLogger; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; + +import javax.net.ssl.HttpsURLConnection; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPOutputStream; + +/** + * bStats collects some data for plugin authors. + * + * Check out https://bStats.org/ to learn more about bStats! + */ +public class Metrics { + public static final int B_STATS_VERSION = 1; + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/server-implementation"; + + // A list with all custom charts + private final List charts = new ArrayList<>(); + + // The name of the server software + private final String name; + + // The uuid of the server + private final String serverUUID; + + // Should failed requests be logged? + private static boolean logFailedRequests = false; + + // The logger for the failed requests + private static MainLogger logger; + + public Metrics(String name, String serverUUID, boolean logFailedRequests, MainLogger logger) { + this.name = name; + this.serverUUID = serverUUID; + Metrics.logFailedRequests = logFailedRequests; + Metrics.logger = logger; + + // Start submitting the data + startSubmitting(); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + if (chart == null) { + throw new IllegalArgumentException("Chart cannot be null!"); + } + charts.add(chart); + } + + /** + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() { + final Runnable submitTask = this::submitData; + + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate(submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + /** + * Gets the plugin specific data. + * + * @return The plugin specific data. + */ + private JSONObject getPluginData() { + JSONObject data = new JSONObject(); + + data.put("pluginName", name); // Append the name of the server software + JSONArray customCharts = new JSONArray(); + for (CustomChart customChart : charts) { + // Add the data of the custom charts + JSONObject chart = customChart.getRequestJsonObject(); + if (chart == null) { // If the chart is null, we skip it + continue; + } + customCharts.add(chart); + } + data.put("customCharts", customCharts); + + return data; + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JSONObject getServerData() { + // OS specific data + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JSONObject data = new JSONObject(); + data.put("serverUUID", serverUUID); + data.put("osName", osName); + data.put("osArch", osArch); + data.put("osVersion", osVersion); + data.put("coreCount", coreCount); + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JSONObject data = getServerData(); + + JSONArray pluginData = new JSONArray(); + pluginData.add(getPluginData()); + data.put("plugins", pluginData); + + try { + // We are still in the Thread of the timer, so nothing get blocked :) + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + logger.warning("Could not submit stats of " + name, e); + } + } + } + + /** + * Sends the data to the bStats server. + * + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(JSONObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null!"); + } + + HttpsURLConnection connection = (HttpsURLConnection) new java.net.URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + connection.getInputStream().close(); // We don't care about the response - Just send our data :) + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + gzip.close(); + return outputStream.toByteArray(); + } + + /** + * Represents a custom chart. + */ + public static abstract class CustomChart { + + // The id of the chart + final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + private JSONObject getRequestJsonObject() { + JSONObject chart = new JSONObject(); + chart.put("chartId", chartId); + try { + JSONObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.put("data", data); + } catch (Throwable t) { + return null; + } + return chart; + } + + protected abstract JSONObject getChartData() throws Exception; + + } + + /** + * Represents a custom simple pie. + */ + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + } + + /** + * Represents a custom advanced pie. + */ + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom drilldown pie. + */ + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JSONObject value = new JSONObject(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + value.put(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + values.put(entryValues.getKey(), value); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom single line chart. + */ + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + + } + + /** + * Represents a custom multi line chart. + */ + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom simple bar chart. + */ + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + JSONArray categoryValues = new JSONArray(); + categoryValues.add(entry.getValue()); + values.put(entry.getKey(), categoryValues); + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom advanced bar chart. + */ + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + continue; // Skip this invalid + } + allSkipped = false; + JSONArray categoryValues = new JSONArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(categoryValue); + } + values.put(entry.getKey(), categoryValues); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + +} \ No newline at end of file diff --git a/src/main/java/cn/nukkit/metrics/NukkitMetrics.java b/src/main/java/cn/nukkit/metrics/NukkitMetrics.java new file mode 100644 index 00000000000..0a1c9bde671 --- /dev/null +++ b/src/main/java/cn/nukkit/metrics/NukkitMetrics.java @@ -0,0 +1,170 @@ +package cn.nukkit.metrics; + +import cn.nukkit.Server; +import cn.nukkit.utils.Config; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class NukkitMetrics { + private static boolean metricsStarted = false; + + private final Server server; + + private boolean enabled; + private String serverUUID; + private boolean logFailedRequests; + + public NukkitMetrics(Server server) { + this.server = server; + + if (metricsStarted) { + return; + } + + try { + this.loadConfig(); + } catch (IOException e) { + e.printStackTrace(); + } + + if (enabled) { + Metrics metrics = new Metrics("Nukkit", serverUUID, logFailedRequests, server.getLogger()); + + metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> server.getOnlinePlayers().size())); + metrics.addCustomChart(new Metrics.SimplePie("codename", server::getCodename)); + metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", server::getVersion)); + metrics.addCustomChart(new Metrics.SimplePie("nukkit_version", server::getNukkitVersion)); + metrics.addCustomChart(new Metrics.SimplePie("xbox_auth", () -> server.getPropertyBoolean("xbox-auth") ? "Required" : "Not required")); + + metrics.addCustomChart(new Metrics.AdvancedPie("player_platform", () -> { + Map valueMap = new HashMap<>(); + + server.getOnlinePlayers().forEach((uuid, player) -> { + String deviceOS = mapDeviceOSToString(player.getLoginChainData().getDeviceOS()); + if (!valueMap.containsKey(deviceOS)) { + valueMap.put(deviceOS, 1); + } else { + valueMap.put(deviceOS, valueMap.get(deviceOS) + 1); + } + }); + return valueMap; + })); + + metrics.addCustomChart(new Metrics.AdvancedPie("player_game_version", () -> { + Map valueMap = new HashMap<>(); + + server.getOnlinePlayers().forEach((uuid, player) -> { + String gameVersion = player.getLoginChainData().getGameVersion(); + if (!valueMap.containsKey(gameVersion)) { + valueMap.put(gameVersion, 1); + } else { + valueMap.put(gameVersion, valueMap.get(gameVersion) + 1); + } + }); + return valueMap; + })); + + // The following code can be attributed to the PaperMC project + // https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614 + metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { + Map> map = new HashMap<>(); + String javaVersion = System.getProperty("java.version"); + Map entry = new HashMap<>(); + entry.put(javaVersion, 1); + + // http://openjdk.java.net/jeps/223 + // Java decided to change their versioning scheme and in doing so modified the java.version system + // property to return $major[.$minor][.$secuity][-ea], as opposed to 1.$major.0_$identifier + // we can handle pre-9 by checking if the "major" is equal to "1", otherwise, 9+ + String majorVersion = javaVersion.split("\\.")[0]; + String release; + + int indexOf = javaVersion.lastIndexOf('.'); + + if (majorVersion.equals("1")) { + release = "Java " + javaVersion.substring(0, indexOf); + } else { + // of course, it really wouldn't be all that simple if they didn't add a quirk, now would it + // valid strings for the major may potentially include values such as -ea to deannotate a pre release + Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion); + if (versionMatcher.find()) { + majorVersion = versionMatcher.group(0); + } + release = "Java " + majorVersion; + } + map.put(release, entry); + return map; + })); + + metricsStarted = true; + } + } + + /** + * Loads the bStats configuration. + */ + private void loadConfig() throws IOException { + File bStatsFolder = new File(server.getPluginPath(), "bStats"); + + if (!bStatsFolder.exists() && !bStatsFolder.mkdirs()) { + server.getLogger().warning("Failed to create bStats metrics directory"); + return; + } + + File configFile = new File(bStatsFolder, "config.yml"); + if (!configFile.exists()) { + writeFile(configFile, + "# bStats collects some data for plugin authors like how many servers are using their plugins.", + "# To honor their work, you should not disable it.", + "# This has nearly no effect on the server performance!", + "# Check out https://bStats.org/ to learn more :)", + "enabled: true", + "serverUuid: \"" + UUID.randomUUID().toString() + "\"", + "logFailedRequests: false"); + } + + Config config = new Config(configFile, Config.YAML); + + // Load configuration + this.enabled = config.getBoolean("enabled", true); + this.serverUUID = config.getString("serverUuid"); + this.logFailedRequests = config.getBoolean("logFailedRequests", false); + } + + private void writeFile(File file, String... lines) throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + for (String line : lines) { + writer.write(line); + writer.newLine(); + } + } + } + + private String mapDeviceOSToString(int os) { + switch (os) { + case 1: return "Android"; + case 2: return "iOS"; + case 3: return "macOS"; + case 4: return "FireOS"; + case 5: return "Gear VR"; + case 6: return "Hololens"; + case 7: return "Windows 10"; + case 8: return "Windows"; + case 9: return "Dedicated"; + case 10: return "PS4"; + case 11: return "Switch"; + case 12: return "Switch"; + case 13: return "Xbox One"; + case 14: return "Windows Phone"; + } + return "Unknown"; + } +} diff --git a/src/main/java/cn/nukkit/network/Network.java b/src/main/java/cn/nukkit/network/Network.java index 84976ebbf63..34664469c25 100644 --- a/src/main/java/cn/nukkit/network/Network.java +++ b/src/main/java/cn/nukkit/network/Network.java @@ -5,23 +5,23 @@ import cn.nukkit.Server; import cn.nukkit.nbt.stream.FastByteArrayOutputStream; import cn.nukkit.network.protocol.*; -import cn.nukkit.utils.Binary; import cn.nukkit.utils.BinaryStream; import cn.nukkit.utils.MainLogger; import cn.nukkit.utils.ThreadCache; import cn.nukkit.utils.Utils; import cn.nukkit.utils.VarInt; -import cn.nukkit.utils.Zlib; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.extern.log4j.Log4j2; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.io.ByteArrayInputStream; import java.io.IOException; -import java.util.ArrayList; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolException; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -72,62 +72,70 @@ public Network(Server server) { public static byte[] inflateRaw(byte[] data) throws IOException, DataFormatException { Inflater inflater = INFLATER_RAW.get(); - inflater.reset(); - inflater.setInput(data); - inflater.finished(); - - FastByteArrayOutputStream bos = ThreadCache.fbaos.get(); - bos.reset(); - byte[] buf = BUFFER.get(); - while (!inflater.finished()) { - int i = inflater.inflate(buf); - if (i == 0) { - throw new IOException("Could not decompress the data. Needs input: "+inflater.needsInput()+", Needs Dictionary: "+inflater.needsDictionary()); + try { + inflater.setInput(data); + inflater.finished(); + + FastByteArrayOutputStream bos = ThreadCache.fbaos.get(); + bos.reset(); + byte[] buf = BUFFER.get(); + while (!inflater.finished()) { + int i = inflater.inflate(buf); + if (i == 0) { + throw new IOException("Could not decompress the data. Needs input: " + inflater.needsInput() + ", Needs Dictionary: " + inflater.needsDictionary()); + } + bos.write(buf, 0, i); } - bos.write(buf, 0, i); + return bos.toByteArray(); + } finally { + inflater.reset(); } - return bos.toByteArray(); } public static byte[] deflateRaw(byte[] data, int level) throws IOException { Deflater deflater = DEFLATER_RAW.get(); - deflater.reset(); - deflater.setLevel(level); - deflater.setInput(data); - deflater.finish(); - FastByteArrayOutputStream bos = ThreadCache.fbaos.get(); - bos.reset(); - byte[] buffer = BUFFER.get(); - while (!deflater.finished()) { - int i = deflater.deflate(buffer); - bos.write(buffer, 0, i); - } + try { + deflater.setLevel(level); + deflater.setInput(data); + deflater.finish(); + FastByteArrayOutputStream bos = ThreadCache.fbaos.get(); + bos.reset(); + byte[] buffer = BUFFER.get(); + while (!deflater.finished()) { + int i = deflater.deflate(buffer); + bos.write(buffer, 0, i); + } - return bos.toByteArray(); + return bos.toByteArray(); + } finally { + deflater.reset(); + } } public static byte[] deflateRaw(byte[][] datas, int level) throws IOException { Deflater deflater = DEFLATER_RAW.get(); - deflater.reset(); - deflater.setLevel(level); - FastByteArrayOutputStream bos = ThreadCache.fbaos.get(); - bos.reset(); - byte[] buffer = BUFFER.get(); - - for (byte[] data : datas) { - deflater.setInput(data); - while (!deflater.needsInput()) { + try { + deflater.setLevel(level); + FastByteArrayOutputStream bos = ThreadCache.fbaos.get(); + bos.reset(); + byte[] buffer = BUFFER.get(); + + for (byte[] data : datas) { + deflater.setInput(data); + while (!deflater.needsInput()) { + int i = deflater.deflate(buffer); + bos.write(buffer, 0, i); + } + } + deflater.finish(); + while (!deflater.finished()) { int i = deflater.deflate(buffer); bos.write(buffer, 0, i); } + return bos.toByteArray(); + } finally { + deflater.reset(); } - deflater.finish(); - while (!deflater.finished()) { - int i = deflater.deflate(buffer); - bos.write(buffer, 0, i); - } - //Deflater::end is called the time when the process exits. - return bos.toByteArray(); } public void addStatistics(double upload, double download) { @@ -216,48 +224,61 @@ public Server getServer() { } public void processBatch(BatchPacket packet, Player player) { + List packets = new ObjectArrayList<>(); + try { + processBatch(packet.payload, packets); + } catch (ProtocolException e) { + player.close("", e.getMessage()); + log.error("Unable to process player packets ", e); + } + } + + public void processBatch(byte[] payload, Collection packets) throws ProtocolException { byte[] data; try { - data = Network.inflateRaw(packet.payload); + data = Network.inflateRaw(payload); //data = Zlib.inflate(packet.payload, 2 * 1024 * 1024); // Max 2MB } catch (Exception e) { log.debug("Exception while inflating batch packet", e); return; } - int len = data.length; BinaryStream stream = new BinaryStream(data); try { - List packets = new ArrayList<>(); int count = 0; - while (stream.offset < len) { + while (!stream.feof()) { count++; if (count >= 1000) { - player.close("", "Illegal Batch Packet"); - return; + throw new ProtocolException("Illegal batch with " + count + " packets"); } byte[] buf = stream.getByteArray(); - DataPacket pk = this.getPacketFromBuffer(buf); + ByteArrayInputStream bais = new ByteArrayInputStream(buf); + int header = (int) VarInt.readUnsignedVarInt(bais); + + // | Client ID | Sender ID | Packet ID | + // | 2 bits | 2 bits | 10 bits | + int packetId = header & 0x3ff; + + DataPacket pk = this.getPacket(packetId); if (pk != null) { + pk.setBuffer(buf, buf.length - bais.available()); try { pk.decode(); } catch (Exception e) { - log.warn("Unable to decode {} from {}", pk.getClass().getSimpleName(), player.getName()); - log.throwing(e); if (log.isTraceEnabled()) { - log.trace("Dumping Packet\n{}", ByteBufUtil.prettyHexDump(Unpooled.wrappedBuffer(packet.payload))); + log.trace("Dumping Packet\n{}", ByteBufUtil.prettyHexDump(Unpooled.wrappedBuffer(buf))); } - throw e; + log.error("Unable to decode packet", e); + throw new IllegalStateException("Unable to decode " + pk.getClass().getSimpleName()); } packets.add(pk); + } else { + log.debug("Received unknown packet with ID: {}", Integer.toHexString(packetId)); } } - - processPackets(player, packets); - } catch (Exception e) { MainLogger.getLogger().error("Error whilst decoding batch packet", e); } @@ -274,17 +295,8 @@ public void processPackets(Player player, List packets) { packets.forEach(player::handleDataPacket); } - private DataPacket getPacketFromBuffer(byte[] buffer) throws IOException { - ByteArrayInputStream stream = new ByteArrayInputStream(buffer); - DataPacket pk = this.getPacket((byte) VarInt.readUnsignedVarInt(stream)); - if (pk != null) { - pk.setBuffer(buffer, buffer.length - stream.available()); - } - return pk; - } - - public DataPacket getPacket(byte id) { - Class clazz = this.packetPool[id & 0xff]; + public DataPacket getPacket(int id) { + Class clazz = this.packetPool[id]; if (clazz != null) { try { return clazz.newInstance(); @@ -328,6 +340,7 @@ private void registerPackets() { this.registerPacket(ProtocolInfo.ADD_PLAYER_PACKET, AddPlayerPacket.class); this.registerPacket(ProtocolInfo.ADVENTURE_SETTINGS_PACKET, AdventureSettingsPacket.class); this.registerPacket(ProtocolInfo.ANIMATE_PACKET, AnimatePacket.class); + this.registerPacket(ProtocolInfo.ANVIL_DAMAGE_PACKET, AnvilDamagePacket.class); this.registerPacket(ProtocolInfo.AVAILABLE_COMMANDS_PACKET, AvailableCommandsPacket.class); this.registerPacket(ProtocolInfo.BATCH_PACKET, BatchPacket.class); this.registerPacket(ProtocolInfo.BLOCK_ENTITY_DATA_PACKET, BlockEntityDataPacket.class); @@ -429,5 +442,6 @@ private void registerPackets() { this.registerPacket(ProtocolInfo.PLAYER_ARMOR_DAMAGE_PACKET, PlayerArmorDamagePacket.class); this.registerPacket(ProtocolInfo.PLAYER_ENCHANT_OPTIONS_PACKET, PlayerEnchantOptionsPacket.class); this.registerPacket(ProtocolInfo.UPDATE_PLAYER_GAME_TYPE_PACKET, UpdatePlayerGameTypePacket.class); + this.registerPacket(ProtocolInfo.FILTER_TEXT_PACKET, FilterTextPacket.class); } } diff --git a/src/main/java/cn/nukkit/network/RakNetInterface.java b/src/main/java/cn/nukkit/network/RakNetInterface.java index 1bda4d466f8..238bea31618 100644 --- a/src/main/java/cn/nukkit/network/RakNetInterface.java +++ b/src/main/java/cn/nukkit/network/RakNetInterface.java @@ -7,7 +7,9 @@ import cn.nukkit.network.protocol.BatchPacket; import cn.nukkit.network.protocol.DataPacket; import cn.nukkit.network.protocol.ProtocolInfo; +import cn.nukkit.utils.BinaryStream; import cn.nukkit.utils.Utils; +import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.nukkitx.network.raknet.*; import com.nukkitx.network.util.DisconnectReason; @@ -15,17 +17,23 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.socket.DatagramPacket; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.FastThreadLocal; +import io.netty.util.concurrent.ScheduledFuture; +import io.netty.util.internal.PlatformDependent; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.apache.logging.log4j.message.FormattedMessage; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.ProtocolException; import java.nio.charset.StandardCharsets; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; /** @@ -41,7 +49,19 @@ public class RakNetInterface implements RakNetServerListener, AdvancedSourceInte private final RakNetServer raknet; - private Set sessionListeners = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Map sessions = new HashMap<>(); + + private final Queue sessionCreationQueue = PlatformDependent.newMpscQueue(); + + + private final Set> tickFutures = new HashSet<>(); + + private final FastThreadLocal> sessionsToTick = new FastThreadLocal>() { + @Override + protected Set initialValue() { + return Collections.newSetFromMap(new IdentityHashMap<>()); + } + }; private byte[] advertisement; @@ -53,6 +73,14 @@ public RakNetInterface(Server server) { this.raknet = new RakNetServer(bindAddress, Runtime.getRuntime().availableProcessors()); this.raknet.bind().join(); this.raknet.setListener(this); + + for (EventExecutor executor : this.raknet.getBootstrap().config().group()) { + this.tickFutures.add(executor.scheduleAtFixedRate(() -> { + for (NukkitRakNetSession session : sessionsToTick.get()) { + session.sendOutbound(); + } + }, 0, 50, TimeUnit.MILLISECONDS)); + } } @Override @@ -62,18 +90,41 @@ public void setNetwork(Network network) { @Override public boolean process() { - Iterator iterator = this.sessionListeners.iterator(); + NukkitRakNetSession session; + while ((session = this.sessionCreationQueue.poll()) != null) { + InetSocketAddress address = session.raknet.getAddress(); + PlayerCreationEvent ev = new PlayerCreationEvent(this, Player.class, Player.class, null, address); + this.server.getPluginManager().callEvent(ev); + Class clazz = ev.getPlayerClass(); + + try { + Constructor constructor = clazz.getConstructor(SourceInterface.class, Long.class, InetSocketAddress.class); + Player player = constructor.newInstance(this, ev.getClientId(), ev.getSocketAddress()); + this.server.addPlayer(address, player); + session.player = player; + this.sessions.put(address, session); + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { + Server.getInstance().getLogger().logException(e); + } + } + + Iterator iterator = this.sessions.values().iterator(); while (iterator.hasNext()) { - NukkitSessionListener listener = iterator.next(); - Player player = listener.player; - if (listener.disconnectReason != null) { - player.close(player.getLeaveMessage(), listener.disconnectReason, false); + NukkitRakNetSession nukkitSession = iterator.next(); + Player player = nukkitSession.player; + if (nukkitSession.disconnectReason != null) { + player.close(player.getLeaveMessage(), nukkitSession.disconnectReason, false); iterator.remove(); continue; } DataPacket packet; - while ((packet = listener.packets.poll()) != null) { - listener.player.handleDataPacket(packet); + while ((packet = nukkitSession.inbound.poll()) != null) { + try { + nukkitSession.player.handleDataPacket(packet); + } catch (Exception e) { + log.error(new FormattedMessage("An error occurred whilst handling {} for {}", + new Object[]{packet.getClass().getSimpleName(), nukkitSession.player.getName()}, e)); + } } } return true; @@ -100,11 +151,13 @@ public void close(Player player, String reason) { @Override public void shutdown() { + this.tickFutures.forEach(future -> future.cancel(false)); this.raknet.close(); } @Override public void emergencyShutdown() { + this.tickFutures.forEach(future -> future.cancel(true)); this.raknet.close(); } @@ -161,30 +214,13 @@ public Integer putPacket(Player player, DataPacket packet, boolean needACK) { @Override public Integer putPacket(Player player, DataPacket packet, boolean needACK, boolean immediate) { - RakNetServerSession session = this.raknet.getSession(player.getSocketAddress()); - if (session == null) { - return null; - } + NukkitRakNetSession session = this.sessions.get(player.getSocketAddress()); - byte[] buffer; - if (packet.pid() == ProtocolInfo.BATCH_PACKET) { - buffer = ((BatchPacket) packet).payload; - if (buffer == null) { - return null; - } - } else { - this.server.batchPackets(new Player[]{player}, new DataPacket[]{packet}, true); - return null; + if (session != null) { + packet.tryEncode(); + session.outbound.offer(packet); } - ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer(1 + buffer.length); - byteBuf.writeByte(0xfe); - byteBuf.writeBytes(buffer); - byteBuf.readerIndex(0); - - session.send(byteBuf, immediate ? RakNetPriority.IMMEDIATE : RakNetPriority.MEDIUM, packet.reliability, - packet.getChannel()); - return null; } @@ -200,20 +236,15 @@ public byte[] onQuery(InetSocketAddress inetSocketAddress) { @Override public void onSessionCreation(RakNetServerSession session) { - PlayerCreationEvent ev = new PlayerCreationEvent(this, Player.class, Player.class, null, session.getAddress()); - this.server.getPluginManager().callEvent(ev); - Class clazz = ev.getPlayerClass(); - - try { - Constructor constructor = clazz.getConstructor(SourceInterface.class, Long.class, InetSocketAddress.class); - Player player = (Player) constructor.newInstance(this, ev.getClientId(), ev.getSocketAddress()); - this.server.addPlayer(session.getAddress(), player); - NukkitSessionListener listener = new NukkitSessionListener(player); - this.sessionListeners.add(listener); - session.setListener(listener); - } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { - Server.getInstance().getLogger().logException(e); - } + NukkitRakNetSession nukkitSession = new NukkitRakNetSession(session); + session.setListener(nukkitSession); + this.sessionCreationQueue.offer(nukkitSession); + + // We need to make sure this gets put into the correct thread local hashmap + // for ticking or race conditions will occur. + session.getEventLoop().execute(() -> { + this.sessionsToTick.get().add(nukkitSession); + }); } @Override @@ -222,22 +253,23 @@ public void onUnhandledDatagram(ChannelHandlerContext ctx, DatagramPacket datagr } @RequiredArgsConstructor - private class NukkitSessionListener implements RakNetSessionListener { - private final Player player; - private final Queue packets = new ConcurrentLinkedQueue<>(); + private class NukkitRakNetSession implements RakNetSessionListener { + private final RakNetServerSession raknet; + private final Queue inbound = PlatformDependent.newSpscQueue(); + private final Queue outbound = PlatformDependent.newMpscQueue(); private String disconnectReason = null; + private Player player; @Override public void onSessionChangeState(RakNetState rakNetState) { - } @Override public void onDisconnect(DisconnectReason disconnectReason) { if (disconnectReason == DisconnectReason.TIMED_OUT) { - this.disconnectReason = "Timed out"; + this.disconnect("Timed out"); } else { - this.disconnectReason = "Disconnected from Server"; + this.disconnect("Disconnected from Server"); } } @@ -246,17 +278,15 @@ public void onEncapsulated(EncapsulatedPacket packet) { ByteBuf buffer = packet.getBuffer(); short packetId = buffer.readUnsignedByte(); if (packetId == 0xfe) { - DataPacket batchPacket = RakNetInterface.this.network.getPacket(ProtocolInfo.BATCH_PACKET); - if (batchPacket == null) { - return; - } - byte[] packetBuffer = new byte[buffer.readableBytes()]; buffer.readBytes(packetBuffer); - batchPacket.setBuffer(packetBuffer); - batchPacket.decode(); - packets.offer(batchPacket); + try { + RakNetInterface.this.network.processBatch(packetBuffer, this.inbound); + } catch (ProtocolException e) { + this.disconnect("Sent malformed packet"); + log.error("Unable to process batch packet", e); + } } } @@ -264,5 +294,55 @@ public void onEncapsulated(EncapsulatedPacket packet) { public void onDirect(ByteBuf byteBuf) { // We don't allow any direct packets so ignore. } + + private void disconnect(String message) { + this.disconnectReason = message; + RakNetInterface.this.sessionsToTick.get().remove(this); + } + + private void sendOutbound() { + List toBatch = new ObjectArrayList<>(); + DataPacket packet; + while ((packet = this.outbound.poll()) != null) { + if (packet.pid() == ProtocolInfo.BATCH_PACKET) { + if (!toBatch.isEmpty()) { + this.sendPackets(toBatch); + toBatch.clear(); + } + + this.sendPacket(((BatchPacket) packet).payload); + } else { + toBatch.add(packet); + } + } + + if (!toBatch.isEmpty()) { + this.sendPackets(toBatch); + } + } + + private void sendPackets(Collection packets) { + BinaryStream batched = new BinaryStream(); + for (DataPacket packet : packets) { + Preconditions.checkArgument(!(packet instanceof BatchPacket), "Cannot batch BatchPacket"); + Preconditions.checkState(packet.isEncoded, "Packet should have already been encoded"); + byte[] buf = packet.getBuffer(); + batched.putUnsignedVarInt(buf.length); + batched.put(buf); + } + + try { + this.sendPacket(Network.deflateRaw(batched.getBuffer(), network.getServer().networkCompressionLevel)); + } catch (IOException e) { + log.error("Unable to compress batched packets", e); + } + } + + private void sendPacket(byte[] payload) { + ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer(1 + payload.length); + byteBuf.writeByte(0xfe); + byteBuf.writeBytes(payload); + this.raknet.send(byteBuf); + } } } diff --git a/src/main/java/cn/nukkit/network/protocol/AnvilDamagePacket.java b/src/main/java/cn/nukkit/network/protocol/AnvilDamagePacket.java new file mode 100644 index 00000000000..f03d8b886f0 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/AnvilDamagePacket.java @@ -0,0 +1,32 @@ +package cn.nukkit.network.protocol; + +import cn.nukkit.math.BlockVector3; +import lombok.ToString; + +@ToString +public class AnvilDamagePacket extends DataPacket { + + public int damage; + public int x; + public int y; + public int z; + + @Override + public byte pid() { + return ProtocolInfo.ANVIL_DAMAGE_PACKET; + } + + @Override + public void decode() { + this.damage = this.getByte(); + BlockVector3 vec = this.getBlockVector3(); + this.x = vec.x; + this.y = vec.y; + this.z = vec.z; + } + + @Override + public void encode() { + + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java b/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java index 3044535699a..8e59f8fa958 100644 --- a/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java @@ -26,23 +26,20 @@ public class AvailableCommandsPacket extends DataPacket { public static final int ARG_FLAG_SOFT_ENUM = 0x4000000; public static final int ARG_TYPE_INT = 1; - public static final int ARG_TYPE_FLOAT = 2; - public static final int ARG_TYPE_VALUE = 3; - public static final int ARG_TYPE_WILDCARD_INT = 4; - public static final int ARG_TYPE_OPERATOR = 5; - public static final int ARG_TYPE_TARGET = 6; - public static final int ARG_TYPE_WILDCARD_TARGET = 7; - - public static final int ARG_TYPE_FILE_PATH = 14; - - public static final int ARG_TYPE_STRING = 29; - public static final int ARG_TYPE_BLOCK_POSITION = 37; - public static final int ARG_TYPE_POSITION = 38; - - public static final int ARG_TYPE_MESSAGE = 41; - public static final int ARG_TYPE_RAWTEXT = 43; - public static final int ARG_TYPE_JSON = 47; - public static final int ARG_TYPE_COMMAND = 54; + public static final int ARG_TYPE_FLOAT = 3; + public static final int ARG_TYPE_VALUE = 4; + public static final int ARG_TYPE_WILDCARD_INT = 5; + public static final int ARG_TYPE_OPERATOR = 6; + public static final int ARG_TYPE_TARGET = 7; + public static final int ARG_TYPE_WILDCARD_TARGET = 8; + public static final int ARG_TYPE_FILE_PATH = 16; + public static final int ARG_TYPE_STRING = 32; + public static final int ARG_TYPE_BLOCK_POSITION = 40; + public static final int ARG_TYPE_POSITION = 41; + public static final int ARG_TYPE_MESSAGE = 44; + public static final int ARG_TYPE_RAWTEXT = 46; + public static final int ARG_TYPE_JSON = 50; + public static final int ARG_TYPE_COMMAND = 63; public Map commands; public final Map> softEnums = new HashMap<>(); diff --git a/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java b/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java index d168053c818..6d8d96c0a80 100644 --- a/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java @@ -16,15 +16,18 @@ public byte pid() { } public int windowId; + public boolean wasServerInitiated = true; @Override public void decode() { this.windowId = (byte) this.getByte(); + this.wasServerInitiated = this.getBoolean(); } @Override public void encode() { this.reset(); this.putByte((byte) this.windowId); + this.putBoolean(this.wasServerInitiated); } } diff --git a/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java b/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java index e06922caf9c..cc1ef660e1b 100644 --- a/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java @@ -41,6 +41,10 @@ public void addFurnaceRecipe(FurnaceRecipe... recipe) { Collections.addAll(entries, recipe); } + public void addMultiRecipe(MultiRecipe... recipe) { + Collections.addAll(entries, recipe); + } + public void addBrewingRecipe(BrewingRecipe... recipe) { Collections.addAll(brewingEntries, recipe); } @@ -65,6 +69,8 @@ public void encode() { this.reset(); this.putUnsignedVarInt(entries.size()); + int recipeNetworkId = 1; + for (Recipe recipe : entries) { this.putVarInt(recipe.getType().ordinal()); switch (recipe.getType()) { @@ -81,7 +87,7 @@ public void encode() { this.putUUID(shapeless.getId()); this.putString(CRAFTING_TAG_CRAFTING_TABLE); this.putVarInt(shapeless.getPriority()); - this.putUnsignedVarInt(0); + this.putUnsignedVarInt(recipeNetworkId++); break; case SHAPED: ShapedRecipe shaped = (ShapedRecipe) recipe; @@ -104,7 +110,7 @@ public void encode() { this.putUUID(shaped.getId()); this.putString(CRAFTING_TAG_CRAFTING_TABLE); this.putVarInt(shaped.getPriority()); - this.putUnsignedVarInt(0); + this.putUnsignedVarInt(recipeNetworkId++); break; case FURNACE: case FURNACE_DATA: @@ -117,6 +123,10 @@ public void encode() { this.putSlot(furnace.getResult()); this.putString(CRAFTING_TAG_FURNACE); break; + case MULTI: + this.putUUID(((MultiRecipe) recipe).getId()); + this.putUnsignedVarInt(recipeNetworkId++); + break; } } diff --git a/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java b/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java index 4daedf84d16..2509de46f39 100644 --- a/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java @@ -22,7 +22,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarInt(entries.length); + this.putUnsignedVarInt(entries.length); for (int i = 0; i < entries.length; i++) { this.putUnsignedVarInt(i + 1); this.putSlot(entries[i]); diff --git a/src/main/java/cn/nukkit/network/protocol/DataPacket.java b/src/main/java/cn/nukkit/network/protocol/DataPacket.java index 3a24945a4a8..3969ca1cc19 100644 --- a/src/main/java/cn/nukkit/network/protocol/DataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/DataPacket.java @@ -12,7 +12,7 @@ */ public abstract class DataPacket extends BinaryStream implements Cloneable { - public boolean isEncoded = false; + public volatile boolean isEncoded = false; private int channel = 0; public RakNetReliability reliability = RakNetReliability.RELIABLE_ORDERED; @@ -23,6 +23,13 @@ public abstract class DataPacket extends BinaryStream implements Cloneable { public abstract void encode(); + public final void tryEncode() { + if (!this.isEncoded) { + this.isEncoded = true; + this.encode(); + } + } + @Override public DataPacket reset() { super.reset(); @@ -64,9 +71,8 @@ public BatchPacket compress(int level) { byte[] buf = getBuffer(); batchPayload[0] = Binary.writeUnsignedVarInt(buf.length); batchPayload[1] = buf; - byte[] data = Binary.appendBytes(batchPayload); try { - batch.payload = Network.deflateRaw(data, level); + batch.payload = Network.deflateRaw(batchPayload, level); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/cn/nukkit/network/protocol/EmoteListPacket.java b/src/main/java/cn/nukkit/network/protocol/EmoteListPacket.java index 40d938cb4e9..ba49ea4c280 100644 --- a/src/main/java/cn/nukkit/network/protocol/EmoteListPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/EmoteListPacket.java @@ -20,7 +20,7 @@ public byte pid() { @Override public void decode() { - this.runtimeId = this.getEntityUniqueId(); + this.runtimeId = this.getEntityRuntimeId(); int size = (int) this.getUnsignedVarInt(); for (int i = 0; i < size; i++) { UUID id = this.getUUID(); @@ -31,7 +31,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putEntityUniqueId(runtimeId); + this.putEntityRuntimeId(runtimeId); this.putUnsignedVarInt(pieceIds.size()); for (UUID id : pieceIds) { this.putUUID(id); diff --git a/src/main/java/cn/nukkit/network/protocol/EmotePacket.java b/src/main/java/cn/nukkit/network/protocol/EmotePacket.java index 6cbfbd49b29..13d9b674647 100644 --- a/src/main/java/cn/nukkit/network/protocol/EmotePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/EmotePacket.java @@ -16,7 +16,7 @@ public byte pid() { @Override public void decode() { - this.runtimeId = this.getUnsignedVarLong(); + this.runtimeId = this.getEntityRuntimeId(); this.emoteID = this.getString(); this.flags = (byte) this.getByte(); } @@ -24,7 +24,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putUnsignedVarLong(this.runtimeId); + this.putEntityRuntimeId(this.runtimeId); this.putString(this.emoteID); this.putByte(flags); } diff --git a/src/main/java/cn/nukkit/network/protocol/FilterTextPacket.java b/src/main/java/cn/nukkit/network/protocol/FilterTextPacket.java new file mode 100644 index 00000000000..0d73c3b01e1 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/FilterTextPacket.java @@ -0,0 +1,30 @@ +package cn.nukkit.network.protocol; + +import lombok.ToString; + +@ToString +public class FilterTextPacket extends DataPacket { + + public static final byte NETWORK_ID = ProtocolInfo.FILTER_TEXT_PACKET; + + public String text; + public boolean fromServer; + + @Override + public byte pid() { + return NETWORK_ID; + } + + @Override + public void decode() { + this.text = this.getString(); + this.fromServer = this.getBoolean(); + } + + @Override + public void encode() { + this.reset(); + this.putString(this.text); + this.putBoolean(this.fromServer); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java b/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java index b9ac3a30bd1..b2403327447 100644 --- a/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java @@ -48,6 +48,7 @@ public class InventoryTransactionPacket extends DataPacket { */ public boolean isCraftingPart = false; public boolean isEnchantingPart = false; + public boolean isRepairItemPart = false; @Override public byte pid() { diff --git a/src/main/java/cn/nukkit/network/protocol/LoginPacket.java b/src/main/java/cn/nukkit/network/protocol/LoginPacket.java index 61cad3ea87f..3312461a8ac 100644 --- a/src/main/java/cn/nukkit/network/protocol/LoginPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/LoginPacket.java @@ -82,6 +82,9 @@ private void decodeSkinData() { if (skinToken.has("SkinId")) { skin.setSkinId(skinToken.get("SkinId").getAsString()); } + if (skinToken.has("PlayFabID")) { + skin.setPlayFabId(skinToken.get("PlayFabID").getAsString()); + } if (skinToken.has("CapeId")) { skin.setCapeId(skinToken.get("CapeId").getAsString()); } @@ -150,7 +153,8 @@ private static SkinAnimation getAnimation(JsonObject element) { byte[] data = Base64.getDecoder().decode(element.get("Image").getAsString()); int width = element.get("ImageWidth").getAsInt(); int height = element.get("ImageHeight").getAsInt(); - return new SkinAnimation(new SerializedImage(width, height, data), type, frames); + int expression = element.get("AnimationExpression").getAsInt(); + return new SkinAnimation(new SerializedImage(width, height, data), type, frames, expression); } private static SerializedImage getImage(JsonObject token, String name) { diff --git a/src/main/java/cn/nukkit/network/protocol/MoveEntityDeltaPacket.java b/src/main/java/cn/nukkit/network/protocol/MoveEntityDeltaPacket.java index ea6a56482bd..1b97f5eae4e 100644 --- a/src/main/java/cn/nukkit/network/protocol/MoveEntityDeltaPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MoveEntityDeltaPacket.java @@ -14,9 +14,9 @@ public class MoveEntityDeltaPacket extends DataPacket { public static final int FLAG_HAS_PITCH = 0b100000; public int flags = 0; - public int xDelta = 0; - public int yDelta = 0; - public int zDelta = 0; + public float x = 0; + public float y = 0; + public float z = 0; public double yawDelta = 0; public double headYawDelta = 0; public double pitchDelta = 0; @@ -29,9 +29,9 @@ public byte pid() { @Override public void decode() { this.flags = this.getByte(); - this.xDelta = getCoordinate(FLAG_HAS_X); - this.yDelta = getCoordinate(FLAG_HAS_Y); - this.zDelta = getCoordinate(FLAG_HAS_Z); + this.x = getCoordinate(FLAG_HAS_X); + this.y = getCoordinate(FLAG_HAS_Y); + this.z = getCoordinate(FLAG_HAS_Z); this.yawDelta = getRotation(FLAG_HAS_YAW); this.headYawDelta = getRotation(FLAG_HAS_HEAD_YAW); this.pitchDelta = getRotation(FLAG_HAS_PITCH); @@ -40,17 +40,17 @@ public void decode() { @Override public void encode() { this.putByte((byte) flags); - putCoordinate(FLAG_HAS_X, this.xDelta); - putCoordinate(FLAG_HAS_Y, this.yDelta); - putCoordinate(FLAG_HAS_Z, this.zDelta); + putCoordinate(FLAG_HAS_X, this.x); + putCoordinate(FLAG_HAS_Y, this.y); + putCoordinate(FLAG_HAS_Z, this.z); putRotation(FLAG_HAS_YAW, this.yawDelta); putRotation(FLAG_HAS_HEAD_YAW, this.headYawDelta); putRotation(FLAG_HAS_PITCH, this.pitchDelta); } - private int getCoordinate(int flag) { + private float getCoordinate(int flag) { if ((flags & flag) != 0) { - return this.getVarInt(); + return this.getLFloat(); } return 0; } @@ -62,9 +62,9 @@ private double getRotation(int flag) { return 0d; } - private void putCoordinate(int flag, int value) { + private void putCoordinate(int flag, float value) { if ((flags & flag) != 0) { - this.putVarInt(value); + this.putLFloat(value); } } diff --git a/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java b/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java index bb09c316be9..e8d4a50c9fc 100644 --- a/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java @@ -28,6 +28,7 @@ public class MovePlayerPacket extends DataPacket { public long ridingEid; public int int1 = 0; public int int2 = 0; + public long frame; @Override public void decode() { @@ -46,6 +47,7 @@ public void decode() { this.int1 = this.getLInt(); this.int2 = this.getLInt(); } + this.frame = this.getUnsignedVarLong(); } @Override @@ -63,6 +65,7 @@ public void encode() { this.putLInt(this.int1); this.putLInt(this.int2); } + this.putUnsignedVarLong(this.frame); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java index 653810e6115..b2694be4fbf 100644 --- a/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java @@ -44,8 +44,8 @@ public void encode() { } if (type == TYPE_ADD) { - for (Entry entry : this.entries) { // Biggest wtf - this.putBoolean(entry.skin.isTrusted()); + for (Entry entry : this.entries) { + this.putBoolean(true); } } } diff --git a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java index 3ac5dab33ed..8160e816cfd 100644 --- a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java +++ b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java @@ -14,12 +14,12 @@ public interface ProtocolInfo { * Actual Minecraft: PE protocol version */ @SuppressWarnings("UnnecessaryBoxing") - int CURRENT_PROTOCOL = Integer.valueOf("408"); // DO NOT REMOVE BOXING + int CURRENT_PROTOCOL = Integer.valueOf("428"); // DO NOT REMOVE BOXING List SUPPORTED_PROTOCOLS = Ints.asList(CURRENT_PROTOCOL); - String MINECRAFT_VERSION = "v1.16.20"; - String MINECRAFT_VERSION_NETWORK = "1.16.20"; + String MINECRAFT_VERSION = "v1.16.210"; + String MINECRAFT_VERSION_NETWORK = "1.16.210"; byte LOGIN_PACKET = 0x01; byte PLAY_STATUS_PACKET = 0x02; @@ -171,6 +171,14 @@ public interface ProtocolInfo { byte POS_TRACKING_CLIENT_REQUEST_PACKET = (byte) 0x9a; byte DEBUG_INFO_PACKET = (byte) 0x9b; byte PACKET_VIOLATION_WARNING_PACKET = (byte) 0x9c; + byte MOTION_PREDICTION_HINTS_PACKET = (byte) 0x9d; + byte ANIMATE_ENTITY_PACKET = (byte) 0x9e; + byte CAMERA_SHAKE_PACKET = (byte) 0x9f; + byte PLAYER_FOG_PACKET = (byte) 0xa0; + byte CORRECT_PLAYER_MOVE_PREDICTION_PACKET = (byte) 0xa1; + byte ITEM_COMPONENT_PACKET = (byte) 0xa2; + byte FILTER_TEXT_PACKET = (byte) 0xa3; + byte CLIENTBOUND_DEBUG_RENDERER_PACKET = (byte) 0xa4; byte BATCH_PACKET = (byte) 0xff; } diff --git a/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java b/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java index 40a7b5b30bd..7a601abcd95 100644 --- a/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ResourcePackStackPacket.java @@ -37,8 +37,10 @@ public void encode() { this.putString(entry.getPackVersion()); this.putString(""); //TODO: subpack name } - this.putBoolean(this.isExperimental); + this.putString(this.gameVersion); + this.putLInt(0); // Experiments length + this.putBoolean(false); // Were experiments previously toggled } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java b/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java index 9bce9daee48..fd868b369ce 100644 --- a/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java @@ -24,8 +24,8 @@ public void encode() { this.putBoolean(this.mustAccept); this.putBoolean(this.scripting); - encodePacks(this.resourcePackEntries); encodePacks(this.behaviourPackEntries); + encodePacks(this.resourcePackEntries); } private void encodePacks(ResourcePack[] packs) { @@ -38,6 +38,7 @@ private void encodePacks(ResourcePack[] packs) { this.putString(""); // sub-pack name this.putString(""); // content identity this.putBoolean(false); // scripting + this.putBoolean(false); // raytracing capable } } diff --git a/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java b/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java index a04bc6ba07b..88a36def1d9 100644 --- a/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java @@ -19,6 +19,7 @@ public byte pid() { public long eid; public EntityMetadata metadata; + public long frame; @Override public void decode() { @@ -30,5 +31,6 @@ public void encode() { this.reset(); this.putEntityRuntimeId(this.eid); this.put(Binary.writeMetadata(this.metadata)); + this.putUnsignedVarLong(this.frame); } } diff --git a/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java b/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java index 345f0b118d7..a1bcb233445 100644 --- a/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java @@ -1,26 +1,15 @@ package cn.nukkit.network.protocol; -import cn.nukkit.Server; +import cn.nukkit.item.RuntimeItems; import cn.nukkit.level.GameRules; -import cn.nukkit.level.GlobalBlockPalette; -import cn.nukkit.utils.BinaryStream; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; import lombok.ToString; import lombok.extern.log4j.Log4j2; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.util.Collection; - /** * Created on 15-10-13. */ @Log4j2 -@ToString(exclude = {"blockPalette"}) +@ToString public class StartGamePacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.START_GAME_PACKET; @@ -31,31 +20,6 @@ public class StartGamePacket extends DataPacket { public static final int GAME_PUBLISH_SETTING_FRIENDS_OF_FRIENDS = 3; public static final int GAME_PUBLISH_SETTING_PUBLIC = 4; - private static final byte[] ITEM_DATA_PALETTE; - - static { - InputStream stream = Server.class.getClassLoader().getResourceAsStream("runtime_item_ids.json"); - if (stream == null) { - throw new AssertionError("Unable to locate RuntimeID table"); - } - Reader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); - - Gson gson = new Gson(); - Type collectionType = new TypeToken>() { - }.getType(); - Collection entries = gson.fromJson(reader, collectionType); - BinaryStream paletteBuffer = new BinaryStream(); - - paletteBuffer.putUnsignedVarInt(entries.size()); - - for (ItemData data : entries) { - paletteBuffer.putString(data.name); - paletteBuffer.putLShort(data.id); - } - - ITEM_DATA_PALETTE = paletteBuffer.getBuffer(); - } - @Override public byte pid() { return NETWORK_ID; @@ -132,8 +96,8 @@ public void encode() { this.putLFloat(this.pitch); this.putVarInt(this.seed); - this.putLShort(0x00); // SpawnBiomeType - this.putString(""); // UserDefinedBiomeName + this.putLShort(0x00); // SpawnBiomeType - Default + this.putString("plains"); // UserDefinedBiomeName this.putVarInt(this.dimension); this.putVarInt(this.generator); this.putVarInt(this.worldGamemode); @@ -143,7 +107,7 @@ public void encode() { this.putVarInt(this.dayCycleStopTime); this.putVarInt(this.eduEditionOffer); this.putBoolean(this.hasEduFeaturesEnabled); - this.putString(""); // UnknownString0 + this.putString(""); // Education Edition Product ID this.putLFloat(this.rainLevel); this.putLFloat(this.lightningLevel); this.putBoolean(this.hasConfirmedPlatformLockedContent); @@ -154,6 +118,8 @@ public void encode() { this.putBoolean(this.commandsEnabled); this.putBoolean(this.isTexturePacksRequired); this.putGameRules(this.gameRules); + this.putLInt(0); // Experiment count + this.putBoolean(false); // Were experiments previously toggled this.putBoolean(this.bonusChest); this.putBoolean(this.hasStartWithMapEnabled); this.putVarInt(this.permissionLevel); @@ -166,26 +132,23 @@ public void encode() { this.putBoolean(this.isWorldTemplateOptionLocked); this.putBoolean(this.isOnlySpawningV1Villagers); this.putString(this.vanillaVersion); - this.putLInt(0); // UnknownInt0 - this.putLInt(0); // UnknownInt1 - this.putBoolean(false); - this.putBoolean(false); + this.putLInt(16); // Limited world width + this.putLInt(16); // Limited world height + this.putBoolean(false); // Nether type + this.putBoolean(false); // Experimental Gameplay this.putString(this.levelId); this.putString(this.worldName); this.putString(this.premiumWorldTemplateId); this.putBoolean(this.isTrial); - this.putBoolean(this.isMovementServerAuthoritative); + this.putUnsignedVarInt(this.isMovementServerAuthoritative ? 1 : 0); // 2 - rewind + this.putVarInt(0); // RewindHistorySize + this.putBoolean(false); // isServerAuthoritativeBlockBreaking this.putLLong(this.currentTick); this.putVarInt(this.enchantmentSeed); - this.put(GlobalBlockPalette.BLOCK_PALETTE); - this.put(ITEM_DATA_PALETTE); + this.putUnsignedVarInt(0); // Custom blocks + this.put(RuntimeItems.getRuntimeMapping().getItemDataPalette()); this.putString(this.multiplayerCorrelationId); this.putBoolean(this.isInventoryServerAuthoritative); } - - private static class ItemData { - private String name; - private int id; - } } diff --git a/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java b/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java index 07668b6f96a..88ed705b909 100644 --- a/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java @@ -13,6 +13,7 @@ public class UpdateAttributesPacket extends DataPacket { public Attribute[] entries; public long entityId; + public long frame; @Override public byte pid() { @@ -40,6 +41,7 @@ public void encode() { this.putString(entry.getName()); } } + this.putUnsignedVarInt(this.frame); } } 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 b9bb4b7ee03..36b132c4af5 100644 --- a/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java +++ b/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java @@ -5,6 +5,7 @@ import cn.nukkit.inventory.BeaconInventory; import cn.nukkit.inventory.EnchantInventory; import cn.nukkit.inventory.Inventory; +import cn.nukkit.inventory.PlayerUIComponent; import cn.nukkit.inventory.transaction.action.*; import cn.nukkit.item.Item; import cn.nukkit.network.protocol.InventoryTransactionPacket; @@ -95,6 +96,11 @@ public NetworkInventoryAction read(InventoryTransactionPacket packet) { case SOURCE_TYPE_ENCHANT_MATERIAL: packet.isEnchantingPart = true; break; + case SOURCE_TYPE_ANVIL_INPUT: + case SOURCE_TYPE_ANVIL_MATERIAL: + case SOURCE_TYPE_ANVIL_RESULT: + packet.isRepairItemPart = true; + break; } break; } @@ -144,21 +150,46 @@ public InventoryAction createInventoryAction(Player player) { } // 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; + switch (this.inventorySlot) { + case PlayerUIComponent.CREATED_ITEM_OUTPUT_UI_SLOT: + if (player.getWindowById(Player.ANVIL_WINDOW_ID) != null) { + this.windowId = Player.ANVIL_WINDOW_ID; + this.inventorySlot = 2; + } + break; + case 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 + break; + case 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; + break; + case AnvilInventory.ANVIL_INPUT_UI_SLOT: + if (player.getWindowById(Player.ANVIL_WINDOW_ID) == null) { + player.getServer().getLogger().error("Player " + player.getName() + " does not have anvil window open"); + return null; + } + this.windowId = Player.ANVIL_WINDOW_ID; + this.inventorySlot = 0; + break; + case AnvilInventory.ANVIL_MATERIAL_UI_SLOT: + if (player.getWindowById(Player.ANVIL_WINDOW_ID) == null) { + player.getServer().getLogger().error("Player " + player.getName() + " does not have anvil window open"); + return null; + } + this.windowId = Player.ANVIL_WINDOW_ID; + this.inventorySlot = 1; + break; } } @@ -223,28 +254,12 @@ public InventoryAction createInventoryAction(Player player) { switch (this.windowId) { case SOURCE_TYPE_ANVIL_INPUT: - //System.out.println("action input"); - this.inventorySlot = 0; - return new SlotChangeAction(anvil, this.inventorySlot, this.oldItem, this.newItem); case SOURCE_TYPE_ANVIL_MATERIAL: - //System.out.println("material"); - this.inventorySlot = 1; - return new SlotChangeAction(anvil, this.inventorySlot, this.oldItem, this.newItem); - case SOURCE_TYPE_ANVIL_OUTPUT: - //System.out.println("action output"); - break; case SOURCE_TYPE_ANVIL_RESULT: - this.inventorySlot = 2; - anvil.clear(0); - Item material = anvil.getItem(1); - if (!material.isNull()) { - material.setCount(material.getCount() - 1); - anvil.setItem(1, material); - } - anvil.setItem(2, this.oldItem); - //System.out.println("action result"); - return new SlotChangeAction(anvil, this.inventorySlot, this.oldItem, this.newItem); + return new RepairItemAction(this.oldItem, this.newItem, this.windowId); } + + return new SlotChangeAction(anvil, this.inventorySlot, this.oldItem, this.newItem); } if (this.windowId >= SOURCE_TYPE_ENCHANT_OUTPUT && this.windowId <= SOURCE_TYPE_ENCHANT_INPUT) { diff --git a/src/main/java/cn/nukkit/potion/Effect.java b/src/main/java/cn/nukkit/potion/Effect.java index e7c6a1a1e69..3412ba82234 100644 --- a/src/main/java/cn/nukkit/potion/Effect.java +++ b/src/main/java/cn/nukkit/potion/Effect.java @@ -22,12 +22,16 @@ public class Effect implements Cloneable { public static final int MINING_FATIGUE = 4; public static final int STRENGTH = 5; public static final int HEALING = 6; + public static final int INSTANT_HEALTH = 6; public static final int HARMING = 7; + public static final int INSTANT_DAMAGE = 7; public static final int JUMP = 8; + public static final int JUMP_BOOST = 8; public static final int NAUSEA = 9; public static final int CONFUSION = 9; public static final int REGENERATION = 10; public static final int DAMAGE_RESISTANCE = 11; + public static final int RESISTANCE = 11; public static final int FIRE_RESISTANCE = 12; public static final int WATER_BREATHING = 13; public static final int INVISIBILITY = 14; @@ -42,8 +46,10 @@ public class Effect implements Cloneable { public static final int SATURATION = 23; public static final int LEVITATION = 24; public static final int FATAL_POISON = 25; - public static final int COUNDIT_POWER = 26; + public static final int CONDUIT_POWER = 26; public static final int SLOW_FALLING = 27; + public static final int BAD_OMEN = 28; + public static final int VILLAGE_HERO = 29; protected static Effect[] effects; @@ -76,10 +82,12 @@ public static void init() { effects[Effect.ABSORPTION] = new Effect(Effect.ABSORPTION, "%potion.absorption", 36, 107, 251); effects[Effect.SATURATION] = new Effect(Effect.SATURATION, "%potion.saturation", 255, 0, 255); - effects[Effect.LEVITATION] = new Effect(Effect.LEVITATION, "%potion.levitation", 206, 255, 255); + effects[Effect.LEVITATION] = new Effect(Effect.LEVITATION, "%potion.levitation", 206, 255, 255, true); effects[Effect.FATAL_POISON] = new Effect(Effect.FATAL_POISON, "%potion.poison", 78, 147, 49, true); - effects[Effect.COUNDIT_POWER] = new Effect(Effect.COUNDIT_POWER, "%potion.conduitPower", 29, 194, 209); + effects[Effect.CONDUIT_POWER] = new Effect(Effect.CONDUIT_POWER, "%potion.conduitPower", 29, 194, 209); effects[Effect.SLOW_FALLING] = new Effect(Effect.SLOW_FALLING, "%potion.slowFalling", 206, 255, 255); + effects[Effect.BAD_OMEN] = new Effect(Effect.BAD_OMEN, "%effect.badOmen", 11, 97, 56, true); + effects[Effect.VILLAGE_HERO] = new Effect(Effect.VILLAGE_HERO, "%effect.villageHero", 68, 255, 68); } public static Effect getEffect(int id) { @@ -145,7 +153,7 @@ public int getDuration() { } public boolean isVisible() { - return show; + return show && this.id != VILLAGE_HERO; } public Effect setVisible(boolean visible) { @@ -179,6 +187,7 @@ public boolean canTick() { int interval; switch (this.id) { case Effect.POISON: //POISON + case Effect.FATAL_POISON: if ((interval = (25 >> this.amplifier)) > 0) { return (this.duration % interval) == 0; } @@ -200,7 +209,8 @@ public boolean canTick() { public void applyEffect(Entity entity) { switch (this.id) { case Effect.POISON: //POISON - if (entity.getHealth() > 1) { + case Effect.FATAL_POISON: + if (entity.getHealth() > 1 || this.id == FATAL_POISON) { entity.attack(new EntityDamageEvent(entity, DamageCause.MAGIC, 1)); } break; diff --git a/src/main/java/cn/nukkit/potion/Potion.java b/src/main/java/cn/nukkit/potion/Potion.java index 9a8f6c21959..47dd48d9a21 100644 --- a/src/main/java/cn/nukkit/potion/Potion.java +++ b/src/main/java/cn/nukkit/potion/Potion.java @@ -3,6 +3,7 @@ import cn.nukkit.Player; import cn.nukkit.entity.Entity; import cn.nukkit.entity.EntityLiving; +import cn.nukkit.entity.EntitySmite; import cn.nukkit.event.entity.EntityDamageEvent; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; import cn.nukkit.event.entity.EntityRegainHealthEvent; @@ -201,11 +202,19 @@ public void applyPotion(Entity entity, double health) { switch (this.getId()) { case INSTANT_HEALTH: case INSTANT_HEALTH_II: - entity.heal(new EntityRegainHealthEvent(entity, (float) (health * (double) (4 << (applyEffect.getAmplifier() + 1))), EntityRegainHealthEvent.CAUSE_MAGIC)); + if (entity instanceof EntitySmite) { + entity.attack(new EntityDamageEvent(entity, DamageCause.MAGIC, (float) (health * (double) (6 << (applyEffect.getAmplifier() + 1))))); + } else { + entity.heal(new EntityRegainHealthEvent(entity, (float) (health * (double) (4 << (applyEffect.getAmplifier() + 1))), EntityRegainHealthEvent.CAUSE_MAGIC)); + } break; case HARMING: case HARMING_II: - entity.attack(new EntityDamageEvent(entity, DamageCause.MAGIC, (float) (health * (double) (6 << (applyEffect.getAmplifier() + 1))))); + if (entity instanceof EntitySmite) { + entity.heal(new EntityRegainHealthEvent(entity, (float) (health * (double) (4 << (applyEffect.getAmplifier() + 1))), EntityRegainHealthEvent.CAUSE_MAGIC)); + } else { + entity.attack(new EntityDamageEvent(entity, DamageCause.MAGIC, (float) (health * (double) (6 << (applyEffect.getAmplifier() + 1))))); + } break; default: int duration = (int) ((isSplash() ? health : 1) * (double) applyEffect.getDuration() + 0.5); diff --git a/src/main/java/cn/nukkit/raknet/RakNet.java b/src/main/java/cn/nukkit/raknet/RakNet.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/cn/nukkit/raknet/protocol/Packet.java b/src/main/java/cn/nukkit/raknet/protocol/Packet.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/cn/nukkit/raknet/server/SessionManager.java b/src/main/java/cn/nukkit/raknet/server/SessionManager.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/main/java/cn/nukkit/utils/Binary.java b/src/main/java/cn/nukkit/utils/Binary.java index 93bf08a6e4f..2d5bbef300a 100644 --- a/src/main/java/cn/nukkit/utils/Binary.java +++ b/src/main/java/cn/nukkit/utils/Binary.java @@ -472,11 +472,14 @@ public static byte[] appendBytes(byte[][] bytes) { for (byte[] b : bytes) { length += b.length; } - ByteBuffer buffer = ByteBuffer.allocate(length); + + byte[] appendedBytes = new byte[length]; + int index = 0; for (byte[] b : bytes) { - buffer.put(b); + System.arraycopy(b, 0, appendedBytes, index, b.length); + index += b.length; } - return buffer.array(); + return appendedBytes; } public static byte[] appendBytes(byte byte1, byte[]... bytes2) { @@ -497,12 +500,16 @@ public static byte[] appendBytes(byte[] bytes1, byte[]... bytes2) { for (byte[] bytes : bytes2) { length += bytes.length; } - ByteBuffer buffer = ByteBuffer.allocate(length); - buffer.put(bytes1); - for (byte[] bytes : bytes2) { - buffer.put(bytes); + + byte[] appendedBytes = new byte[length]; + System.arraycopy(bytes1, 0, appendedBytes, 0, bytes1.length); + int index = bytes1.length; + + for (byte[] b : bytes2) { + System.arraycopy(b, 0, appendedBytes, index, b.length); + index += b.length; } - return buffer.array(); + return appendedBytes; } diff --git a/src/main/java/cn/nukkit/utils/BinaryStream.java b/src/main/java/cn/nukkit/utils/BinaryStream.java index 90f3efbdb52..f29d4815b00 100644 --- a/src/main/java/cn/nukkit/utils/BinaryStream.java +++ b/src/main/java/cn/nukkit/utils/BinaryStream.java @@ -4,6 +4,7 @@ import cn.nukkit.entity.data.Skin; import cn.nukkit.item.Item; import cn.nukkit.item.ItemDurable; +import cn.nukkit.item.RuntimeItems; import cn.nukkit.level.GameRule; import cn.nukkit.level.GameRules; import cn.nukkit.math.BlockFace; @@ -260,6 +261,7 @@ public UUID getUUID() { public void putSkin(Skin skin) { this.putString(skin.getSkinId()); + this.putString(skin.getPlayFabId()); this.putString(skin.getSkinResourcePatch()); this.putImage(skin.getSkinData()); @@ -269,6 +271,7 @@ public void putSkin(Skin skin) { this.putImage(animation.image); this.putLInt(animation.type); this.putLFloat(animation.frames); + this.putLInt(animation.expression); } this.putImage(skin.getCapeData()); @@ -306,6 +309,7 @@ public void putSkin(Skin skin) { public Skin getSkin() { Skin skin = new Skin(); skin.setSkinId(this.getString()); + skin.setPlayFabId(this.getString()); skin.setSkinResourcePatch(this.getString()); skin.setSkinData(this.getImage()); @@ -314,7 +318,8 @@ public Skin getSkin() { SerializedImage image = this.getImage(); int type = this.getLInt(); float frames = this.getLFloat(); - skin.getAnimations().add(new SkinAnimation(image, type, frames)); + int expression = this.getLInt(); + skin.getAnimations().add(new SkinAnimation(image, type, frames, expression)); } skin.setCapeData(this.getImage()); @@ -365,14 +370,21 @@ public SerializedImage getImage() { } public Item getSlot() { - int id = this.getVarInt(); - - if (id == 0) { + int networkId = this.getVarInt(); + if (networkId == 0) { return Item.get(0, 0, 0); } + + int legacyFullId = RuntimeItems.getRuntimeMapping().getLegacyFullId(networkId); + int id = RuntimeItems.getId(legacyFullId); + boolean hasData = RuntimeItems.hasData(legacyFullId); + int auxValue = this.getVarInt(); int data = auxValue >> 8; - if (data == Short.MAX_VALUE) { + if (hasData) { + // Swap data using legacy full id + data = RuntimeItems.getData(legacyFullId); + } else if (data == Short.MAX_VALUE) { data = -1; } int cnt = auxValue & 0xff; @@ -458,13 +470,16 @@ public void putSlot(Item item) { return; } - boolean isDurable = item instanceof ItemDurable; - - this.putVarInt(item.getId()); + int networkFullId = RuntimeItems.getRuntimeMapping().getNetworkFullId(item); + int networkId = RuntimeItems.getNetworkId(networkFullId); + boolean clearData = RuntimeItems.hasData(networkFullId); + this.putVarInt(networkId); int auxValue = item.getCount(); + boolean isDurable = item instanceof ItemDurable; if (!isDurable) { - auxValue |= (((item.hasMeta() ? item.getDamage() : -1) & 0x7fff) << 8); + int meta = clearData ? 0 : item.hasMeta() ? item.getDamage() : -1;; + auxValue |= ((meta & 0x7fff) << 8); } this.putVarInt(auxValue); @@ -511,16 +526,23 @@ public void putSlot(Item item) { } public Item getRecipeIngredient() { - int id = this.getVarInt(); - - if (id == 0) { + int networkId = this.getVarInt(); + if (networkId == 0) { return Item.get(0, 0, 0); } + int legacyFullId = RuntimeItems.getRuntimeMapping().getLegacyFullId(networkId); + int id = RuntimeItems.getId(legacyFullId); + boolean hasData = RuntimeItems.hasData(legacyFullId); + int damage = this.getVarInt(); - if (damage == 0x7fff) damage = -1; - int count = this.getVarInt(); + if (hasData) { + damage = RuntimeItems.getData(legacyFullId); + } else if (damage == 0x7fff) { + damage = -1; + } + int count = this.getVarInt(); return Item.get(id, damage, count); } @@ -529,13 +551,15 @@ public void putRecipeIngredient(Item ingredient) { this.putVarInt(0); return; } - this.putVarInt(ingredient.getId()); - int damage; - if (ingredient.hasMeta()) { - damage = ingredient.getDamage(); - } else { - damage = 0x7fff; + + int networkFullId = RuntimeItems.getRuntimeMapping().getNetworkFullId(ingredient); + int networkId = RuntimeItems.getNetworkId(networkFullId); + int damage = ingredient.hasMeta() ? ingredient.getDamage() : 0x7fff; + if (RuntimeItems.hasData(networkFullId)) { + damage = 0; } + + this.putVarInt(networkId); this.putVarInt(damage); this.putVarInt(ingredient.getCount()); } diff --git a/src/main/java/cn/nukkit/utils/DefaultPlayerDataSerializer.java b/src/main/java/cn/nukkit/utils/DefaultPlayerDataSerializer.java index 6ecbd7ecce1..8c9cf530e67 100644 --- a/src/main/java/cn/nukkit/utils/DefaultPlayerDataSerializer.java +++ b/src/main/java/cn/nukkit/utils/DefaultPlayerDataSerializer.java @@ -2,19 +2,25 @@ import cn.nukkit.Server; import com.google.common.base.Preconditions; -import lombok.RequiredArgsConstructor; import java.io.*; import java.util.Optional; import java.util.UUID; -@RequiredArgsConstructor public class DefaultPlayerDataSerializer implements PlayerDataSerializer { - private final Server server; + private String dataPath; + + public DefaultPlayerDataSerializer(Server server) { + this(server.getDataPath()); + } + + public DefaultPlayerDataSerializer(String dataPath) { + this.dataPath = dataPath; + } @Override public Optional read(String name, UUID uuid) throws IOException { - String path = server.getDataPath() + "players/" + name + ".dat"; + String path = dataPath + "players/" + name + ".dat"; File file = new File(path); if (!file.exists()) { return Optional.empty(); @@ -26,7 +32,7 @@ public Optional read(String name, UUID uuid) throws IOException { @Override public OutputStream write(String name, UUID uuid) throws IOException { Preconditions.checkNotNull(name, "name"); - String path = server.getDataPath() + "players/" + name + ".dat"; + String path = dataPath + "players/" + name + ".dat"; File file = new File(path); return new FileOutputStream(file); } diff --git a/src/main/java/cn/nukkit/utils/SkinAnimation.java b/src/main/java/cn/nukkit/utils/SkinAnimation.java index 99db3288b38..236261d21ae 100644 --- a/src/main/java/cn/nukkit/utils/SkinAnimation.java +++ b/src/main/java/cn/nukkit/utils/SkinAnimation.java @@ -7,10 +7,12 @@ public class SkinAnimation { public final SerializedImage image; public final int type; public final float frames; + public final int expression; - public SkinAnimation(SerializedImage image, int type, float frames) { + public SkinAnimation(SerializedImage image, int type, float frames, int expression) { this.image = image; this.type = type; this.frames = frames; + this.expression = expression; } } diff --git a/src/main/resources/biome_definitions.dat b/src/main/resources/biome_definitions.dat index 6d72cc924919f5e018c677c9e3a2def659f07f6d..68705c5d4f97c43f712894fcf5f25cb07bcf5892 100644 GIT binary patch literal 37626 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 literal 8775 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%0DYH@f?K6z3ea!a zDA)$c6rj(tQE+RNOab~U8wIyS$rPZkvQZF3pCWoT5Qrn{83KQ$j2B!1S;t1Y7e`T2 z5J#Y&uu<&Ak%t2G5jKjwIPy?{{=r7E7e^in&^Op9_TtDx0s4iHI&=Q+sa9bfJ9tpZ z-$tRet21H6>{i_A{B<+hd}Lv_exR1}e+)_Kp6A?=@&mh=|8q#px4WvEe?DDJFK3fq znp`crtM=* z{J7%(f5cw_*|a;|Pu`uqqHlCi!?fwR;K7=qmvSC!GxT5Ig$%BILjPZ|qM^rb^K~k= zUwoUWQ^hN^MsfnJeU_rNHnXQ<`?W^s)!C5vvpl{=r6bT9pM+J;*EHCE@ljvXh*$X9 zc6K5$`;8Q>MSV?!?blk=*Q$MAi`I6K*S6iMXziORTJz+!J#4?$qP~_%Yt*9#5&G>E ztwnuJgYDN^)YpKk?-@-Iq14R=TKjH_)}p?q!S-t{>T4SD3axEtixQzflcKe#uW7LT zT7$mU`{eC@UL&{Ze>L4N9{P+`yuxTCCvf#oQ;Y_E&5G^U8uS^fc!kzTPN21~rf4nd zYrxfy>1!JC3a!x`KhWCOQ?wTKH4Sz_U(<+JXzk^m)_y8QYf)b-Tf#kg%~Qf@i7@cB zZ>4B0>T6}S=E-XwtA}h86cM4;D3nS$%7h!+X9(NvS`1 zRrnUzW@gh?;9Hrn!1G}nR^VHiu)qUj8&=?3nXteUWE)nHw*Z#M<-sFm8&=%6JXqiv zvkfcmTOKU1In9O@_bm@rWWUG{r`N0ce)k;v9Pbc2zo?eYx?WG4IlEdcPw`0F{_4l% z0lT&@xUzi+BYy)@YNqB+Tgn+ zVQ}}8t_{#b*^I*7PC5qYnQRzD(ZyhehtZU#?$(52_51*P z0s=2$lN9x$`~m_m$}b@BqWl5^FUl_<@S^+z0xx2X74@S00$3-Vq@G_u;6?cb1YVS1 zK;T6h0o~6D{_yV2Ub_W#yEDsca7U*?7rroupE7unk#nHFh z&E(2iBdcDCP|gSiYhK+q zA0D8FpP3D9M+Ohiqlu(#zPPIX8A>1hsw!8&)^`gyhv48oY7Kap+bjS)$Yg-9SOLw^ zrM6Ot=D_xM3pj^lGI)ySNd7RuW|i9cx`pV8!aUF%*aC0ST-4_@;3=Am`kV$p;d9%L zS9A%s!CN#J^*IfAisqs|2hv4!Q5{6L}sLyEt6h61z4lkMm z+u<#mi~5`fJVkR+p94S4S+&(+YR%gIj~{gqPeKgfneF|=TbBm z^*IfILUY?q^P)MhY2Ko_sLyG@Q#9wvb9<&!Xl}c4UNi?b&VQ$Kn8(BV-0R7FJQ-d0 z&NmGYsQ93o&PVt4Y_{3l-q|A^*3uA^{iaR7|JE*vurCJ-`*y6>AG2cd9h0$IZ_A3s zcTC1=eK0E)-!U1B?opi`#EJ$1@GbBpE4OcXu)wn{JJzsod9c8)c01OvZ+Wo5BW^p^ zzHjyBGmg9kcD38F_VZRM7I?B}$J+O;R4njp&yKb4Td7#!>7E^H*ta|`51#MYv4(xi zg9V=O*|COw%Yy}WwcD|VeanLd9{hiAzuzizru^?A1^+A~g=q_cSOm02DHbv984wE438heQ2jj2^XnayAxM6WnfIcUM zg8LH(1!!whD7YPQP=GEbg@U^e2L)(aQYg6Da8Q6AC53`}3kL;gNm3}dm2gmi4kU$w zI|l~^XgE?RxKVIWfc^q(Eab1TcTP# z-yuYx@5n`D>Oq7E^dPy2O#O%ufquk8#2Y~~`4Q+%auGTG2=pnrh@5@|dX`*7PCo+u z%g^l3cPJmtuXyh@e<^GCD#iKNtLtTRH@|8xI}_2rO~yT#h>}rEo+BtA10|!FTt-kp z>Pbd1`HG-`oRf@Vng|IBNH)nRrrD67fGm@YVww^O3P>->D5iOlpp?if7nYVwQ21xT z)IC`%rWul;fEiLUifNi8C}5hDjAEKA2@04iC8L-oOo9R?OvxywS(BiESyM8KY3d{> zVCs~NVwyh*3Yb52zisvKJp5?qy#21B?!^kmUB~btVfq=TfPr4d3dTK1elXD4Si!g> z$qxql94i=cMZokAC&ZMX>#>5xear&}dLS!U+{Zj%pd+$^#eK{J2Kpl_Sm0xuJy6bb z-^9m2w`2tid@K_f=$))!fsbVZ1D%u=Eby^RV4$zEg2jEz$E$hKZec0BdE zzcBI7f=Q^CfIZAXz(31Gz^>#V;Gbn8FfAL26+i>!wSsB2NC-fu^l@js_043qkoDDz zivN35Ur$)!YK{M+-Pi~0MpENB}iZsxYYsO|;hZJcb^J}D;mKsGGXr49FOsk0^4RpjBX{H53kp|jajWpAm zphyF~t45k>*-xZ_hEyZXw6Z7CK$oeJW?H-xX`pq~NHeX^i8Rm;YNYXLE@6H1R4#sr zH^+VWjFcSr;a6jG+=ovd$#EZk;WWp6_&kps_u)8_3zq>ZYDw)AkU(GyO+r9Y+tHNheId zS|P$nYACGx9_p-KOjnz4bnhl%>iFQ4xQpFTO82YW8!4`@9dY_TD5G0vXZ7dxZV2Ze zR6GzVBp9Q^*){dsRr;IT&_Z%9dY5pk^BlFK8iKaQINQ}%uha{Tjp&}8? zL%Ta|S>kA1-_5R;owLC&URB#`qfNzpUft873d{5NtJhOHIbk;Wd@^IlQhU~aGSukg zA~MxiAp-R^7m=y{3K6Kkxrj{lS%^S=&P8OZ-$DfHcP=7ReHS87-*XXZ>OVV8`@Bzx zwsU2<0_wAe@ma;0BS$&_Fo(agA3_$VE1mw>wQ|RAQO-?6@boToc7}hT+k*vHsL2Sf zfa-3czo@S2<(pB?9#Z&0t7c(SYBp+fpjqlZ0zGag=t2+bnuT7i*(g7&F`xxaiiPc< zW{uezXqM`3z)FQBxB@b|g?>P@Mt-1KP@XMpO3g-X4m3*@C(z?|E-5~nn9J0fjq(G{ zg2L3-?6~(3|9t){Nim)`8AHt=bE;FNgongBx+e ze%EtfZV!ba>zJbsWwi?Yh)|O#j%-skUpWas_m1o;=8DI^+rS;IHk@ zB_Hzk9es*wR)0xLN3_JWtjEoI-FX=y@fE56b~kQ@98g9g2_N)Fmruz?x4E8q`}WXu#@E4jR;vD`>zn zP!1Z@$17;SdQc7;)cq@Hz$JIMli*XAb|LBBbY)*5J2d-5lm4d2q0?uD`)xR<*y?5+ii|$ z<3+A)<3+B3cyR!lB1MFQNO1@^#fS(8G2#$ziVhJDqQfEF6c-{K#Dzn+DI!F;A|e)d zw~IGZIS?q^OQ`N{{_bulggH4J@{m-Me~VOwe-BADIkreuIQEcKlShkG;L%~SrOltK zbu*ugX7#O{N~^Z!-#=u{?yH6!bYixhu6DuK!EsBLTumV z%0U4lNlDSZ7#Jda;qVC%P)dYDKH;GNp{1la;1eDQsxI#E*u4Lq!yurdDOCY|;!B55 zfF}e>ghM{zp#V<@P9N86&w6-uMQ6_C=jsPg`n8J25G00PR47UYo5qDckljPh#$wryfXiy-8+ zsE2hHg#|hTX;=$#NM})Z2Rftt6X=Y>Fu0Ns5{Gpbg#|hTX{xWY=62WExo7f!jszd< z;Z)+7TwMx89RDm>%vFh_#NZLfKl6xVCvuDqB&%QD>B(>DOEL3xU2yl^e?!K>>b|jAHT`K>{j-(s-yVNcS9#k8r{Xs+sUk%2d_V8?C%q|gy&ootU+!p>h**URSKcyRc% zAc!dk2nfgli5f9w0099RAQ8kA{{jNyKM}+f{Q?4_KM}+f`vL-DKM{my>FV$m5HPt< z1mSAz00BkZ4#J@~m`B2dm?#2eh|D5T>}M8%qB^q(6t9^@ph(Ot0>xNn5h!}nMX(bU zj*Fg%GI7xpQ6?^WBFe-?Pehrx=!qy37d;VW;-V*_OkA*q3&%xIM47ngiD0dT%racX_ngBFG;{ptFQ{ zJ5h|HEtC9SK;5NiA125Z(s*!%6%;&bag%;fjhoBM*=GAwjF)3QVBt&oST-8GTu#St z94wqCa+n7&qxZ>FosEE$RuKmgU< zgS)Xs$5VIDWz}x-mym>mf&TsOqr=wj9a*IBdb*S^p`5*(r7X^h(n&_0G^#_dfsE zP}lfbw`+Vmp3J6qvV}w~g5R+^*!$50b2^_;_kftu z>&d+HLcDrRKN=GD(}#qW@3WW3<1(cI;R2;6uf9wfK)68F$;)L5e>I$Iq2qYlI8b zn!H?2{{m$uFPGE5K!wT6<@7I5T)wrRzpnYys6+4ThO>lk?%#txTuNUukY$E2*@n01 z*0_h#a9=OiyUK8WUeP9KN_hI2{%R;a>g1W+QsjZOr;}%LH<1U@nNFU`jYJ+uQ{DBg zj{UsbUH`Ko+wHsTl)unB*b{~R*4>A;;pOGXox6qZdT>$Xw+?}JzmfbPWKxgUjeM9z zE$4q9lGDpA&GNRMJ*=vh??L_;`TD_rFE5uV7=;T2V_q&(&?VBrPUjJp z2mi9-I3|x39FP&SXvgHJf&&s^795j*3J%DBW*n4W$Q$&%tKfjNmj%b^J0RO-!EyQy zNODoO^7A-H)s&EQqZ4Xvo16T`S0bWO+cq@g-J}{mAlwfa4mhAp4Q!0Rg9&TS4|C z%L4)qFb_bcSJPTffmAP9{H5f4yJ{4=VfWd6v#jUWB~JqXue&5|tjd*bU6d=JE~e6Q zXsY!lB%m;+l9>7|Apw;!m4xHjp%YLVQ%N|W9VDPOrjl?(J4irr>~_J6%|^?q=h^2U zA)tdn+&ut&F`d6DctFYj(B)`DSgveCSgwE&PGdC%uyB9?PUHBB#X*FFe-`usX&k2D z6%G)*X&k1|6%G)(X&k1&6%G)%pLCw3EvCE+W%p3xz-%#=Fj(nr#Qf}D49S&k49OJ` zLpC;c{umqoO~A&N0$;VsWoLKD<)02n|*6o7O39M!?h z(TdFK^f^$HySwIgdG4}a)ic>;89nbh9I@x7dj~@2xar=B$>B}AdzfVSGg=yJ-}E?G zRKolG#ohV&wp`E`_a5%Ed7**crM;uq&_}CTeYv~y*@xA-SZ|uNqU*VA4fINAVkUqsaC8bEw3E;!*lMqIi@bk0>6cz(V3t2Ih_=7OI(SkHjC* z_DK8@ZI8rjY_Cf#_w`I>4j135_`gTvW=`*;@u$JsN%CkyA5MK%&FQd{i*Mt3 z?QKrpyj#%68t*CEH=>hwD?LXBqh8S1-pB0Gr)c4l8SB^+qpe|vBRIOGvAxa7+jrP@ zPcA_}@^FtGy^7&(J@tUYzgy|~vB!IY_8nHqlQVD0;U3?*IaW3vG-9qq$F97?n@zNp z$UE8-w5=vHdxv|1#w|JA6SS@(Q9U8!798!dZ5#2^(uwNWk^9KQJw@wARMPfT?@H`O z#-d)(@Q(Ks?OQNeoo4h#mowfK>keD)sio$}9qq9rS25a1GJB^I^CJ)U1g$%)k*8+d zf}=gYZS$;Sw09X;$9M?;pOjZk4h;7Mt*gt+PshAP$9sbIRpqLuW8RX(J+^ftuBvaD z-x2dZ_IOXxz7d@~bXHHuz(ogqiWV-JvEJDw4W~mN+rGoLdvXc-k%xPN)-9T?jvc)U z^A;WN3EFp9CGS@C9UjX=vQjKJzcabH1wF8LN}HRv4*!#~wBaA`@uN3t-|b)y_&^)J z+rN1d2Hp;)%z#hO!oxZ3=^41_c#m!0h_^ny6#eJ}K1B;hl=66agG%fL>Gbv5bgDVewEcu&y2ifr|i%v*A}$F^?7 zRkiK&J7eC*9`7mIH=>hwtNKO;qh8{0d-N$<)ZW#BGhkJ_Fji}_Ir+PZ(Ejiv(v~R&=_0BG5_`Lbp z)*ZIoQ%lW{JK7VpZOKG+?8sFZx8!h7(7MAKd8exG@K7G|REF14Hgdw5{qb$vR*wVw zr?gpl>+L@w3mNX=o}hKNV>#dfZRl?Q<|&wWJC-8jJwf{pr?V$#-jc&Twsj+}`s9-H zV~_U~?Hkd_yA{8Y!L$w?`J1!>pQ43JW~^gRj3UOnak}6EbeW(H`5j5kJ+o%wlhv`Cs)*^d z%Nbs8KDKp-E%($?^W%>81Z^Af_ID~VD~wxmxF=}cVU0XB;}#rk)V6;?bvvC;#>@Kk z`sWwTY`VIcT#YVg_4v(d-OMMW%jJ~*`TmP)wXWBbRdxQd`h2onO`G}ok3Tu9s`sB) zm$PR4=7NsKsBb6H+ug0{w!7P%eO6tzS4NBFWVK?^({*+JN%guOPv_U8tLbtwUX!qj z|My6;X_Sv9`zwzq`}`4QKYK*k&yOVg>=FGO$v&2!BiYCDb0qs%evV`x%g>Q)ji3L8 zk@9fW++EHlqt&dwl%)P)wQd%4ty#C}JeIdJ$cUD?-xwK3TYm1AlhI;EWo|c=^H^%NpKqs!_mk=Js>wS&bP3i}=prN5RO=#wHI+MAFt@9ZVq+xuC^kldk78paSYzW@ zlDMyDvt1v3abQczq+YFu&s5bfQ=L#fI&+R^rS4R zTEcz;1}!+w)QtwG43bZMT~cSalt*%ld|Y`6bG!{hUoTh-ffd^M^R*?CuLJbHrguV-8N~- zfgaDITU#7(%qNt1AA6*yWz&c@-j&oF8Aj{Ak-teB>S13f8=ZvFB=$xX{g$BR;hNsEs3q-;8@j3;E$k^?=aMRTk+Jm|t) zUyd20k2%hhvZp2&Jqd%B9Oy||RFR6Fgh30A^LX}*c%`=0J$-{d@<30^q7h}ZebLh} zX~~hEmQ4$$qvJJ35p!vm7`(oFJc|xn>*=K3#~tTM*)!tX??}>B7_{U-Ps*ahx_CMU zEjZ3&+B46byS>Z6+QNhPzw0|09$qT35A>ugs!K&r!K6h;dQvu3WuvEH(vkx`o<$>$ zsc&^p-lUH`($lhOL>mv?(UUN0(V?D}RZHficW{}(>AuIa>9DS#{WAPbmdI_DE04rd!u6(-s~tQWZumJJgf1>aaEv zE6qt6wdhEXY18Ojb@Q$Ju=txh>ro+m3LMAOa&|_LO&m_FP%fM>GMq5w+)e1b$ld`8S7d-)k zmK^9wSyYvZo`69Mj`Mi-jCiHK)jf5CKJq|M%c2owJoH6R!K5WedRjIun2z4TB?h1G z9?zn~)_OW=_i@L0QuZvFijEhd3WJs$=t)_0SQqa|6&)VLLnewI#v6ale5 zZB00^pGcc=x4!-47Z=QBM{&UFZHR9F=G`{wb`(WMdQvtWPFPRKq$LM>Jc~vg z^9d#1#~$fv*)*b!cO_0E!)V<%@;7NiJuR!2%t^20A zsZZC~^>Oj>fJr)ATE>F9Wk zQN&!@B?hnW9?zn~)_OW=_i@L0Qud7a_B)bv6$ULi(37(0ur8jCK?{yEYR{joZm09f zcv-(*|NNqvO;MaWOeqcx?8NKSCi3Ib3Y$lPS@4> zC)NFAy1Z)WH_OR*J#FR{|L^=|_4#DEqW?Vq@h4~1dmmQ6GP$nDZ+}aBqxaAFPrg!3 zul_Lo>nrEg=k<(#_5Snfa@LIBT+qgi`gW3^jpaJn_-Do0zT#u!pA~02_p$NMva+4! z^S5WmU~~H0^JB0%{q4mu*qr|MQpt9`tmntcZ~wA6za_Dm{7u-j`7Mdf*Y((B)rv?X@eu3dakEgHTu zs=t`7URHPWH*@OSV*L;6t?kYJF8ar=Hw&%+&sUS2RbRvy*slVKAV(P_e(8`1GS6o{ zZBsAXOr$+Ne^Fg8oBQ<*!Rx!s-$8%7({k7?>ExNBP~-uP>g1UsOXRurX^Iw+2YggF zK2Du#e-U{g`ejDS53*3wP4@HU0ZLEwe3*sO z;+G!EkFro&{L(}DaTZF8UwSBC?Nfdm^q0%YVnR2D8ql4c(Ezg#?guOL&KdDzo(@$kJ;dDy3N@$kJ;dFU4VJgj*2Oho(#bbMxeLH}{` zfZoo`1O3Oz1G+df5A+`=59rs-JkWnwX%7DZotc>j`j3+b^k8Nl=s!*#&~2G{p#L~| zKwo9%f&Rmg4*vlil$i(mkCO-VN@gDDKTaOd6`6UU|2TO-|MRtO|FfK~ZtErO4%w}% zU3|S-?5>X5ts0~C+r{K%bv1ci-_2-0$nd_8R-L@><6j^T+jTwdO21s;;RJQn$@Q$d zs+VtKP_57Pq4-iBgcsH2ayovqazL=M0*LQJ`;0asj<8+loD{P&+Y#W{4r<@76%wsKNA z{R-r(PrLc*?soBJI^Q+T;lpTkKl67`?{l!((n>V>gGdBjv^7{ zhi~lLk-F;1tr+u@>UBMy&aeCX-F;hsZiWn;jAF99pa9D!qnKPmP=HG$qnLa}P)hg; zR|0i15EM{AlC{F=-+=rgp`Vto)G?z62aILQYub*LQ2I+Pe`daVGR|Jlb(=Lanch~Do%Pr zO2tV}NU1pK3E}@J5sW<{rQ)O~L=z{yhh}%n#cVRVp4DH{TS&A)l-?q0*6TX9Rv2n4 ze3zpwa=abkza-7>)_>bQ5^{w!5?o>Yq1^%wS`=>~E&okG!k03WprBNc@Xs=npvYB_ z@Xs=npzv0Z@Xs=npd6qe;hzO0{UxmB;%>RwWweVadZ&4OGhIz){e4ILs`GRvKkuF>Tc(}Id=Ly2I?ZV|sqzhLN9;kL6 zylodQc#$q#fp@6>dHA+nxbQ`~a0TDNUP|BE2@XhO-F88NbJ*WJJRpe~c@B7&2L|LX zBg`RR^3W8_UHaBeJSWnHCwY#CC(?y0cn)}u2PV>mD_{D7YVY7B80dRE{6z zV>al&`y1tBIgkak{r@j@Gfr@YN=a~q(s^)&W(~m=>e7QN)EEa>Kr5;rpULlJd?0`6 zWtu!oWb)5~#Z0(3<=a3FEp-K9O)kbIeGy#0@ z0C0apUv6;7e?egtlsuFC_aEdhrgQy?BI5QSTbjf_ubM9=Z#@GCInyNh{c7AS=bMD) z(0gk7{ERjz%-)Wg*Q52#WJDhdnoXj)bk_-p{=xFY_qv@cW$|)5x&EiUlrC4uFWa;) zSAeS|p|UpAh7$~ssFN@_`#LZ{(oVuKb+4igkiC;IObxAIfHa-0pu1Zmu!cizd zIHu^r6pcawqA`WS6pTUvf-!~Sna?lyXF*4iLSYI;(FF*_6bf#W95KQ_%cu*}lvH#9 zDrbr=Op{ch01a0Pg=yL<6rk@)p?Kft7yPq~e!-7z9Nr0@_N3^7cO*C{z($4?3f^_# zpa7c{QYd(HfrA3+J(+f0(T2MYEl0=VI6E`x}P@< zMIGTR$3ICO#Sz&|ZI7^lwkN;dOwEw6fo3Q_o2f+-Hqauav7skz9Chkg5VmLZtDq?4 zXLI@+Xs7bCIsFYZS^3$V{svmF?z2kHZ>xHK#jB^XBjVy!#TO~r(bfJNJ!-f5W8_Q+ z)0hY0gX)I9Xx&`T-Y#zR$My;a$XFhX^J;t-Jx`fswW~)W9wa4?`0ZvJhaEub`I%s; zZRZL{sB?dO$U!oLnUaox0hh@PX397M2AnA~m?_-|SY97Xu5AJaYFmQF5SY`)Kw--a z=JYX8)iQ%QeGHVeZ}#)prf#+8;M?a0zh@{f1u1(YXjb1Y>~j9TAvwXS?M9Ak)8gMa zBnIk@zSudw*^TGo8&!K@6rQiC{%|pyxh;mj(nSfbkhcX_v}RV~KCA90D}CV>R!{^| z)b^~J)GiK?^HVsUS9AIZ^^LlZ5QTsop9Zp`kHC6BKu%8qIVVS7IP?KBI7}1w)6S;j z^H=n7vQ67PT1~I#^^Af;KWY0bx}g+K+C0)t)k~y<;P6N{l_-%8Lc$~6REI=5hy#yw z_F}cyiFDxn9_j474(XYEo_h?3bkLf3#?BSSAsrMZk95;h46!?Sis6xNdU_$!!P5(m zbkkD{kq(|(c%+-2R)}=)w8A6(L!a02&w|A{k95=12^l+hI^mISdMY8(!BYv3bkoxa zkq(|lyz0$2sE+U2<@58ZrhRx88~+yiq)Y+IMQ%w~~pr*zond+xV0`)T{$y6&v5~!6iNv66el0aRI zNix+ykpyaBOp>YIi6l_(VvXS$U^{M~( ziNBN0pGFxsx3~OQr@t_E_KG$V&#$KIX+yJ6-jG+FQ=Pi3gwyL z3djff`Av0F_(7e_&t|Hu!Uig9el}Ar7B*0e^Rt<%x3GcgouAEA?}ZK2`!qIwQW37) za&zj4WvZq@!;DzkD}$jKw7TeH(B8AN}LtjIqB2zFO4x^HDt?-!$?fH#hMTdmjqqxm@dlY&ZZ;zrUryTx~B`Xg*a`t3swkr4`|zk_ED2^4UVj8G_PDxiS#WQ0PgRsogIr_lZ$ z@{(xR%(iMP^lztsY;r1?Z8$ZBpqxGhrWqNboIV9sATvTaeF`j2bU%N)suy%l`}M9s zoxSAc|8^1O;zO#*?f={EpgpgeI{Jb-L$?0Iy4cm^;L3IdMgArmBqj94@@}kuon06} zn(`^?d38CZ4M^&CLJ&Yw@*%vaet0*(o_W*&a*&sy9@7_%^(R`09ze?RFunU+*j&tj6{0*UjvzTX*PV_H>fs<#aY(zopVq(W3tDziDzm8Nbo@F8>?_ z^RD)U^D_$wCxtv9ptx8-xG>}a0VT!)!qp)U2&gj_5H1mUKtQpvfN+(_0|M%e1%zut z9uU4$u&QAJ;btxm2xto}AjAPc&eV;W3-U5R2hzA6eS_=ryBOVY?a4jG>k9a^p z)wO`&JmLWXrPl(2^N0r|vHIdX;sF6W2rL@GdBg((n&j?DWvfX&TF`6wGkJmZ{G)0- zqa*p~4gGO*H($4@gnFV$O}h_hgMm7S{Jx=H#0b%*ku(UuNc2z)~CnBo(l_b@^PKH-4?eTNYu@CgqD=sAoKflqiKK)>->ciu9oub1^o_R3y- zTygGbFWTNRXN|S1V*fxlKLl5Ri`sDBudbTAOZt|q<-H&w0?EQD|FY4)xF+)BdE!^< zIi=|NZuL@~mj6Z4zix`yo;5d{Uf-+_N*QdHZ|-Jq_?(yKi{0-pp3{c4S+jgz+0GpO zFZ9rdh4Klzrju4hD)5J1Js%kA%j%YvmFMePZ6Og0+(0Y#eLCZlEJqs#@APY-A{hjr zR`QGL?PNx$H#^1)ih@?^GaB!$XQZG8=w+Htt`+Tql&_abIahyLtH=ZyUMrIpNT;*U zCrgJ#W7EfhzXYS}DQ3nFZWHg5hg`p2&G4z6rtOa2DLmWh-j z!br+L3AjL9nJ73K(kVbpnJCx<=@cNIOcWdi=@cNAOcWde=@j|nD7tC^;%K`~K%!>5 zO+c=IifYo=ejG(fK^%de)I_l#M;;2$qnar8gg#ejIryKpShK*pDL*1?X#g z6g;Wjt)8gs-5;VD-&LBh@^;?>Slv$7H~fAfZ9wQe15gY4v*hXr1%1+OTyGm%3)+n& z_06V#uPe7LS#o7tO63YDr8YJWXthp6F8?fOdu(z!>K$@HxwOgUICsbe1=1#$BikVt zlti0cj%9~jPz1Zj?5%E_W_>f#ehHLc-Myz{Qm?4Dq~oT>{8BG-sloouZcv1dml@0y z5dsDxA~TpNWCRR^OlB}sObHl>sRS^z58dfwAm}oKIejc|R3eQzeGIJnX4aU~$3T7j zHT~Mnd8~Kd^by|e`B#V1Ss6Z)#|a; zGJH<|0~xOjpVR+9;w!`F^godQx^JRC^l`_0uknu#<-YuECchIlkoWSlnS4&zK+en0 zX7V>-1NknEEjiZ-8_0F}*_{3c@?3s4r@w(5m!Hk)Zy>*Q4=LZ~x88R|)U(b%F_fx$ zM-a@L``rSN`u4w>YWBeurrtg|y8UQYzwN9t(K7eAp?wSVp_KM$tQY(l6514FY6Je( z0stoe7XXlNofI5)(vH*b0=>*_Br*=XC0O`>Qz{$)30CJ!cfHRf@03sIP(B_u-iRZn!Zdi8;$AnF0XsvZg{WiEv}z^SWUj*6E^Rrs~b89 zDa=RV5N#4TzRODB04^(mBebjp5)ew*dz6~p4w)qYJ7kss>=2dEI~0oJp&tte&pQvb zt+@}XWuE(>lH|D$sw|%SpaS8!56bhN`=E5~xev;^p8Ie&6w8_#|KX-4$9=dr$#EZU zF>>68JB1wg;ewyzK3unR+=t6=>^{9!2F^dT;H@P(bLoa&$)Wn2^IrD6*gKIi9M!?& z%l=N%z`?bNJ)d7SQ`uS?$_OK`*GjSGl%GmF_+__T0`Rxk^4%SMVJ+9)4}3Q*-|gYo z&~n`#e&Z~>d2ip#qM2XI?y-vx=!7u7@^Gfv)l&Z&(N~8&-5~RRqJJAwWP)|CULW3` zdp5efThj{WZiVsevubl?w4eiXSKBo}yrnw~$1vo%nP|6}{Z3-pg?w;@DsylJELCI# zM;%uP&VLg?mDMI{-xN^%vtTxvQB$ZFR6z01%0Z#VQUS$3D+h(TR0R}Rm2{eqA7v#S zdM^>W-5oAhsP_qiA-0I1Pb~=C8KGc(FQYb{5enA)GD0~c6s!YggmOkGSR2d;<&03U zUf6s6cjM?+P4}qzaINfPI#i)qj(EfP=A6{Ci>g^n=IzrpI``^svEcXpXU+JH>dh$j z#5NxPH2tGrRprVj`}xiS&LMb~3LYk03jhx?86Zr-Ky!2{(B)@on&Xg622ar($sYzd z%j0tt=7HwEmZrI=&uPF@G#B+b4S>SuwjHlT>o?Lg7xg&}c#7ttKF7!E1$l07xj~`1 zZEqr)(8WVF6wg{@D$BKpF1=5b14AOcdP&k(~+FO=|4>~ z9rQUXc#7tr?^pp8nj<-Z=6FRep+rS}4mdqAysT&o--=#_JOA+~WX%MCsLyEt6q=(c zTA;bFr)e(ga~kjz%|(4q1EA0xEtm$H`>8a|MSZSxagX|(20)=XS~ChX_pLO|MSV^K zUexC_01C~~8d9LS@1$ui>T@7i_WHRL%|(4q1EA0xttkbX`{^{zMSV^Ko}xKVp4&5> zLUXk29BA%)`dR(YaJ67wDS+V$zK_g(tYP~Hh7T+-$tM$RGSbWE1EV>7u8+5?8qCo(B z3p~lP8*$jTJXqjamK|%@w>((9?Q6-h9T9x4<5KJJ!B$rDB06 zdv>gS-%7;-&-UzC`@WTm1)lENv4(xi((j37;Kn*ta}b;5`95*067R zu)u@=@9pxviwjZX>% zH!Kdn0DVph1==;Q&;@8~QYg@#dj$pPVp1s3j(r6MXj)Pz(0+df1?W*yD7d$9cqeE{ zQYg^71`1t(4kU#Fy{Vv}01Zb91$xgxK>_*;unCMl^f_J1S9RLG1*_@RWOPMm^KT9< zNIVqqA0~e~*lXk>GW8uo1p1C#M5Z1@h(HgLi^$ZE2odNz-A{Rs3a zxrm&8^n#);=pu6wIsFLqFTKMDH|xukkLLVw;&xWEdzIq+>(%v=z6E&IUUr^N`p3z* z2NO{;ipg^X1!SOP6qCyc3P?T4C?;PK6p(Y0QA`sdK>^7o8O1ak5)_bSl2JHMsM|e3 z0qG?f#WXJxloENxG&vF!FgZ%rifM)zu51AUGajJP6T`iB$ZW1#D?g2jEz0|t5^ zD_GpeJYb+BvVz5Z%mW7cBP&?oW1Brt$$1QPOIEPJ$1;I|-pL9U_*f<|&`DXr0w2o+ z2Kp*1Slq`v&JVgQD_GpeJYb;bvVz5Z%mW5GFe_Nx$2?%5AN%%xKh|zL#V;Gbn8FfAL2 z6+i>!wSsB2NC-fu)LQ|j2S0NujKj9#dA8ade$tTSNAv5wf-yA{|hO67bKzT z^x~;D#^L4HEH5oAQcD>;D>7(&<9ylFYaOW8`@N>QF7uDOz zY}VX6#tgh$FZUUZ`PMVqB#(YpEv9q*2eHMzps}}%^?F(BPq`AQppNLJzNl91XSyAh z1x>kL?z3wAw$^`KT}BH!ZoSkO)#7fsn0Y)3G}L;z&*&TLuBZjwvR*2E*?mcm<{hR5 zy|6{>^>Q-thy|UlUhFw? zuTi$S`igK+eT~BHC0L|`5^R+2s>1>R>aY>OT#Q9HD8@$N#)>SWK}9x;HkD-&4a%}v zw5c|WXi%GtqRj3+Gg3d z$}O@%Hy>*FO})ID@C15HpM2UaHhIhVKT{3NpY7t! zm0aY5N^X~LFXjROin$%YRnLV1)N?z7xulDHP}1%4ja6NwgR1V4ZYt~|9TawtbW?2? z>7cgTrJKvU$Oq-!F5g(;MLMYPcIoCKFY-Z=x63!yd65q4yj{Ar)Qfmf>OJCZ)n3Gd zYHt^BEBGQD6nwjIYt0w&pyu};dTlo)U)J+^=ktzPwk7}3q0*m1!IfOSbRra>0Z5_X za_*o2T|f#27jy>&=mSzHxU4%UKsS&=!R6gS0lI<|3NG^w3eX*-P;j|-P=GEWg@TK| zg97vlDHL4%9TcEvNTJ{sz(D~zhZG8K1soKhgGiy^j=(_y8i^DNZVVh0pr1&g;10n- z0UC=G3T_k}6rjHd9!HdV>FKW~UhRW<1B0oz5C+g&`1N4wEQA4c7Jdd(Um*;jukbUN zx(ZLNru=pw$~+jYa@e{&lgzqq{pClXNZrR2yQu$xO4tk~3kGE1stJ|ho-^7%_k^&`wY^oIgl8&NomcAi_B&w83 zUw7L`^$!`OP^IvTicUOH7gb>FA6LW&2FsZv#Uf~AkMR5IeiP{fvi|g-^!UF zkP)4}1xoTa!p7h@UjzSJx=E|8qH<+BN0Tc>xlNuYOdzY})1}GngbAdyd`u>v6DE+) z@-d3pb1kb2?Qu}2@s~(5eQJ%5+F>$A`qaEB|w;>Lm)uqN`QDl4j(I3?1gO^5(xfT zz7XYx(*Xhc)C5hKo`;Gi!1K@q2-5>mfdCaa0mAe|R3JdfO@J^x5)}wgV-p}u&qM_R z6x9R>E{Bfj$Xi8CPenx&;HhYWCQOe-1p@TUU)!69uV(e7tkS=FSf%HyBVMZiu-YtB z(+VT4HlBT0t((PYHEZZhj_F!&p5I8OJlJ2Dq28-jw5Xel2s9gJqG#28(_Cdx4`@2f zFdxt%HJ6vONiGF}9>mPV?|slhbp{QAX2eYM!GoLUVgkL1ndwFKqq};!{xXA#UQo0J zU5Xj!!)iWR-%OU92MigYK-c1c8puZaQ`00HfiA`jbvPS&h(JqYCW6_>0|QzcGt5vn z@-Tr;$IOJYk%tDfJZ74qY~*1A9gvynKsNH=fL_RqGnkD$P@p&ZI_!;pAkERIVRJP4 z!AE=m&nUec`4dC^kq2z^mg(Know$Vv^hX||PpZXoy1K2GbgtfrzE*O5lYtGiOCGjY zUAC)w>F-lSZJ=xNp!L+2)~^T~=%GAppH{ccYV~%MqumrZ&`f#YKH1G&lIIG-20AMr z8>fvd4VJKh2Ft@XoYM5UR=_}$q^RR*T%*Pf|!* zPTj}F;5N%`heIYB&md%|{0HGc2{E`r`6#%ODGLmTOmsLL9|4%UTNu_g4jB)SxX?AIqR#qC@O>{Jq zn!K*>X6u;G?8@^c`PXipv8`}&W!urpm2F2WSF&I~-#p%nLGOyNhzGgJqqXspzB6O^ zz>NTWh4dHO0TngdMK!*et|l`V0Z2Y}70^l9wnG9$10dJztxr=EzV4jYt2Xri*7eR% z&)*5RQEa5pH{6=>n_ZNop1$+%l8f~NM6PVZL9T#saATTMtssDqa3ipHtBoZHASm1j zrgSO@AS&Dl?BNbAfWUAgaQ$>3fU^73y{K7E7YlykGOowddNgaUcX4y^0ez@%HmjGT z`itr6tcrd-kB#0Ike7QABUgY~yg*;5^&#RxY9_Gl^UQCh-F44I<)!LA>Ji<`D6~9KDZhuOE^| zzVLW&&lYk8*uq7lH-yLrhH%N&TR~(4D?le$EtYrVTtV?{&k%A27{Uvrw}prYw(yGA zn?u9{bNnjQVBFVBZoyOgi-ddcDK{+B3HRR9TzK#P+o}(V;z1{A&4v5UQGK}Y9Mwku zn+O!A>$@c#z1LgOxS$O%>)!v>uKhi$uIhCio0V`(wzIVVhyKxD*N`jQbq%=!)-{q) zSM}%m?}Q5m2fzmc?Zf?KLhr**mdm$vtljEnhkyQp4ts6x>5y0c-}%ex^U0ErRzCmnCuh~ce)>No z_3ctXnp`%7LoWX;SUtCmmm|?37X+|PE(e}NE{Io~T+>pd*cU{nO|EJEQRISPw8`bi zbPhf9&yxI#>zzX`D3f39B%gIXo6-Kb)oK^G7q2S5IBKuj9^vhB-}eahU>IA)KWll^ zLl@Z~ll#>3{$B_DJX zj`5%K2~Q5=gP!8EZq{7Zi>szt?tGM@}w^&UndtOnn z*iz_k>qbnN$JIpa%K3>vfT)SsyYmx)m{Ak4zvm|cQKTj!pa_vZ_y0D!*QR{~aiu1T z`;mtzCD!78}tu$NM1qQv!Sg+SLMa$J@ z#_%D{CRX+GcKhG753ISL@QZxB0CPs~ncb~udU5q|L2aOah|IE!pwK}5l#+yamIDz) zP)5W(E$Y*tXc0tNM#McWdJsV*S`g33H%t2>h}MjVds@`zGcj~xL|e7!F?4j&G91wn z#62x~5J5?{7`h64k(Xn0=f&u>W>||6#62x~d@(xDNkYt$7o!uOB*Z-}dVDcD6BB*Z-}dVDcDo0=}}sb3u3G>yD-7B-E0OVIt~ zsO}N}!J!_p-$h%oxT6blwgU_Fg=tvrq&-<%pchQT;?6ByTcH0lW1Z#qEzt9&Vde2H z(C4LLl70Fv3EYOpsVde2H(1)dAf)11en5xE-_6%AEB=-}{XW*+u$J3(MX9R)vP$^m%X@DQ zno{@X)}YkAxr-`wZ!QC+?#+q3)V(6LN&OZRLT`F2T21qz78ZL-0KB9+EPhOZw3hocZOL> ztS<=(3OACOQEcxE2?|(#NVAXa)gb`^s}0=~=P1VB(zdo4bQ)!(Xm! z6M|d;2_YTYm=uHtB!wI_ro9CbRl2yR5p?{CntccdJ2R#5jo94|5Umy@D8a5V6PQBI0|c6R~gQBI0|c6VXlf ziCFy(KLQ=0!)nlvoJ64aa}Ys4auR_q&p`zJ$VmkHIR_E+BUYTlk3i?Owe2y{&jBIrj>BG4aw z+*w!WeMiy}eYN8MmV@zl4Q|EWqIQ=b(%&9zgN%Yr8CV4K&w@&B6l@Av5zIeJ6Kt}v z2_s^*`ig%cNVhmAqaH;x$X=~Ac~ly;+FwBbu37*uehV6+=dAs7^D zqhKzq4jY4d-2MLNJs)pNzNjge->1JvuINkh-eT6-4{$B zKJj!(UxscZ63#xWHdjWAoDEq@pq765ie+WYi)0PLQR3cwEgr~o8?7+`Z;^&|Q~X@IB?lm>|UKxu%e z4^-_z^F1Hf+p2KvJdhP2>I0=U5cPr508t+RPMFmM(OOU%AnF690ir%o8UXsh*<<$u z#{_^ra7+N`1IGl2`T%gky=CC+wV#|++ETkd8=7{xqHMwM-tQL_UKxJ zUpNahyzjPAhxtZlr_KZI>&bj9J9c8L5#Kp*gV8l@h2D*J?>)Q^=+ow-&7(;EN=N4t zfcXWs$?li}TgN`8z}AtEDX>jm2L(pG&h9kBHY^};*oFlJ4%@JRz}wH-XCqW&3S*cpEkTd zm=77ft4&oW1(ar8q*{TIf_j*on`V!H0@=w5KWJnEJV|2CJWJYo5?~nEoZV2P2ZU; zMALjG3(<6-$wD-3XtEGZFPbbw(~zS6Z#|JE5%XG*^@h=vAnOgI>pBrzf~I>X6t%j_cKw#+UvZp-W<16yt$ zrmv<~bo@heKUeK_PqgJRLZ_NbLdvCT&U(ik zept<>Kf0S<(cy-R^$lw>ROP{Q6L_MvJ3%0HLbXWbbqbOEv*4v%wMgWQ3X%LXuSn#h z3X%LXuSn#&3X%LXuSn$43X%LXuSm2wq7cbH^NK`EB?^(?$N;s+qJcJ+!+<9Ja* zzjCM{QwT#n(!t(RgvC}u@W@cxq!1Qc1Hr;jC#4V;Tm8VoP;;da7F+kg!cf1Z5EfhU zz`{@qrVtie>%hWLSEdjKRylsQJJY$Dt|z1GS^Xs)OGgtc`gmWnUe^+NiKm+WSKSzF z&oGrMq=(>2eppis3mS;wM6H?PTR=d3CxVz_UqC?YCxV#rf`EX$kO*SR83F=wMsFL( zbbUi-k}aE+ymS=n=(>j+-#5+GE)4f}sL-p=tMziaSUj92dGTJwKNzjwE+*$yQ@;sC zg{gwo*hU|Bnwv!tnl-dn{!{Y)-7-mVWgD~dHy~#74C6($S}gCzlh{Z&M1qmfEG6Iu z0iB2Pd38Bmj&B^0G~o##L9pk6d{O<7-k+O!R7sPkfD&YaT$J^=9#89-E_bf`2lrf8 zu57g{SN2B&Qc0eXTvS)}^36yQhHUayzrwliydcxKInT_Qg6$o+0IlN$SGMYwyuh>W zV0}Cfv^Fnr$On|*VMrb(WflNP;9*G~ByG$CCh#;R7n3f+0TX!Kl7~qjtAGkTkx^59 zVXooscp4OSD4xnlI}j;lgdK{3Qqr(@myveZZ?nmZ*NQTtihE>lV5L2>C#<+f2GR<9 zWKUWFkNom@IdV``c8?sCmfIr-g=P21h_uWeIVkN29@&ckIP-Sr$Z%*qQN=y7C#|$c z_JkGp$Us_QkL*d)dt~o93oY$TXt#gH?}~)$LAzPr@1$u{fAvSMY*&Be3RwNg2+kdM zY&Brll?xPK3Z|YJp?J#ffa0H(gJSbJpuk#AMosbhiUSHP%VdNyt@esffi)N>6u%Y! zn%24KW%jG-axz{|n>i~grYR7i+jSVh+^)mOl~;7HaVlrye8KGb)CL@$H zLcv;0Mkr^5g2kAOP|gSiYcaj&F`F7X{0405XI{dp%AVM+LH+{$qhD3!3hyEcBGdxT zAz1e7ED_D|Ad>+Unj<-ZE_p9gg623RlfhE}Nd7Ru=7qi9H%+2-0{9$-d7wGoQI(*% zsLyG@QvjkqrvXs-+_vNW1=80hetBP4g65(=rvXm^ApBvU(;V8a(A>5+`2~`<(H!qC zOVC`@=QQ9c08yXQ04Ov^eMk_k-%it9)aNwdDVihvVV?s|zxT?nLUY?448K6~Ha_=V z`d3Mx(}1S{M14*JpwQfQ-XNOe*I5!Y7xg&}cnSdF@B3W$pk?6nd$lzM0Ol(zfWmYn zCrB5(r6<93(C4h+DFD!StN;qlk(@wtyoEhMb5WlIPJdjV(*P(mM^m&wbG(s0L32@` z(}1S{M14*JpwJvGmv!_I#6k5GLn6f7aOa1SW>Qjob)DA=nU z6#TP{6zp0K3jSF}3e%Fd_yu_AlwuLn8n#e?r%Wjnw0h;S2>&djMfe%9!!N+22yE1$ZKsLc!0n9Nq~Yfu&IJ!z%{` zc;=NtL90L>3jSHf7~v<@4qbo;-QY3W;D;pq??(K7Lf$lp%>I>XdL>u3&k*DaNNp*o zhxURU3??A~jYtZ~fqh~M5YUUHfE?OGrXT_RNE!*=U#1`dJxL15;k{=H7SNd(S$g~6 z;7-X9gDnxn5|!iN%68>id^$Z5^iUI`3zY2?rTL=^RPGdzgy;g*JB`F0U7&)ekR(MH zsN=zVL^F98_<1#MX3g?>bCnsy`9Wv&AnrUpfE(mJ);rcdd3Dazd{(Da2I_C zeYY?ot_h}|3SrmFdhh$4VKD*1KpPenM%7?C`+Tx=SPe`iqvD=bQ!za|b;2?|V9+_TCVZJ>rkg;DLK6NEgP1a(3o?Rq(xc%*qM!8w0L)!`#x-liDY z<*XiiLL0PY-A$vLWH@?F8F8}Q6+88)e$7yI`+n!dn+G}Nj=cHrx+HD0Dp$5sLAe5Q zP%15lr&@19!aoaU@u?)Hb!j011w5663$#NgpsuHqnAWF7C!pk~l5k;n=!Adv{{!c{ BF3bP` literal 1264335 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 Re|%US&p$ft?9W%#{{y_0PvQUo diff --git a/src/main/resources/runtime_item_ids.json b/src/main/resources/runtime_item_ids.json index 82d33640624..ccacb0ffa37 100644 --- a/src/main/resources/runtime_item_ids.json +++ b/src/main/resources/runtime_item_ids.json @@ -1,3230 +1 @@ -[ - { - "name": "minecraft:purpur_block", - "id": 201 - }, - { - "name": "minecraft:bow", - "id": 261 - }, - { - "name": "minecraft:end_bricks", - "id": 206 - }, - { - "name": "minecraft:air", - "id": -158 - }, - { - "name": "minecraft:element_94", - "id": -105 - }, - { - "name": "minecraft:rabbit", - "id": 411 - }, - { - "name": "minecraft:element_25", - "id": -36 - }, - { - "name": "minecraft:mushroom_stew", - "id": 282 - }, - { - "name": "minecraft:polished_blackstone_brick_slab", - "id": -284 - }, - { - "name": "minecraft:cooked_porkchop", - "id": 320 - }, - { - "name": "minecraft:record_ward", - "id": 509 - }, - { - "name": "minecraft:appleenchanted", - "id": 466 - }, - { - "name": "minecraft:pumpkin", - "id": 86 - }, - { - "name": "minecraft:slime", - "id": 165 - }, - { - "name": "minecraft:apple", - "id": 260 - }, - { - "name": "minecraft:element_50", - "id": -61 - }, - { - "name": "minecraft:stripped_oak_log", - "id": -10 - }, - { - "name": "minecraft:golden_apple", - "id": 322 - }, - { - "name": "minecraft:fish", - "id": 349 - }, - { - "name": "minecraft:item.dark_oak_door", - "id": 197 - }, - { - "name": "minecraft:light_block", - "id": -215 - }, - { - "name": "minecraft:yellow_glazed_terracotta", - "id": 224 - }, - { - "name": "minecraft:stone_brick_stairs", - "id": 109 - }, - { - "name": "minecraft:portal", - "id": 90 - }, - { - "name": "minecraft:gold_ingot", - "id": 266 - }, - { - "name": "minecraft:iron_ingot", - "id": 265 - }, - { - "name": "minecraft:cookie", - "id": 357 - }, - { - "name": "minecraft:porkchop", - "id": 319 - }, - { - "name": "minecraft:bread", - "id": 297 - }, - { - "name": "minecraft:element_7", - "id": -18 - }, - { - "name": "minecraft:diamond_block", - "id": 57 - }, - { - "name": "minecraft:iron_pickaxe", - "id": 257 - }, - { - "name": "minecraft:element_27", - "id": -38 - }, - { - "name": "minecraft:beef", - "id": 363 - }, - { - "name" : "minecraft:salmon", - "id" : 460 - }, - { - "name": "minecraft:melon", - "id": 360 - }, - { - "name": "minecraft:clownfish", - "id": 461 - }, - { - "name": "minecraft:element_16", - "id": -27 - }, - { - "name": "minecraft:tripwire", - "id": 132 - }, - { - "name": "minecraft:stone_axe", - "id": 275 - }, - { - "name": "minecraft:stained_glass_pane", - "id": 160 - }, - { - "name": "minecraft:trapped_chest", - "id": 146 - }, - { - "name": "minecraft:pufferfish", - "id": 462 - }, - { - "name": "minecraft:bucket", - "id": 325 - }, - { - "name": "minecraft:ancient_debris", - "id": -271 - }, - { - "name": "minecraft:anvil", - "id": 145 - }, - { - "name": "minecraft:stick", - "id": 280 - }, - { - "name": "minecraft:cooked_fish", - "id": 350 - }, - { - "name": "minecraft:cooked_salmon", - "id": 463 - }, - { - "name": "minecraft:element_61", - "id": -72 - }, - { - "name": "minecraft:sparkler", - "id": 442 - }, - { - "name": "minecraft:warped_door", - "id": 756 - }, - { - "name" : "minecraft:dried_kelp", - "id" : 464 - }, - { - "name": "minecraft:hay_block", - "id": 170 - }, - { - "name": "minecraft:wooden_shovel", - "id": 269 - }, - { - "name": "minecraft:nautilus_shell", - "id": 465 - }, - { - "name": "minecraft:element_1", - "id": -12 - }, - { - "name": "minecraft:stonecutter_block", - "id": -197 - }, - { - "name": "minecraft:cooked_beef", - "id": 364 - }, - { - "name": "minecraft:comparator", - "id": 404 - }, - { - "name": "minecraft:carrot", - "id": 391 - }, - { - "name": "minecraft:command_block", - "id": 137 - }, - { - "name": "minecraft:chicken", - "id": 365 - }, - { - "name": "minecraft:potion", - "id": 373 - }, - { - "name": "minecraft:rotten_flesh", - "id": 367 - }, - { - "name": "minecraft:dirt", - "id": 3 - }, - { - "name": "minecraft:element_62", - "id": -73 - }, - { - "name": "minecraft:daylight_detector", - "id": 151 - }, - { - "name": "minecraft:snow_layer", - "id": 78 - }, - { - "name": "minecraft:rabbit_foot", - "id": 414 - }, - { - "name": "minecraft:lingering_potion", - "id": 441 - }, - { - "name": "minecraft:campfire", - "id": 720 - }, - { - "name": "minecraft:smoker", - "id": -198 - }, - { - "name": "minecraft:warped_fence", - "id": -257 - }, - { - "name": "minecraft:cooked_chicken", - "id": 366 - }, - { - "name": "minecraft:light_blue_glazed_terracotta", - "id": 223 - }, - { - "name": "minecraft:stone_sword", - "id": 272 - }, - { - "name": "minecraft:record_far", - "id": 504 - }, - { - "name": "minecraft:spider_eye", - "id": 375 - }, - { - "name": "minecraft:smooth_quartz_stairs", - "id": -185 - }, - { - "name": "minecraft:potato", - "id": 392 - }, - { - "name": "minecraft:baked_potato", - "id": 393 - }, - { - "name": "minecraft:element_88", - "id": -99 - }, - { - "name": "minecraft:golden_carrot", - "id": 396 - }, - { - "name": "minecraft:spruce_stairs", - "id": 134 - }, - { - "name": "minecraft:poisonous_potato", - "id": 394 - }, - { - "name": "minecraft:element_13", - "id": -24 - }, - { - "name": "minecraft:obsidian", - "id": 49 - }, - { - "name": "minecraft:pumpkin_pie", - "id": 400 - }, - { - "name": "minecraft:diamond_pickaxe", - "id": 278 - }, - { - "name": "minecraft:lantern", - "id": -208 - }, - { - "name": "minecraft:iron_sword", - "id": 267 - }, - { - "name": "minecraft:smooth_stone", - "id": -183 - }, - { - "name": "minecraft:beetroot", - "id": 457 - }, - { - "name": "minecraft:element_43", - "id": -54 - }, - { - "name": "minecraft:beetroot_soup", - "id": 459 - }, - { - "name": "minecraft:red_mushroom", - "id": 40 - }, - { - "name": "minecraft:wooden_pickaxe", - "id": 270 - }, - { - "name": "minecraft:invisiblebedrock", - "id": 95 - }, - { - "name": "minecraft:sweet_berries", - "id": 477 - }, - { - "name": "minecraft:prismarine_bricks_stairs", - "id": -4 - }, - { - "name": "minecraft:cooked_rabbit", - "id": 412 - }, - { - "name": "minecraft:rabbit_stew", - "id": 413 - }, - { - "name": "minecraft:birch_fence_gate", - "id": 184 - }, - { - "name": "minecraft:wheat_seeds", - "id": 295 - }, - { - "name": "minecraft:chest", - "id": 54 - }, - { - "name": "minecraft:pumpkin_seeds", - "id": 361 - }, - { - "name": "minecraft:element_2", - "id": -13 - }, - { - "name": "minecraft:item.crimson_door", - "id": -244 - }, - { - "name": "minecraft:command_block_minecart", - "id": 443 - }, - { - "name": "minecraft:melon_seeds", - "id": 362 - }, - { - "name": "minecraft:iron_axe", - "id": 258 - }, - { - "name": "minecraft:spawn_egg", - "id": 383 - }, - { - "name": "minecraft:element_93", - "id": -104 - }, - { - "name": "minecraft:nether_wart", - "id": 372 - }, - { - "name": "minecraft:beetroot_seeds", - "id": 458 - }, - { - "name": "minecraft:element_35", - "id": -46 - }, - { - "name" : "minecraft:iron_shovel", - "id" : 256 - }, - { - "name": "minecraft:element_104", - "id": -115 - }, - { - "name": "minecraft:granite_stairs", - "id": -169 - }, - { - "name": "minecraft:flint_and_steel", - "id": 259 - }, - { - "name": "minecraft:stone_shovel", - "id": 273 - }, - { - "name": "minecraft:horsearmorleather", - "id": 416 - }, - { - "name": "minecraft:item.cauldron", - "id": 118 - }, - { - "name": "minecraft:melon_block", - "id": 103 - }, - { - "name": "minecraft:arrow", - "id": 262 - }, - { - "name": "minecraft:coal", - "id": 263 - }, - { - "name": "minecraft:real_double_stone_slab2", - "id": 181 - }, - { - "name": "minecraft:chorus_plant", - "id": 240 - }, - { - "name": "minecraft:gold_block", - "id": 41 - }, - { - "name": "minecraft:carrots", - "id": 141 - }, - { - "name": "minecraft:diamond", - "id": 264 - }, - { - "name": "minecraft:wooden_sword", - "id": 268 - }, - { - "name": "minecraft:record_strad", - "id": 508 - }, - { - "name": "minecraft:netherite_boots", - "id": 751 - }, - { - "name": "minecraft:dark_oak_stairs", - "id": 164 - }, - { - "name": "minecraft:farmland", - "id": 60 - }, - { - "name": "minecraft:wooden_axe", - "id": 271 - }, - { - "name": "minecraft:stone_pickaxe", - "id": 274 - }, - { - "name": "minecraft:planks", - "id": 5 - }, - { - "name": "minecraft:chainmail_helmet", - "id": 302 - }, - { - "name": "minecraft:diamond_shovel", - "id": 277 - }, - { - "name": "minecraft:diamond_sword", - "id": 276 - }, - { - "name": "minecraft:smithing_table", - "id": -202 - }, - { - "name": "minecraft:diamond_axe", - "id": 279 - }, - { - "name": "minecraft:bowl", - "id": 281 - }, - { - "name": "minecraft:flowing_water", - "id": 8 - }, - { - "name": "minecraft:golden_sword", - "id": 283 - }, - { - "name": "minecraft:honey_block", - "id": -220 - }, - { - "name": "minecraft:golden_shovel", - "id": 284 - }, - { - "name": "minecraft:golden_pickaxe", - "id": 285 - }, - { - "name": "minecraft:lit_redstone_lamp", - "id": 124 - }, - { - "name": "minecraft:elytra", - "id": 444 - }, - { - "name": "minecraft:golden_axe", - "id": 286 - }, - { - "name": "minecraft:element_52", - "id": -63 - }, - { - "name": "minecraft:string", - "id": 287 - }, - { - "name": "minecraft:real_double_stone_slab4", - "id": -168 - }, - { - "name": "minecraft:feather", - "id": 288 - }, - { - "name": "minecraft:gunpowder", - "id": 289 - }, - { - "name": "minecraft:acacia_stairs", - "id": 163 - }, - { - "name" : "minecraft:wooden_hoe", - "id" : 290 - }, - { - "name": "minecraft:stone_hoe", - "id": 291 - }, - { - "name": "minecraft:iron_hoe", - "id": 292 - }, - { - "name": "minecraft:diamond_hoe", - "id": 293 - }, - { - "name": "minecraft:element_86", - "id": -97 - }, - { - "name": "minecraft:golden_hoe", - "id": 294 - }, - { - "name": "minecraft:wheat", - "id": 296 - }, - { - "name": "minecraft:leather_helmet", - "id": 298 - }, - { - "name": "minecraft:leather_chestplate", - "id": 299 - }, - { - "name": "minecraft:leather_leggings", - "id": 300 - }, - { - "name": "minecraft:lodestone", - "id": -222 - }, - { - "name": "minecraft:brown_mushroom", - "id": 39 - }, - { - "name": "minecraft:leather_boots", - "id": 301 - }, - { - "name": "minecraft:chainmail_chestplate", - "id": 303 - }, - { - "name": "minecraft:end_gateway", - "id": 209 - }, - { - "name": "minecraft:item.beetroot", - "id": 244 - }, - { - "name" : "minecraft:chainmail_leggings", - "id" : 304 - }, - { - "name": "minecraft:element_101", - "id": -112 - }, - { - "name": "minecraft:chainmail_boots", - "id": 305 - }, - { - "name": "minecraft:soul_sand", - "id": 88 - }, - { - "name": "minecraft:iron_helmet", - "id": 306 - }, - { - "name": "minecraft:snowball", - "id": 332 - }, - { - "name": "minecraft:element_49", - "id": -60 - }, - { - "name": "minecraft:record_mellohi", - "id": 506 - }, - { - "name": "minecraft:iron_chestplate", - "id": 307 - }, - { - "name": "minecraft:barrel", - "id": -203 - }, - { - "name": "minecraft:iron_leggings", - "id": 308 - }, - { - "name": "minecraft:crimson_double_slab", - "id": -266 - }, - { - "name": "minecraft:iron_boots", - "id": 309 - }, - { - "name": "minecraft:real_double_stone_slab3", - "id": -167 - }, - { - "name": "minecraft:ender_eye", - "id": 381 - }, - { - "name": "minecraft:stickypistonarmcollision", - "id": -217 - }, - { - "name": "minecraft:iron_trapdoor", - "id": 167 - }, - { - "name": "minecraft:diamond_helmet", - "id": 310 - }, - { - "name": "minecraft:stone_pressure_plate", - "id": 70 - }, - { - "name": "minecraft:diamond_chestplate", - "id": 311 - }, - { - "name": "minecraft:sand", - "id": 12 - }, - { - "name": "minecraft:light_weighted_pressure_plate", - "id": 147 - }, - { - "name": "minecraft:piston", - "id": 33 - }, - { - "name": "minecraft:diamond_leggings", - "id": 312 - }, - { - "name": "minecraft:element_30", - "id": -41 - }, - { - "name": "minecraft:diamond_boots", - "id": 313 - }, - { - "name": "minecraft:golden_helmet", - "id": 314 - }, - { - "name": "minecraft:element_51", - "id": -62 - }, - { - "name": "minecraft:double_wooden_slab", - "id": 157 - }, - { - "name": "minecraft:hard_stained_glass", - "id": 254 - }, - { - "name": "minecraft:element_84", - "id": -95 - }, - { - "name": "minecraft:golden_chestplate", - "id": 315 - }, - { - "name": "minecraft:sealantern", - "id": 169 - }, - { - "name": "minecraft:bedrock", - "id": 7 - }, - { - "name": "minecraft:glowstone", - "id": 89 - }, - { - "name": "minecraft:golden_leggings", - "id": 316 - }, - { - "name": "minecraft:golden_boots", - "id": 317 - }, - { - "name": "minecraft:shield", - "id": 513 - }, - { - "name": "minecraft:jungle_fence_gate", - "id": 185 - }, - { - "name": "minecraft:carpet", - "id": 171 - }, - { - "name": "minecraft:flowing_lava", - "id": 10 - }, - { - "name": "minecraft:flint", - "id": 318 - }, - { - "name": "minecraft:painting", - "id": 321 - }, - { - "name": "minecraft:heart_of_the_sea", - "id": 467 - }, - { - "name": "minecraft:sign", - "id": 323 - }, - { - "name": "minecraft:muttonraw", - "id": 423 - }, - { - "name": "minecraft:element_55", - "id": -66 - }, - { - "name": "minecraft:wooden_door", - "id": 324 - }, - { - "name": "minecraft:concrete_powder", - "id": 237 - }, - { - "name": "minecraft:minecart", - "id": 328 - }, - { - "name": "minecraft:saddle", - "id": 329 - }, - { - "name": "minecraft:nether_wart_block", - "id": 214 - }, - { - "name": "minecraft:crimson_roots", - "id": -223 - }, - { - "name": "minecraft:element_116", - "id": -127 - }, - { - "name": "minecraft:iron_door", - "id": 330 - }, - { - "name": "minecraft:redstone", - "id": 331 - }, - { - "name": "minecraft:boat", - "id": 333 - }, - { - "name": "minecraft:written_book", - "id": 387 - }, - { - "name": "minecraft:iron_ore", - "id": 15 - }, - { - "name": "minecraft:leather", - "id": 334 - }, - { - "name": "minecraft:kelp", - "id": 335 - }, - { - "name": "minecraft:gold_nugget", - "id": 371 - }, - { - "name": "minecraft:brick", - "id": 336 - }, - { - "name": "minecraft:element_68", - "id": -79 - }, - { - "name": "minecraft:clay_ball", - "id": 337 - }, - { - "name": "minecraft:carrotonastick", - "id": 398 - }, - { - "name": "minecraft:reeds", - "id": 338 - }, - { - "name": "minecraft:paper", - "id": 339 - }, - { - "name": "minecraft:element_23", - "id": -34 - }, - { - "name": "minecraft:coral", - "id": -131 - }, - { - "name": "minecraft:book", - "id": 340 - }, - { - "name": "minecraft:end_portal", - "id": 119 - }, - { - "name": "minecraft:trident", - "id": 455 - }, - { - "name": "minecraft:slime_ball", - "id": 341 - }, - { - "name": "minecraft:chest_minecart", - "id": 342 - }, - { - "name": "minecraft:element_71", - "id": -82 - }, - { - "name": "minecraft:egg", - "id": 344 - }, - { - "name": "minecraft:netherite_sword", - "id": 743 - }, - { - "name": "minecraft:item.reeds", - "id": 83 - }, - { - "name" : "minecraft:compass", - "id" : 345 - }, - { - "name": "minecraft:crimson_stairs", - "id": -254 - }, - { - "name": "minecraft:fishing_rod", - "id": 346 - }, - { - "name": "minecraft:andesite_stairs", - "id": -171 - }, - { - "name": "minecraft:reserved6", - "id": 255 - }, - { - "name": "minecraft:clock", - "id": 347 - }, - { - "name": "minecraft:red_sandstone", - "id": 179 - }, - { - "name": "minecraft:spruce_button", - "id": -144 - }, - { - "name": "minecraft:glowstone_dust", - "id": 348 - }, - { - "name": "minecraft:blaze_rod", - "id": 369 - }, - { - "name": "minecraft:dye", - "id": 351 - }, - { - "name": "minecraft:element_74", - "id": -85 - }, - { - "name" : "minecraft:bone", - "id" : 352 - }, - { - "name": "minecraft:map", - "id": 358 - }, - { - "name": "minecraft:sugar", - "id": 353 - }, - { - "name": "minecraft:name_tag", - "id": 421 - }, - { - "name": "minecraft:cake", - "id": 354 - }, - { - "name": "minecraft:bed", - "id": 355 - }, - { - "name": "minecraft:stained_glass", - "id": 241 - }, - { - "name": "minecraft:repeater", - "id": 356 - }, - { - "name": "minecraft:beacon", - "id": 138 - }, - { - "name": "minecraft:netherite_chestplate", - "id": 749 - }, - { - "name": "minecraft:unpowered_comparator", - "id": 149 - }, - { - "name": "minecraft:shears", - "id": 359 - }, - { - "name": "minecraft:element_31", - "id": -42 - }, - { - "name": "minecraft:ender_pearl", - "id": 368 - }, - { - "name": "minecraft:red_sandstone_stairs", - "id": 180 - }, - { - "name": "minecraft:carved_pumpkin", - "id": -155 - }, - { - "name": "minecraft:ghast_tear", - "id": 370 - }, - { - "name": "minecraft:glass_bottle", - "id": 374 - }, - { - "name": "minecraft:element_44", - "id": -55 - }, - { - "name": "minecraft:lava", - "id": 11 - }, - { - "name": "minecraft:polished_blackstone_brick_stairs", - "id": -275 - }, - { - "name": "minecraft:jungle_pressure_plate", - "id": -153 - }, - { - "name": "minecraft:fermented_spider_eye", - "id": 376 - }, - { - "name": "minecraft:honeycomb_block", - "id": -221 - }, - { - "name": "minecraft:blaze_powder", - "id": 377 - }, - { - "name": "minecraft:magma_cream", - "id": 378 - }, - { - "name": "minecraft:jigsaw", - "id": -211 - }, - { - "name": "minecraft:brewing_stand", - "id": 379 - }, - { - "name": "minecraft:cauldron", - "id": 380 - }, - { - "name": "minecraft:element_111", - "id": -122 - }, - { - "name": "minecraft:rapid_fertilizer", - "id": 449 - }, - { - "name": "minecraft:clay", - "id": 82 - }, - { - "name": "minecraft:speckled_melon", - "id": 382 - }, - { - "name": "minecraft:experience_bottle", - "id": 384 - }, - { - "name": "minecraft:element_48", - "id": -59 - }, - { - "name": "minecraft:coal_block", - "id": 173 - }, - { - "name": "minecraft:fireball", - "id": 385 - }, - { - "name": "minecraft:writable_book", - "id": 386 - }, - { - "name": "minecraft:element_69", - "id": -80 - }, - { - "name": "minecraft:emerald", - "id": 388 - }, - { - "name": "minecraft:record_pigstep", - "id": 759 - }, - { - "name": "minecraft:element_66", - "id": -77 - }, - { - "name": "minecraft:frame", - "id": 389 - }, - { - "name": "minecraft:brewingstandblock", - "id": 117 - }, - { - "name": "minecraft:flower_pot", - "id": 390 - }, - { - "name": "minecraft:emptymap", - "id": 395 - }, - { - "name": "minecraft:element_110", - "id": -121 - }, - { - "name": "minecraft:element_75", - "id": -86 - }, - { - "name": "minecraft:skull", - "id": 397 - }, - { - "name": "minecraft:crimson_door", - "id": 755 - }, - { - "name": "minecraft:sponge", - "id": 19 - }, - { - "name": "minecraft:netherstar", - "id": 399 - }, - { - "name": "minecraft:fireworks", - "id": 401 - }, - { - "name": "minecraft:hopper_minecart", - "id": 408 - }, - { - "name" : "minecraft:fireworkscharge", - "id" : 402 - }, - { - "name" : "minecraft:enchanted_book", - "id" : 403 - }, - { - "name" : "minecraft:netherbrick", - "id" : 405 - }, - { - "name": "minecraft:cobblestone_wall", - "id": 139 - }, - { - "name" : "minecraft:quartz", - "id" : 406 - }, - { - "name" : "minecraft:tnt_minecart", - "id" : 407 - }, - { - "name": "minecraft:element_63", - "id": -74 - }, - { - "name": "minecraft:hopper", - "id": 410 - }, - { - "name": "minecraft:cobblestone", - "id": 4 - }, - { - "name": "minecraft:dragon_breath", - "id": 437 - }, - { - "name": "minecraft:rabbit_hide", - "id": 415 - }, - { - "name": "minecraft:horsearmoriron", - "id": 417 - }, - { - "name": "minecraft:horsearmorgold", - "id": 418 - }, - { - "name": "minecraft:colored_torch_bp", - "id": 204 - }, - { - "name": "minecraft:element_102", - "id": -113 - }, - { - "name": "minecraft:quartz_ore", - "id": 153 - }, - { - "name": "minecraft:netherite_shovel", - "id": 744 - }, - { - "name": "minecraft:horsearmordiamond", - "id": 419 - }, - { - "name": "minecraft:record_13", - "id": 500 - }, - { - "name": "minecraft:record_cat", - "id": 501 - }, - { - "name": "minecraft:element_3", - "id": -14 - }, - { - "name": "minecraft:polished_diorite_stairs", - "id": -173 - }, - { - "name": "minecraft:monster_egg", - "id": 97 - }, - { - "name": "minecraft:record_blocks", - "id": 502 - }, - { - "name": "minecraft:crimson_standing_sign", - "id": -250 - }, - { - "name": "minecraft:record_chirp", - "id": 503 - }, - { - "name": "minecraft:record_mall", - "id": 505 - }, - { - "name": "minecraft:respawn_anchor", - "id": -272 - }, - { - "name": "minecraft:record_stal", - "id": 507 - }, - { - "name": "minecraft:record_11", - "id": 510 - }, - { - "name": "minecraft:record_wait", - "id": 511 - }, - { - "name": "minecraft:info_update2", - "id": 249 - }, - { - "name": "minecraft:lead", - "id": 420 - }, - { - "name": "minecraft:prismarine_crystals", - "id": 422 - }, - { - "name": "minecraft:acacia_sign", - "id": 475 - }, - { - "name": "minecraft:muttoncooked", - "id": 424 - }, - { - "name": "minecraft:armor_stand", - "id": 425 - }, - { - "name": "minecraft:coal_ore", - "id": 16 - }, - { - "name": "minecraft:element_32", - "id": -43 - }, - { - "name": "minecraft:spruce_door", - "id": 427 - }, - { - "name": "minecraft:phantom_membrane", - "id": 470 - }, - { - "name": "minecraft:birch_door", - "id": 428 - }, - { - "name": "minecraft:element_85", - "id": -96 - }, - { - "name": "minecraft:polished_blackstone_wall", - "id": -297 - }, - { - "name": "minecraft:jungle_door", - "id": 429 - }, - { - "name": "minecraft:acacia_door", - "id": 430 - }, - { - "name": "minecraft:element_42", - "id": -53 - }, - { - "name": "minecraft:dark_oak_door", - "id": 431 - }, - { - "name": "minecraft:netherite_leggings", - "id": 750 - }, - { - "name": "minecraft:stripped_crimson_stem", - "id": -240 - }, - { - "name": "minecraft:chorus_fruit", - "id": 432 - }, - { - "name": "minecraft:camera", - "id": 498 - }, - { - "name": "minecraft:suspicious_stew", - "id": 734 - }, - { - "name": "minecraft:chorus_fruit_popped", - "id": 433 - }, - { - "name": "minecraft:element_98", - "id": -109 - }, - { - "name" : "minecraft:splash_potion", - "id" : 438 - }, - { - "name": "minecraft:element_73", - "id": -84 - }, - { - "name": "minecraft:prismarine_shard", - "id": 409 - }, - { - "name": "minecraft:seagrass", - "id": -130 - }, - { - "name": "minecraft:dark_oak_pressure_plate", - "id": -152 - }, - { - "name": "minecraft:shulker_shell", - "id": 445 - }, - { - "name": "minecraft:redstone_block", - "id": 152 - }, - { - "name": "minecraft:banner", - "id": 446 - }, - { - "name": "minecraft:totem", - "id": 450 - }, - { - "name": "minecraft:blackstone_slab", - "id": -282 - }, - { - "name": "minecraft:element_118", - "id": -129 - }, - { - "name": "minecraft:iron_nugget", - "id": 452 - }, - { - "name": "minecraft:netherite_pickaxe", - "id": 745 - }, - { - "name": "minecraft:jukebox", - "id": 84 - }, - { - "name": "minecraft:turtle_shell_piece", - "id": 468 - }, - { - "name": "minecraft:turtle_helmet", - "id": 469 - }, - { - "name": "minecraft:crossbow", - "id": 471 - }, - { - "name": "minecraft:glowingobsidian", - "id": 246 - }, - { - "name": "minecraft:leaves2", - "id": 161 - }, - { - "name": "minecraft:spruce_sign", - "id": 472 - }, - { - "name": "minecraft:element_38", - "id": -49 - }, - { - "name": "minecraft:coral_fan_hang2", - "id": -136 - }, - { - "name": "minecraft:birch_sign", - "id": 473 - }, - { - "name": "minecraft:coral_fan_dead", - "id": -134 - }, - { - "name": "minecraft:balloon", - "id": 448 - }, - { - "name": "minecraft:jungle_sign", - "id": 474 - }, - { - "name": "minecraft:darkoak_sign", - "id": 476 - }, - { - "name": "minecraft:element_24", - "id": -35 - }, - { - "name": "minecraft:banner_pattern", - "id": 434 - }, - { - "name": "minecraft:honeycomb", - "id": 736 - }, - { - "name": "minecraft:element_78", - "id": -89 - }, - { - "name": "minecraft:red_nether_brick", - "id": 215 - }, - { - "name": "minecraft:honey_bottle", - "id": 737 - }, - { - "name": "minecraft:compound", - "id": 499 - }, - { - "name": "minecraft:ice_bomb", - "id": 453 - }, - { - "name": "minecraft:brick_block", - "id": 45 - }, - { - "name": "minecraft:bleach", - "id": 451 - }, - { - "name": "minecraft:colored_torch_rg", - "id": 202 - }, - { - "name": "minecraft:medicine", - "id": 447 - }, - { - "name": "minecraft:warped_fungus", - "id": -229 - }, - { - "name": "minecraft:end_portal_frame", - "id": 120 - }, - { - "name": "minecraft:element_92", - "id": -103 - }, - { - "name": "minecraft:glow_stick", - "id": 166 - }, - { - "name": "minecraft:lodestonecompass", - "id": 741 - }, - { - "name": "minecraft:element_17", - "id": -28 - }, - { - "name": "minecraft:lit_pumpkin", - "id": 91 - }, - { - "name": "minecraft:netherite_ingot", - "id": 742 - }, - { - "name": "minecraft:chain_command_block", - "id": 189 - }, - { - "name": "minecraft:loom", - "id": -204 - }, - { - "name": "minecraft:item.warped_door", - "id": -245 - }, - { - "name": "minecraft:netherite_axe", - "id": 746 - }, - { - "name": "minecraft:netherite_hoe", - "id": 747 - }, - { - "name": "minecraft:dark_oak_fence_gate", - "id": 186 - }, - { - "name": "minecraft:element_115", - "id": -126 - }, - { - "name": "minecraft:netherite_helmet", - "id": 748 - }, - { - "name": "minecraft:element_117", - "id": -128 - }, - { - "name": "minecraft:netherite_scrap", - "id": 752 - }, - { - "name": "minecraft:crimson_sign", - "id": 753 - }, - { - "name": "minecraft:concrete", - "id": 236 - }, - { - "name": "minecraft:chiseled_nether_bricks", - "id": -302 - }, - { - "name": "minecraft:mob_spawner", - "id": 52 - }, - { - "name": "minecraft:warped_sign", - "id": 754 - }, - { - "name": "minecraft:chain", - "id": 758 - }, - { - "name": "minecraft:warped_fungus_on_a_stick", - "id": 757 - }, - { - "name": "minecraft:nether_sprouts", - "id": 760 - }, - { - "name": "minecraft:cartography_table", - "id": -200 - }, - { - "name": "minecraft:polished_blackstone_slab", - "id": -293 - }, - { - "name": "minecraft:soul_campfire", - "id": 801 - }, - { - "name": "minecraft:stone", - "id": 1 - }, - { - "name": "minecraft:wool", - "id": 35 - }, - { - "name": "minecraft:yellow_flower", - "id": 37 - }, - { - "name": "minecraft:stained_hardened_clay", - "id": 159 - }, - { - "name": "minecraft:log", - "id": 17 - }, - { - "name": "minecraft:fence", - "id": 85 - }, - { - "name": "minecraft:element_53", - "id": -64 - }, - { - "name": "minecraft:stonebrick", - "id": 98 - }, - { - "name": "minecraft:lit_blast_furnace", - "id": -214 - }, - { - "name": "minecraft:coral_block", - "id": -132 - }, - { - "name": "minecraft:polished_blackstone_bricks", - "id": -274 - }, - { - "name": "minecraft:double_stone_slab", - "id": 44 - }, - { - "name": "minecraft:element_100", - "id": -111 - }, - { - "name": "minecraft:double_stone_slab2", - "id": 182 - }, - { - "name": "minecraft:fence_gate", - "id": 107 - }, - { - "name": "minecraft:double_stone_slab3", - "id": -162 - }, - { - "name": "minecraft:rail", - "id": 66 - }, - { - "name": "minecraft:double_stone_slab4", - "id": -166 - }, - { - "name": "minecraft:stripped_acacia_log", - "id": -8 - }, - { - "name": "minecraft:real_double_stone_slab", - "id": 43 - }, - { - "name": "minecraft:coral_fan", - "id": -133 - }, - { - "name": "minecraft:sea_pickle", - "id": -156 - }, - { - "name": "minecraft:polished_blackstone_button", - "id": -296 - }, - { - "name": "minecraft:element_90", - "id": -101 - }, - { - "name": "minecraft:polished_blackstone_double_slab", - "id": -294 - }, - { - "name": "minecraft:sapling", - "id": 6 - }, - { - "name": "minecraft:leaves", - "id": 18 - }, - { - "name": "minecraft:sandstone", - "id": 24 - }, - { - "name": "minecraft:silver_glazed_terracotta", - "id": 228 - }, - { - "name": "minecraft:wooden_slab", - "id": 158 - }, - { - "name": "minecraft:warped_roots", - "id": -224 - }, - { - "name": "minecraft:element_11", - "id": -22 - }, - { - "name": "minecraft:red_flower", - "id": 38 - }, - { - "name": "minecraft:element_59", - "id": -70 - }, - { - "name": "minecraft:double_plant", - "id": 175 - }, - { - "name": "minecraft:waterlily", - "id": 111 - }, - { - "name": "minecraft:quartz_block", - "id": 155 - }, - { - "name": "minecraft:element_95", - "id": -106 - }, - { - "name": "minecraft:soul_soil", - "id": -236 - }, - { - "name": "minecraft:acacia_pressure_plate", - "id": -150 - }, - { - "name": "minecraft:tallgrass", - "id": 31 - }, - { - "name": "minecraft:brown_mushroom_block", - "id": 99 - }, - { - "name": "minecraft:element_103", - "id": -114 - }, - { - "name": "minecraft:crimson_fungus", - "id": -228 - }, - { - "name": "minecraft:item.frame", - "id": 199 - }, - { - "name": "minecraft:red_mushroom_block", - "id": 100 - }, - { - "name": "minecraft:log2", - "id": 162 - }, - { - "name": "minecraft:conduit", - "id": -157 - }, - { - "name": "minecraft:prismarine", - "id": 168 - }, - { - "name": "minecraft:magma", - "id": 213 - }, - { - "name": "minecraft:element_22", - "id": -33 - }, - { - "name" : "minecraft:undyed_shulker_box", - "id" : 205 - }, - { - "name": "minecraft:shulker_box", - "id": 218 - }, - { - "name": "minecraft:spruce_standing_sign", - "id": -181 - }, - { - "name": "minecraft:sticky_piston", - "id": 29 - }, - { - "name": "minecraft:element_10", - "id": -21 - }, - { - "name": "minecraft:turtle_egg", - "id": -159 - }, - { - "name": "minecraft:bamboo", - "id": -163 - }, - { - "name": "minecraft:observer", - "id": 251 - }, - { - "name" : "minecraft:scaffolding", - "id" : -165 - }, - { - "name" : "minecraft:blast_furnace", - "id" : -196 - }, - { - "name": "minecraft:grindstone", - "id": -195 - }, - { - "name" : "minecraft:bell", - "id" : -206 - }, - { - "name": "minecraft:end_rod", - "id": 208 - }, - { - "name": "minecraft:fletching_table", - "id": -201 - }, - { - "name": "minecraft:item.hopper", - "id": 154 - }, - { - "name": "minecraft:wood", - "id": -212 - }, - { - "name": "minecraft:chemistry_table", - "id": 238 - }, - { - "name": "minecraft:tnt", - "id": 46 - }, - { - "name": "minecraft:hard_stained_glass_pane", - "id": 191 - }, - { - "name": "minecraft:crimson_slab", - "id": -264 - }, - { - "name": "minecraft:element_87", - "id": -98 - }, - { - "name": "minecraft:warped_slab", - "id": -265 - }, - { - "name": "minecraft:element_0", - "id": 36 - }, - { - "name": "minecraft:element_4", - "id": -15 - }, - { - "name": "minecraft:ender_chest", - "id": 130 - }, - { - "name": "minecraft:element_5", - "id": -16 - }, - { - "name" : "minecraft:element_6", - "id" : -17 - }, - { - "name" : "minecraft:element_8", - "id" : -19 - }, - { - "name" : "minecraft:element_9", - "id" : -20 - }, - { - "name" : "minecraft:element_12", - "id" : -23 - }, - { - "name" : "minecraft:element_14", - "id" : -25 - }, - { - "name": "minecraft:element_15", - "id": -26 - }, - { - "name": "minecraft:element_18", - "id": -29 - }, - { - "name": "minecraft:element_19", - "id": -30 - }, - { - "name": "minecraft:element_20", - "id": -31 - }, - { - "name": "minecraft:element_21", - "id": -32 - }, - { - "name": "minecraft:element_26", - "id": -37 - }, - { - "name": "minecraft:element_28", - "id": -39 - }, - { - "name": "minecraft:element_29", - "id": -40 - }, - { - "name": "minecraft:element_33", - "id": -44 - }, - { - "name": "minecraft:element_34", - "id": -45 - }, - { - "name": "minecraft:element_36", - "id": -47 - }, - { - "name": "minecraft:ice", - "id": 79 - }, - { - "name": "minecraft:element_37", - "id": -48 - }, - { - "name": "minecraft:element_39", - "id": -50 - }, - { - "name": "minecraft:element_40", - "id": -51 - }, - { - "name": "minecraft:element_41", - "id": -52 - }, - { - "name": "minecraft:element_45", - "id": -56 - }, - { - "name": "minecraft:element_46", - "id": -57 - }, - { - "name": "minecraft:netherite_block", - "id": -270 - }, - { - "name": "minecraft:element_47", - "id": -58 - }, - { - "name": "minecraft:element_54", - "id": -65 - }, - { - "name": "minecraft:element_56", - "id": -67 - }, - { - "name": "minecraft:black_glazed_terracotta", - "id": 235 - }, - { - "name": "minecraft:lit_redstone_ore", - "id": 74 - }, - { - "name": "minecraft:crafting_table", - "id": 58 - }, - { - "name": "minecraft:element_57", - "id": -68 - }, - { - "name": "minecraft:element_58", - "id": -69 - }, - { - "name": "minecraft:element_60", - "id": -71 - }, - { - "name" : "minecraft:element_64", - "id" : -75 - }, - { - "name" : "minecraft:element_65", - "id" : -76 - }, - { - "name": "minecraft:element_67", - "id": -78 - }, - { - "name": "minecraft:element_70", - "id": -81 - }, - { - "name": "minecraft:element_72", - "id": -83 - }, - { - "name": "minecraft:element_76", - "id": -87 - }, - { - "name": "minecraft:dark_oak_button", - "id": -142 - }, - { - "name": "minecraft:element_77", - "id": -88 - }, - { - "name": "minecraft:diorite_stairs", - "id": -170 - }, - { - "name": "minecraft:redstone_torch", - "id": 76 - }, - { - "name": "minecraft:element_79", - "id": -90 - }, - { - "name": "minecraft:iron_bars", - "id": 101 - }, - { - "name": "minecraft:element_80", - "id": -91 - }, - { - "name": "minecraft:element_81", - "id": -92 - }, - { - "name": "minecraft:element_82", - "id": -93 - }, - { - "name": "minecraft:underwater_torch", - "id": 239 - }, - { - "name": "minecraft:blue_ice", - "id": -11 - }, - { - "name": "minecraft:element_83", - "id": -94 - }, - { - "name": "minecraft:element_89", - "id": -100 - }, - { - "name": "minecraft:element_91", - "id": -102 - }, - { - "name": "minecraft:element_96", - "id": -107 - }, - { - "name": "minecraft:element_97", - "id": -108 - }, - { - "name": "minecraft:cactus", - "id": 81 - }, - { - "name" : "minecraft:element_99", - "id" : -110 - }, - { - "name": "minecraft:element_105", - "id": -116 - }, - { - "name": "minecraft:element_106", - "id": -117 - }, - { - "name": "minecraft:cyan_glazed_terracotta", - "id": 229 - }, - { - "name": "minecraft:element_107", - "id": -118 - }, - { - "name": "minecraft:element_108", - "id": -119 - }, - { - "name": "minecraft:element_109", - "id": -120 - }, - { - "name": "minecraft:element_112", - "id": -123 - }, - { - "name": "minecraft:warped_button", - "id": -261 - }, - { - "name": "minecraft:element_113", - "id": -124 - }, - { - "name": "minecraft:birch_stairs", - "id": 135 - }, - { - "name": "minecraft:element_114", - "id": -125 - }, - { - "name": "minecraft:composter", - "id": -213 - }, - { - "name": "minecraft:crying_obsidian", - "id": -289 - }, - { - "name": "minecraft:end_crystal", - "id": 426 - }, - { - "name": "minecraft:tripwire_hook", - "id": 131 - }, - { - "name": "minecraft:blue_glazed_terracotta", - "id": 231 - }, - { - "name": "minecraft:daylight_detector_inverted", - "id": 178 - }, - { - "name": "minecraft:warped_trapdoor", - "id": -247 - }, - { - "name": "minecraft:twisting_vines", - "id": -287 - }, - { - "name": "minecraft:noteblock", - "id": 25 - }, - { - "name": "minecraft:gravel", - "id": 13 - }, - { - "name": "minecraft:golden_rail", - "id": 27 - }, - { - "name": "minecraft:warped_wall_sign", - "id": -253 - }, - { - "name": "minecraft:oak_stairs", - "id": 53 - }, - { - "name": "minecraft:grass", - "id": 2 - }, - { - "name": "minecraft:acacia_button", - "id": -140 - }, - { - "name": "minecraft:snow", - "id": 80 - }, - { - "name": "minecraft:detector_rail", - "id": 28 - }, - { - "name": "minecraft:dark_oak_trapdoor", - "id": -147 - }, - { - "name": "minecraft:spruce_pressure_plate", - "id": -154 - }, - { - "name": "minecraft:water", - "id": 9 - }, - { - "name": "minecraft:furnace", - "id": 61 - }, - { - "name": "minecraft:item.wooden_door", - "id": 64 - }, - { - "name": "minecraft:gold_ore", - "id": 14 - }, - { - "name": "minecraft:web", - "id": 30 - }, - { - "name": "minecraft:unlit_redstone_torch", - "id": 75 - }, - { - "name": "minecraft:ladder", - "id": 65 - }, - { - "name": "minecraft:sweet_berry_bush", - "id": -207 - }, - { - "name": "minecraft:standing_sign", - "id": 63 - }, - { - "name": "minecraft:glass", - "id": 20 - }, - { - "name": "minecraft:lapis_ore", - "id": 21 - }, - { - "name": "minecraft:bookshelf", - "id": 47 - }, - { - "name": "minecraft:item.bed", - "id": 26 - }, - { - "name": "minecraft:stripped_warped_hyphae", - "id": -301 - }, - { - "name": "minecraft:wither_rose", - "id": -216 - }, - { - "name": "minecraft:wooden_pressure_plate", - "id": 72 - }, - { - "name": "minecraft:powered_comparator", - "id": 150 - }, - { - "name": "minecraft:lapis_block", - "id": 22 - }, - { - "name": "minecraft:dispenser", - "id": 23 - }, - { - "name": "minecraft:item.wheat", - "id": 59 - }, - { - "name": "minecraft:item.spruce_door", - "id": 193 - }, - { - "name": "minecraft:diamond_ore", - "id": 56 - }, - { - "name": "minecraft:deadbush", - "id": 32 - }, - { - "name": "minecraft:pistonarmcollision", - "id": 34 - }, - { - "name": "minecraft:blackstone_stairs", - "id": -276 - }, - { - "name": "minecraft:dried_kelp_block", - "id": -139 - }, - { - "name": "minecraft:item.soul_campfire", - "id": -290 - }, - { - "name": "minecraft:green_glazed_terracotta", - "id": 233 - }, - { - "name": "minecraft:crimson_pressure_plate", - "id": -262 - }, - { - "name": "minecraft:spruce_fence_gate", - "id": 183 - }, - { - "name": "minecraft:iron_block", - "id": 42 - }, - { - "name": "minecraft:lever", - "id": 69 - }, - { - "name": "minecraft:mossy_cobblestone", - "id": 48 - }, - { - "name": "minecraft:torch", - "id": 50 - }, - { - "name": "minecraft:acacia_fence_gate", - "id": 187 - }, - { - "name": "minecraft:quartz_stairs", - "id": 156 - }, - { - "name": "minecraft:dragon_egg", - "id": 122 - }, - { - "name": "minecraft:lava_cauldron", - "id": -210 - }, - { - "name": "minecraft:jungle_standing_sign", - "id": -188 - }, - { - "name": "minecraft:redstone_wire", - "id": 55 - }, - { - "name": "minecraft:jungle_wall_sign", - "id": -189 - }, - { - "name": "minecraft:lit_furnace", - "id": 62 - }, - { - "name": "minecraft:beehive", - "id": -219 - }, - { - "name": "minecraft:crimson_wall_sign", - "id": -252 - }, - { - "name": "minecraft:stone_stairs", - "id": 67 - }, - { - "name": "minecraft:orange_glazed_terracotta", - "id": 221 - }, - { - "name": "minecraft:brick_stairs", - "id": 108 - }, - { - "name": "minecraft:wall_sign", - "id": 68 - }, - { - "name": "minecraft:warped_nylium", - "id": -233 - }, - { - "name": "minecraft:quartz_bricks", - "id": -304 - }, - { - "name" : "minecraft:item.iron_door", - "id" : 71 - }, - { - "name": "minecraft:redstone_ore", - "id": 73 - }, - { - "name": "minecraft:lectern", - "id": -194 - }, - { - "name": "minecraft:gilded_blackstone", - "id": -281 - }, - { - "name" : "minecraft:red_nether_brick_stairs", - "id" : -184 - }, - { - "name": "minecraft:basalt", - "id": -234 - }, - { - "name": "minecraft:stone_button", - "id": 77 - }, - { - "name" : "minecraft:netherrack", - "id" : 87 - }, - { - "name": "minecraft:nether_brick_stairs", - "id": 114 - }, - { - "name": "minecraft:item.acacia_door", - "id": 196 - }, - { - "name": "minecraft:item.cake", - "id": 92 - }, - { - "name" : "minecraft:unpowered_repeater", - "id" : 93 - }, - { - "name": "minecraft:powered_repeater", - "id": 94 - }, - { - "name": "minecraft:trapdoor", - "id": 96 - }, - { - "name": "minecraft:coral_fan_hang3", - "id": -137 - }, - { - "name": "minecraft:item.jungle_door", - "id": 195 - }, - { - "name": "minecraft:glass_pane", - "id": 102 - }, - { - "name": "minecraft:emerald_ore", - "id": 129 - }, - { - "name": "minecraft:crimson_planks", - "id": -242 - }, - { - "name": "minecraft:crimson_stem", - "id": -225 - }, - { - "name": "minecraft:weeping_vines", - "id": -231 - }, - { - "name": "minecraft:pumpkin_stem", - "id": 104 - }, - { - "name": "minecraft:emerald_block", - "id": 133 - }, - { - "name": "minecraft:melon_stem", - "id": 105 - }, - { - "name": "minecraft:chemical_heat", - "id": 192 - }, - { - "name": "minecraft:warped_wart_block", - "id": -227 - }, - { - "name": "minecraft:vine", - "id": 106 - }, - { - "name": "minecraft:bamboo_sapling", - "id": -164 - }, - { - "name": "minecraft:standing_banner", - "id": 176 - }, - { - "name": "minecraft:mycelium", - "id": 110 - }, - { - "name": "minecraft:nether_gold_ore", - "id": -288 - }, - { - "name": "minecraft:nether_brick", - "id": 112 - }, - { - "name": "minecraft:warped_double_slab", - "id": -267 - }, - { - "name": "minecraft:nether_brick_fence", - "id": 113 - }, - { - "name": "minecraft:sandstone_stairs", - "id": 128 - }, - { - "name": "minecraft:item.nether_wart", - "id": 115 - }, - { - "name": "minecraft:enchanting_table", - "id": 116 - }, - { - "name": "minecraft:end_stone", - "id": 121 - }, - { - "name": "minecraft:redstone_lamp", - "id": 123 - }, - { - "name": "minecraft:jungle_stairs", - "id": 136 - }, - { - "name": "minecraft:dropper", - "id": 125 - }, - { - "name" : "minecraft:activator_rail", - "id" : 126 - }, - { - "name" : "minecraft:cocoa", - "id" : 127 - }, - { - "name": "minecraft:soul_torch", - "id": -268 - }, - { - "name" : "minecraft:info_update", - "id" : 248 - }, - { - "name": "minecraft:packed_ice", - "id": 174 - }, - { - "name" : "minecraft:coral_fan_hang", - "id" : -135 - }, - { - "name" : "minecraft:item.flower_pot", - "id" : 140 - }, - { - "name" : "minecraft:potatoes", - "id" : 142 - }, - { - "name" : "minecraft:wooden_button", - "id" : 143 - }, - { - "name" : "minecraft:item.skull", - "id" : 144 - }, - { - "name" : "minecraft:heavy_weighted_pressure_plate", - "id" : 148 - }, - { - "name": "minecraft:purple_glazed_terracotta", - "id": 219 - }, - { - "name": "minecraft:stripped_jungle_log", - "id": -7 - }, - { - "name": "minecraft:hardened_clay", - "id": 172 - }, - { - "name": "minecraft:warped_planks", - "id": -243 - }, - { - "name": "minecraft:acacia_wall_sign", - "id": -191 - }, - { - "name": "minecraft:purpur_stairs", - "id": 203 - }, - { - "name": "minecraft:wall_banner", - "id": 177 - }, - { - "name": "minecraft:spruce_trapdoor", - "id": -149 - }, - { - "name": "minecraft:repeating_command_block", - "id": 188 - }, - { - "name": "minecraft:item.chain", - "id": -286 - }, - { - "name": "minecraft:item.birch_door", - "id": 194 - }, - { - "name": "minecraft:grass_path", - "id": 198 - }, - { - "name": "minecraft:blackstone", - "id": -273 - }, - { - "name": "minecraft:chorus_flower", - "id": 200 - }, - { - "name": "minecraft:normal_stone_stairs", - "id": -180 - }, - { - "name": "minecraft:barrier", - "id": -161 - }, - { - "name": "minecraft:frosted_ice", - "id": 207 - }, - { - "name": "minecraft:structure_block", - "id": 252 - }, - { - "name": "minecraft:allow", - "id": 210 - }, - { - "name": "minecraft:pink_glazed_terracotta", - "id": 226 - }, - { - "name": "minecraft:deny", - "id": 211 - }, - { - "name": "minecraft:border_block", - "id": 212 - }, - { - "name": "minecraft:movingblock", - "id": 250 - }, - { - "name": "minecraft:bone_block", - "id": 216 - }, - { - "name": "minecraft:structure_void", - "id": 217 - }, - { - "name" : "minecraft:white_glazed_terracotta", - "id" : 220 - }, - { - "name" : "minecraft:magenta_glazed_terracotta", - "id" : 222 - }, - { - "name": "minecraft:lime_glazed_terracotta", - "id": 225 - }, - { - "name": "minecraft:gray_glazed_terracotta", - "id": 227 - }, - { - "name": "minecraft:brown_glazed_terracotta", - "id": 232 - }, - { - "name": "minecraft:red_glazed_terracotta", - "id": 234 - }, - { - "name": "minecraft:crimson_nylium", - "id": -232 - }, - { - "name": "minecraft:acacia_trapdoor", - "id": -145 - }, - { - "name": "minecraft:smooth_sandstone_stairs", - "id": -177 - }, - { - "name": "minecraft:item.camera", - "id": 242 - }, - { - "name": "minecraft:podzol", - "id": 243 - }, - { - "name": "minecraft:stonecutter", - "id": 245 - }, - { - "name": "minecraft:netherreactor", - "id": 247 - }, - { - "name": "minecraft:prismarine_stairs", - "id": -2 - }, - { - "name": "minecraft:dark_prismarine_stairs", - "id": -3 - }, - { - "name": "minecraft:stripped_spruce_log", - "id": -5 - }, - { - "name": "minecraft:stripped_birch_log", - "id": -6 - }, - { - "name": "minecraft:stripped_dark_oak_log", - "id": -9 - }, - { - "name": "minecraft:fire", - "id": 51 - }, - { - "name": "minecraft:hard_glass", - "id": 253 - }, - { - "name": "minecraft:acacia_standing_sign", - "id": -190 - }, - { - "name": "minecraft:hard_glass_pane", - "id": 190 - }, - { - "name": "minecraft:mossy_cobblestone_stairs", - "id": -179 - }, - { - "name": "minecraft:crimson_fence_gate", - "id": -258 - }, - { - "name": "minecraft:mossy_stone_brick_stairs", - "id": -175 - }, - { - "name": "minecraft:item.nether_sprouts", - "id": -238 - }, - { - "name": "minecraft:polished_blackstone_brick_double_slab", - "id": -285 - }, - { - "name": "minecraft:cracked_polished_blackstone_bricks", - "id": -280 - }, - { - "name": "minecraft:smooth_red_sandstone_stairs", - "id": -176 - }, - { - "name": "minecraft:shroomlight", - "id": -230 - }, - { - "name": "minecraft:stripped_crimson_hyphae", - "id": -300 - }, - { - "name": "minecraft:crimson_button", - "id": -260 - }, - { - "name": "minecraft:soul_fire", - "id": -237 - }, - { - "name": "minecraft:polished_basalt", - "id": -235 - }, - { - "name": "minecraft:jungle_button", - "id": -143 - }, - { - "name": "minecraft:birch_pressure_plate", - "id": -151 - }, - { - "name": "minecraft:stripped_warped_stem", - "id": -241 - }, - { - "name": "minecraft:birch_wall_sign", - "id": -187 - }, - { - "name": "minecraft:jungle_trapdoor", - "id": -148 - }, - { - "name": "minecraft:item.kelp", - "id": -138 - }, - { - "name": "minecraft:birch_button", - "id": -141 - }, - { - "name": "minecraft:birch_trapdoor", - "id": -146 - }, - { - "name": "minecraft:bubble_column", - "id": -160 - }, - { - "name": "minecraft:polished_granite_stairs", - "id": -172 - }, - { - "name": "minecraft:polished_andesite_stairs", - "id": -174 - }, - { - "name": "minecraft:end_brick_stairs", - "id": -178 - }, - { - "name": "minecraft:spruce_wall_sign", - "id": -182 - }, - { - "name": "minecraft:chiseled_polished_blackstone", - "id": -279 - }, - { - "name": "minecraft:birch_standing_sign", - "id": -186 - }, - { - "name": "minecraft:darkoak_standing_sign", - "id": -192 - }, - { - "name": "minecraft:darkoak_wall_sign", - "id": -193 - }, - { - "name": "minecraft:lit_smoker", - "id": -199 - }, - { - "name": "minecraft:item.campfire", - "id": -209 - }, - { - "name": "minecraft:bee_nest", - "id": -218 - }, - { - "name": "minecraft:warped_fence_gate", - "id": -259 - }, - { - "name": "minecraft:warped_stem", - "id": -226 - }, - { - "name": "minecraft:blackstone_double_slab", - "id": -283 - }, - { - "name": "minecraft:target", - "id": -239 - }, - { - "name": "minecraft:crimson_trapdoor", - "id": -246 - }, - { - "name": "minecraft:polished_blackstone_brick_wall", - "id": -278 - }, - { - "name": "minecraft:warped_standing_sign", - "id": -251 - }, - { - "name": "minecraft:warped_stairs", - "id": -255 - }, - { - "name": "minecraft:crimson_fence", - "id": -256 - }, - { - "name": "minecraft:warped_pressure_plate", - "id": -263 - }, - { - "name": "minecraft:soul_lantern", - "id": -269 - }, - { - "name": "minecraft:blackstone_wall", - "id": -277 - }, - { - "name": "minecraft:polished_blackstone", - "id": -291 - }, - { - "name": "minecraft:polished_blackstone_stairs", - "id": -292 - }, - { - "name": "minecraft:polished_blackstone_pressure_plate", - "id": -295 - }, - { - "name": "minecraft:warped_hyphae", - "id": -298 - }, - { - "name": "minecraft:crimson_hyphae", - "id": -299 - }, - { - "name": "minecraft:cracked_nether_bricks", - "id": -303 - } -] \ No newline at end of file +[{"oldData":4,"name":"minecraft:acacia_boat","id":377,"oldId":333},{"name":"minecraft:acacia_button","id":-140,"oldId":-140},{"name":"minecraft:acacia_door","id":546,"oldId":430},{"name":"minecraft:acacia_fence_gate","id":187,"oldId":187},{"name":"minecraft:acacia_pressure_plate","id":-150,"oldId":-150},{"name":"minecraft:acacia_sign","id":569,"oldId":475},{"name":"minecraft:acacia_stairs","id":163,"oldId":163},{"name":"minecraft:acacia_standing_sign","id":-190,"oldId":-190},{"name":"minecraft:acacia_trapdoor","id":-145,"oldId":-145},{"name":"minecraft:acacia_wall_sign","id":-191,"oldId":-191},{"name":"minecraft:activator_rail","id":126,"oldId":126},{"name":"minecraft:agent_spawn_egg","id":485,"oldId":383,"oldData":56},{"name":"minecraft:air","id":-158,"oldId":-158},{"name":"minecraft:allow","id":210,"oldId":210},{"name":"minecraft:ancient_debris","id":-271,"oldId":-271},{"name":"minecraft:andesite_stairs","id":-171,"oldId":-171},{"name":"minecraft:anvil","id":145,"oldId":145},{"name":"minecraft:apple","id":257,"oldId":260},{"name":"minecraft:armor_stand","id":542,"oldId":425},{"name":"minecraft:arrow","id":301,"oldId":262},{"name":"minecraft:baked_potato","id":281,"oldId":393},{"name":"minecraft:balloon","id":588,"oldId":448},{"name":"minecraft:bamboo","id":-163,"oldId":-163},{"name":"minecraft:bamboo_sapling","id":-164,"oldId":-164},{"name":"minecraft:banner","id":557,"oldId":446},{"name":"minecraft:banner_pattern","id":614,"oldId":434},{"name":"minecraft:barrel","id":-203,"oldId":-203},{"name":"minecraft:barrier","id":-161,"oldId":-161},{"name":"minecraft:basalt","id":-234,"oldId":-234},{"name":"minecraft:bat_spawn_egg","id":451,"oldId":383,"oldData":19},{"name":"minecraft:beacon","id":138,"oldId":138},{"name":"minecraft:bed","id":416,"oldId":355},{"name":"minecraft:bedrock","id":7,"oldId":7},{"name":"minecraft:bee_nest","id":-218,"oldId":-218},{"name":"minecraft:bee_spawn_egg","id":492,"oldId":383,"oldData":122},{"name":"minecraft:beef","id":273,"oldId":363},{"name":"minecraft:beehive","id":-219,"oldId":-219},{"name":"minecraft:beetroot","id":285,"oldId":457},{"name":"minecraft:beetroot_seeds","id":295,"oldId":458},{"name":"minecraft:beetroot_soup","id":286,"oldId":459},{"name":"minecraft:bell","id":-206,"oldId":-206},{"oldData":2,"name":"minecraft:birch_boat","id":374,"oldId":333},{"name":"minecraft:birch_button","id":-141,"oldId":-141},{"name":"minecraft:birch_door","id":544,"oldId":428},{"name":"minecraft:birch_fence_gate","id":184,"oldId":184},{"name":"minecraft:birch_pressure_plate","id":-151,"oldId":-151},{"name":"minecraft:birch_sign","id":567,"oldId":473},{"name":"minecraft:birch_stairs","id":135,"oldId":135},{"name":"minecraft:birch_standing_sign","id":-186,"oldId":-186},{"name":"minecraft:birch_trapdoor","id":-146,"oldId":-146},{"name":"minecraft:birch_wall_sign","id":-187,"oldId":-187},{"oldData":16,"name":"minecraft:black_dye","id":393,"oldId":351},{"name":"minecraft:black_glazed_terracotta","id":235,"oldId":235},{"name":"minecraft:blackstone","id":-273,"oldId":-273},{"name":"minecraft:blackstone_double_slab","id":-283,"oldId":-283},{"name":"minecraft:blackstone_slab","id":-282,"oldId":-282},{"name":"minecraft:blackstone_stairs","id":-276,"oldId":-276},{"name":"minecraft:blackstone_wall","id":-277,"oldId":-277},{"name":"minecraft:blast_furnace","id":-196,"oldId":-196},{"name":"minecraft:blaze_powder","id":427,"oldId":377},{"name":"minecraft:blaze_rod","id":421,"oldId":369},{"name":"minecraft:blaze_spawn_egg","id":454,"oldId":383,"oldData":43},{"name":"minecraft:bleach","id":586,"oldId":451},{"oldData":18,"name":"minecraft:blue_dye","id":397,"oldId":351},{"name":"minecraft:blue_glazed_terracotta","id":231,"oldId":231},{"name":"minecraft:blue_ice","id":-11,"oldId":-11},{"name":"minecraft:boat","id":612,"oldId":333},{"name":"minecraft:bone","id":413,"oldId":352},{"name":"minecraft:bone_block","id":216,"oldId":216},{"oldData":15,"name":"minecraft:bone_meal","id":409,"oldId":351},{"name":"minecraft:book","id":385,"oldId":340},{"name":"minecraft:bookshelf","id":47,"oldId":47},{"name":"minecraft:border_block","id":212,"oldId":212},{"oldData":5,"name":"minecraft:bordure_indented_banner_pattern","id":576,"oldId":434},{"name":"minecraft:bow","id":300,"oldId":261},{"name":"minecraft:bowl","id":321,"oldId":281},{"name":"minecraft:bread","id":261,"oldId":297},{"name":"minecraft:brewing_stand","id":429,"oldId":379},{"name":"minecraft:brewingstandblock","id":117,"oldId":117},{"name":"minecraft:brick","id":381,"oldId":336},{"name":"minecraft:brick_block","id":45,"oldId":45},{"name":"minecraft:brick_stairs","id":108,"oldId":108},{"oldData":17,"name":"minecraft:brown_dye","id":396,"oldId":351},{"name":"minecraft:brown_glazed_terracotta","id":232,"oldId":232},{"name":"minecraft:brown_mushroom","id":39,"oldId":39},{"name":"minecraft:brown_mushroom_block","id":99,"oldId":99},{"name":"minecraft:bubble_column","id":-160,"oldId":-160},{"oldData":0,"name":"minecraft:bucket","id":360,"oldId":325},{"name":"minecraft:cactus","id":81,"oldId":81},{"name":"minecraft:cake","id":415,"oldId":354},{"name":"minecraft:camera","id":583,"oldId":498},{"name":"minecraft:campfire","id":578,"oldId":720},{"name":"minecraft:carpet","id":171,"oldId":171},{"name":"minecraft:carrot","id":279,"oldId":391},{"name":"minecraft:carrot_on_a_stick","id":507,"oldId":398},{"name":"minecraft:carrots","id":141,"oldId":141},{"name":"minecraft:cartography_table","id":-200,"oldId":-200},{"name":"minecraft:carved_pumpkin","id":-155,"oldId":-155},{"name":"minecraft:cat_spawn_egg","id":486,"oldId":383,"oldData":75},{"name":"minecraft:cauldron","id":430,"oldId":380},{"name":"minecraft:cave_spider_spawn_egg","id":455,"oldId":383,"oldData":40},{"name":"minecraft:chain","id":608,"oldId":758},{"name":"minecraft:chain_command_block","id":189,"oldId":189},{"name":"minecraft:chainmail_boots","id":342,"oldId":305},{"name":"minecraft:chainmail_chestplate","id":340,"oldId":303},{"name":"minecraft:chainmail_helmet","id":339,"oldId":302},{"name":"minecraft:chainmail_leggings","id":341,"oldId":304},{"oldData":1,"name":"minecraft:charcoal","id":303,"oldId":263},{"name":"minecraft:chemical_heat","id":192,"oldId":192},{"name":"minecraft:chemistry_table","id":238,"oldId":238},{"name":"minecraft:chest","id":54,"oldId":54},{"name":"minecraft:chest_minecart","id":387,"oldId":342},{"name":"minecraft:chicken","id":275,"oldId":365},{"name":"minecraft:chicken_spawn_egg","id":433,"oldId":383,"oldData":10},{"name":"minecraft:chiseled_nether_bricks","id":-302,"oldId":-302},{"name":"minecraft:chiseled_polished_blackstone","id":-279,"oldId":-279},{"name":"minecraft:chorus_flower","id":200,"oldId":200},{"name":"minecraft:chorus_fruit","id":548,"oldId":432},{"name":"minecraft:chorus_plant","id":240,"oldId":240},{"name":"minecraft:clay","id":82,"oldId":82},{"name":"minecraft:clay_ball","id":382,"oldId":337},{"name":"minecraft:clock","id":391,"oldId":347},{"oldData":0,"name":"minecraft:coal","id":302,"oldId":263},{"name":"minecraft:coal_block","id":173,"oldId":173},{"name":"minecraft:coal_ore","id":16,"oldId":16},{"name":"minecraft:cobblestone","id":4,"oldId":4},{"name":"minecraft:cobblestone_wall","id":139,"oldId":139},{"name":"minecraft:cocoa","id":127,"oldId":127},{"oldData":3,"name":"minecraft:cocoa_beans","id":410,"oldId":351},{"name":"minecraft:cod","id":264,"oldId":349},{"oldData":2,"name":"minecraft:cod_bucket","id":364,"oldId":325},{"name":"minecraft:cod_spawn_egg","id":478,"oldId":383,"oldData":112},{"name":"minecraft:colored_torch_bp","id":204,"oldId":204},{"name":"minecraft:colored_torch_rg","id":202,"oldId":202},{"name":"minecraft:command_block","id":137,"oldId":137},{"name":"minecraft:command_block_minecart","id":553,"oldId":443},{"name":"minecraft:comparator","id":512,"oldId":404},{"name":"minecraft:compass","id":389,"oldId":345},{"name":"minecraft:composter","id":-213,"oldId":-213},{"name":"minecraft:compound","id":584,"oldId":499},{"name":"minecraft:concrete","id":236,"oldId":236},{"name":"minecraft:concrete_powder","id":237,"oldId":237},{"name":"minecraft:conduit","id":-157,"oldId":-157},{"name":"minecraft:cooked_beef","id":274,"oldId":364},{"name":"minecraft:cooked_chicken","id":276,"oldId":366},{"name":"minecraft:cooked_cod","id":268,"oldId":350},{"name":"minecraft:cooked_mutton","id":541,"oldId":424},{"name":"minecraft:cooked_porkchop","id":263,"oldId":320},{"name":"minecraft:cooked_rabbit","id":289,"oldId":412},{"name":"minecraft:cooked_salmon","id":269,"oldId":463},{"name":"minecraft:cookie","id":271,"oldId":357},{"name":"minecraft:coral","id":-131,"oldId":-131},{"name":"minecraft:coral_block","id":-132,"oldId":-132},{"name":"minecraft:coral_fan","id":-133,"oldId":-133},{"name":"minecraft:coral_fan_dead","id":-134,"oldId":-134},{"name":"minecraft:coral_fan_hang","id":-135,"oldId":-135},{"name":"minecraft:coral_fan_hang2","id":-136,"oldId":-136},{"name":"minecraft:coral_fan_hang3","id":-137,"oldId":-137},{"name":"minecraft:cow_spawn_egg","id":434,"oldId":383,"oldData":11},{"name":"minecraft:cracked_nether_bricks","id":-303,"oldId":-303},{"name":"minecraft:cracked_polished_blackstone_bricks","id":-280,"oldId":-280},{"name":"minecraft:crafting_table","id":58,"oldId":58},{"oldData":0,"name":"minecraft:creeper_banner_pattern","id":572,"oldId":434},{"name":"minecraft:creeper_spawn_egg","id":439,"oldId":383,"oldData":33},{"name":"minecraft:crimson_button","id":-260,"oldId":-260},{"name":"minecraft:crimson_door","id":605,"oldId":755},{"name":"minecraft:crimson_double_slab","id":-266,"oldId":-266},{"name":"minecraft:crimson_fence","id":-256,"oldId":-256},{"name":"minecraft:crimson_fence_gate","id":-258,"oldId":-258},{"name":"minecraft:crimson_fungus","id":-228,"oldId":-228},{"name":"minecraft:crimson_hyphae","id":-299,"oldId":-299},{"name":"minecraft:crimson_nylium","id":-232,"oldId":-232},{"name":"minecraft:crimson_planks","id":-242,"oldId":-242},{"name":"minecraft:crimson_pressure_plate","id":-262,"oldId":-262},{"name":"minecraft:crimson_roots","id":-223,"oldId":-223},{"name":"minecraft:crimson_sign","id":603,"oldId":753},{"name":"minecraft:crimson_slab","id":-264,"oldId":-264},{"name":"minecraft:crimson_stairs","id":-254,"oldId":-254},{"name":"minecraft:crimson_standing_sign","id":-250,"oldId":-250},{"name":"minecraft:crimson_stem","id":-225,"oldId":-225},{"name":"minecraft:crimson_trapdoor","id":-246,"oldId":-246},{"name":"minecraft:crimson_wall_sign","id":-252,"oldId":-252},{"name":"minecraft:crossbow","id":565,"oldId":471},{"name":"minecraft:crying_obsidian","id":-289,"oldId":-289},{"oldData":6,"name":"minecraft:cyan_dye","id":399,"oldId":351},{"name":"minecraft:cyan_glazed_terracotta","id":229,"oldId":229},{"oldData":5,"name":"minecraft:dark_oak_boat","id":378,"oldId":333},{"name":"minecraft:dark_oak_button","id":-142,"oldId":-142},{"name":"minecraft:dark_oak_door","id":547,"oldId":431},{"name":"minecraft:dark_oak_fence_gate","id":186,"oldId":186},{"name":"minecraft:dark_oak_pressure_plate","id":-152,"oldId":-152},{"name":"minecraft:dark_oak_sign","id":570,"oldId":476},{"name":"minecraft:dark_oak_stairs","id":164,"oldId":164},{"name":"minecraft:dark_oak_trapdoor","id":-147,"oldId":-147},{"name":"minecraft:dark_prismarine_stairs","id":-3,"oldId":-3},{"name":"minecraft:darkoak_standing_sign","id":-192,"oldId":-192},{"name":"minecraft:darkoak_wall_sign","id":-193,"oldId":-193},{"name":"minecraft:daylight_detector","id":151,"oldId":151},{"name":"minecraft:daylight_detector_inverted","id":178,"oldId":178},{"name":"minecraft:deadbush","id":32,"oldId":32},{"name":"minecraft:debug_stick","id":580},{"name":"minecraft:deny","id":211,"oldId":211},{"name":"minecraft:detector_rail","id":28,"oldId":28},{"name":"minecraft:diamond","id":304,"oldId":264},{"name":"minecraft:diamond_axe","id":319,"oldId":279},{"name":"minecraft:diamond_block","id":57,"oldId":57},{"name":"minecraft:diamond_boots","id":350,"oldId":313},{"name":"minecraft:diamond_chestplate","id":348,"oldId":311},{"name":"minecraft:diamond_helmet","id":347,"oldId":310},{"name":"minecraft:diamond_hoe","id":332,"oldId":293},{"name":"minecraft:diamond_horse_armor","id":523,"oldId":419},{"name":"minecraft:diamond_leggings","id":349,"oldId":312},{"name":"minecraft:diamond_ore","id":56,"oldId":56},{"name":"minecraft:diamond_pickaxe","id":318,"oldId":278},{"name":"minecraft:diamond_shovel","id":317,"oldId":277},{"name":"minecraft:diamond_sword","id":316,"oldId":276},{"name":"minecraft:diorite_stairs","id":-170,"oldId":-170},{"name":"minecraft:dirt","id":3,"oldId":3},{"name":"minecraft:dispenser","id":23,"oldId":23},{"name":"minecraft:dolphin_spawn_egg","id":482,"oldId":383,"oldData":31},{"name":"minecraft:donkey_spawn_egg","id":463,"oldId":383,"oldData":24},{"name":"minecraft:double_plant","id":175,"oldId":175},{"name":"minecraft:double_stone_slab","id":44,"oldId":44},{"name":"minecraft:double_stone_slab2","id":182,"oldId":182},{"name":"minecraft:double_stone_slab3","id":-162,"oldId":-162},{"name":"minecraft:double_stone_slab4","id":-166,"oldId":-166},{"name":"minecraft:double_wooden_slab","id":157,"oldId":157},{"name":"minecraft:dragon_breath","id":550,"oldId":437},{"name":"minecraft:dragon_egg","id":122,"oldId":122},{"name":"minecraft:dried_kelp","id":270,"oldId":464},{"name":"minecraft:dried_kelp_block","id":-139,"oldId":-139},{"name":"minecraft:dropper","id":125,"oldId":125},{"name":"minecraft:drowned_spawn_egg","id":481,"oldId":383,"oldData":110},{"name":"minecraft:dye","id":613,"oldId":351},{"name":"minecraft:egg","id":388,"oldId":344},{"name":"minecraft:elder_guardian_spawn_egg","id":469,"oldId":383,"oldData":50},{"name":"minecraft:element_0","id":36,"oldId":36},{"name":"minecraft:element_1","id":-12,"oldId":-12},{"name":"minecraft:element_10","id":-21,"oldId":-21},{"name":"minecraft:element_100","id":-111,"oldId":-111},{"name":"minecraft:element_101","id":-112,"oldId":-112},{"name":"minecraft:element_102","id":-113,"oldId":-113},{"name":"minecraft:element_103","id":-114,"oldId":-114},{"name":"minecraft:element_104","id":-115,"oldId":-115},{"name":"minecraft:element_105","id":-116,"oldId":-116},{"name":"minecraft:element_106","id":-117,"oldId":-117},{"name":"minecraft:element_107","id":-118,"oldId":-118},{"name":"minecraft:element_108","id":-119,"oldId":-119},{"name":"minecraft:element_109","id":-120,"oldId":-120},{"name":"minecraft:element_11","id":-22,"oldId":-22},{"name":"minecraft:element_110","id":-121,"oldId":-121},{"name":"minecraft:element_111","id":-122,"oldId":-122},{"name":"minecraft:element_112","id":-123,"oldId":-123},{"name":"minecraft:element_113","id":-124,"oldId":-124},{"name":"minecraft:element_114","id":-125,"oldId":-125},{"name":"minecraft:element_115","id":-126,"oldId":-126},{"name":"minecraft:element_116","id":-127,"oldId":-127},{"name":"minecraft:element_117","id":-128,"oldId":-128},{"name":"minecraft:element_118","id":-129,"oldId":-129},{"name":"minecraft:element_12","id":-23,"oldId":-23},{"name":"minecraft:element_13","id":-24,"oldId":-24},{"name":"minecraft:element_14","id":-25,"oldId":-25},{"name":"minecraft:element_15","id":-26,"oldId":-26},{"name":"minecraft:element_16","id":-27,"oldId":-27},{"name":"minecraft:element_17","id":-28,"oldId":-28},{"name":"minecraft:element_18","id":-29,"oldId":-29},{"name":"minecraft:element_19","id":-30,"oldId":-30},{"name":"minecraft:element_2","id":-13,"oldId":-13},{"name":"minecraft:element_20","id":-31,"oldId":-31},{"name":"minecraft:element_21","id":-32,"oldId":-32},{"name":"minecraft:element_22","id":-33,"oldId":-33},{"name":"minecraft:element_23","id":-34,"oldId":-34},{"name":"minecraft:element_24","id":-35,"oldId":-35},{"name":"minecraft:element_25","id":-36,"oldId":-36},{"name":"minecraft:element_26","id":-37,"oldId":-37},{"name":"minecraft:element_27","id":-38,"oldId":-38},{"name":"minecraft:element_28","id":-39,"oldId":-39},{"name":"minecraft:element_29","id":-40,"oldId":-40},{"name":"minecraft:element_3","id":-14,"oldId":-14},{"name":"minecraft:element_30","id":-41,"oldId":-41},{"name":"minecraft:element_31","id":-42,"oldId":-42},{"name":"minecraft:element_32","id":-43,"oldId":-43},{"name":"minecraft:element_33","id":-44,"oldId":-44},{"name":"minecraft:element_34","id":-45,"oldId":-45},{"name":"minecraft:element_35","id":-46,"oldId":-46},{"name":"minecraft:element_36","id":-47,"oldId":-47},{"name":"minecraft:element_37","id":-48,"oldId":-48},{"name":"minecraft:element_38","id":-49,"oldId":-49},{"name":"minecraft:element_39","id":-50,"oldId":-50},{"name":"minecraft:element_4","id":-15,"oldId":-15},{"name":"minecraft:element_40","id":-51,"oldId":-51},{"name":"minecraft:element_41","id":-52,"oldId":-52},{"name":"minecraft:element_42","id":-53,"oldId":-53},{"name":"minecraft:element_43","id":-54,"oldId":-54},{"name":"minecraft:element_44","id":-55,"oldId":-55},{"name":"minecraft:element_45","id":-56,"oldId":-56},{"name":"minecraft:element_46","id":-57,"oldId":-57},{"name":"minecraft:element_47","id":-58,"oldId":-58},{"name":"minecraft:element_48","id":-59,"oldId":-59},{"name":"minecraft:element_49","id":-60,"oldId":-60},{"name":"minecraft:element_5","id":-16,"oldId":-16},{"name":"minecraft:element_50","id":-61,"oldId":-61},{"name":"minecraft:element_51","id":-62,"oldId":-62},{"name":"minecraft:element_52","id":-63,"oldId":-63},{"name":"minecraft:element_53","id":-64,"oldId":-64},{"name":"minecraft:element_54","id":-65,"oldId":-65},{"name":"minecraft:element_55","id":-66,"oldId":-66},{"name":"minecraft:element_56","id":-67,"oldId":-67},{"name":"minecraft:element_57","id":-68,"oldId":-68},{"name":"minecraft:element_58","id":-69,"oldId":-69},{"name":"minecraft:element_59","id":-70,"oldId":-70},{"name":"minecraft:element_6","id":-17,"oldId":-17},{"name":"minecraft:element_60","id":-71,"oldId":-71},{"name":"minecraft:element_61","id":-72,"oldId":-72},{"name":"minecraft:element_62","id":-73,"oldId":-73},{"name":"minecraft:element_63","id":-74,"oldId":-74},{"name":"minecraft:element_64","id":-75,"oldId":-75},{"name":"minecraft:element_65","id":-76,"oldId":-76},{"name":"minecraft:element_66","id":-77,"oldId":-77},{"name":"minecraft:element_67","id":-78,"oldId":-78},{"name":"minecraft:element_68","id":-79,"oldId":-79},{"name":"minecraft:element_69","id":-80,"oldId":-80},{"name":"minecraft:element_7","id":-18,"oldId":-18},{"name":"minecraft:element_70","id":-81,"oldId":-81},{"name":"minecraft:element_71","id":-82,"oldId":-82},{"name":"minecraft:element_72","id":-83,"oldId":-83},{"name":"minecraft:element_73","id":-84,"oldId":-84},{"name":"minecraft:element_74","id":-85,"oldId":-85},{"name":"minecraft:element_75","id":-86,"oldId":-86},{"name":"minecraft:element_76","id":-87,"oldId":-87},{"name":"minecraft:element_77","id":-88,"oldId":-88},{"name":"minecraft:element_78","id":-89,"oldId":-89},{"name":"minecraft:element_79","id":-90,"oldId":-90},{"name":"minecraft:element_8","id":-19,"oldId":-19},{"name":"minecraft:element_80","id":-91,"oldId":-91},{"name":"minecraft:element_81","id":-92,"oldId":-92},{"name":"minecraft:element_82","id":-93,"oldId":-93},{"name":"minecraft:element_83","id":-94,"oldId":-94},{"name":"minecraft:element_84","id":-95,"oldId":-95},{"name":"minecraft:element_85","id":-96,"oldId":-96},{"name":"minecraft:element_86","id":-97,"oldId":-97},{"name":"minecraft:element_87","id":-98,"oldId":-98},{"name":"minecraft:element_88","id":-99,"oldId":-99},{"name":"minecraft:element_89","id":-100,"oldId":-100},{"name":"minecraft:element_9","id":-20,"oldId":-20},{"name":"minecraft:element_90","id":-101,"oldId":-101},{"name":"minecraft:element_91","id":-102,"oldId":-102},{"name":"minecraft:element_92","id":-103,"oldId":-103},{"name":"minecraft:element_93","id":-104,"oldId":-104},{"name":"minecraft:element_94","id":-105,"oldId":-105},{"name":"minecraft:element_95","id":-106,"oldId":-106},{"name":"minecraft:element_96","id":-107,"oldId":-107},{"name":"minecraft:element_97","id":-108,"oldId":-108},{"name":"minecraft:element_98","id":-109,"oldId":-109},{"name":"minecraft:element_99","id":-110,"oldId":-110},{"name":"minecraft:elytra","id":554,"oldId":444},{"name":"minecraft:emerald","id":502,"oldId":388},{"name":"minecraft:emerald_block","id":133,"oldId":133},{"name":"minecraft:emerald_ore","id":129,"oldId":129},{"name":"minecraft:empty_map","id":505,"oldId":395},{"name":"minecraft:enchanted_book","id":511,"oldId":403},{"name":"minecraft:enchanted_golden_apple","id":259,"oldId":466},{"name":"minecraft:enchanting_table","id":116,"oldId":116},{"name":"minecraft:end_brick_stairs","id":-178,"oldId":-178},{"name":"minecraft:end_bricks","id":206,"oldId":206},{"name":"minecraft:end_crystal","id":616,"oldId":426},{"name":"minecraft:end_gateway","id":209,"oldId":209},{"name":"minecraft:end_portal","id":119,"oldId":119},{"name":"minecraft:end_portal_frame","id":120,"oldId":120},{"name":"minecraft:end_rod","id":208,"oldId":208},{"name":"minecraft:end_stone","id":121,"oldId":121},{"name":"minecraft:ender_chest","id":130,"oldId":130},{"name":"minecraft:ender_eye","id":431,"oldId":381},{"name":"minecraft:ender_pearl","id":420,"oldId":368},{"name":"minecraft:enderman_spawn_egg","id":440,"oldId":383,"oldData":38},{"name":"minecraft:endermite_spawn_egg","id":458,"oldId":383,"oldData":55},{"name":"minecraft:evoker_spawn_egg","id":473,"oldId":383,"oldData":104},{"name":"minecraft:experience_bottle","id":498,"oldId":384},{"name":"minecraft:farmland","id":60,"oldId":60},{"name":"minecraft:feather","id":327,"oldId":288},{"name":"minecraft:fence","id":85,"oldId":85},{"name":"minecraft:fence_gate","id":107,"oldId":107},{"name":"minecraft:fermented_spider_eye","id":426,"oldId":376},{"oldData":4,"name":"minecraft:field_masoned_banner_pattern","id":575,"oldId":434},{"name":"minecraft:filled_map","id":418,"oldId":358},{"name":"minecraft:fire","id":51,"oldId":51},{"name":"minecraft:fire_charge","id":499,"oldId":385},{"name":"minecraft:firework_rocket","id":509,"oldId":401},{"name":"minecraft:firework_star","id":510,"oldId":402},{"name":"minecraft:fishing_rod","id":390,"oldId":346},{"name":"minecraft:fletching_table","id":-201,"oldId":-201},{"name":"minecraft:flint","id":356,"oldId":318},{"name":"minecraft:flint_and_steel","id":299,"oldId":259},{"oldData":2,"name":"minecraft:flower_banner_pattern","id":571,"oldId":434},{"name":"minecraft:flower_pot","id":504,"oldId":390},{"name":"minecraft:flowing_lava","id":10,"oldId":10},{"name":"minecraft:flowing_water","id":8,"oldId":8},{"name":"minecraft:fox_spawn_egg","id":488,"oldId":383,"oldData":121},{"name":"minecraft:frame","id":503,"oldId":389},{"name":"minecraft:frosted_ice","id":207,"oldId":207},{"name":"minecraft:furnace","id":61,"oldId":61},{"name":"minecraft:ghast_spawn_egg","id":452,"oldId":383,"oldData":41},{"name":"minecraft:ghast_tear","id":422,"oldId":370},{"name":"minecraft:gilded_blackstone","id":-281,"oldId":-281},{"name":"minecraft:glass","id":20,"oldId":20},{"name":"minecraft:glass_bottle","id":425,"oldId":374},{"name":"minecraft:glass_pane","id":102,"oldId":102},{"name":"minecraft:glistering_melon_slice","id":432,"oldId":382},{"name":"minecraft:glow_stick","id":166,"oldId":166},{"name":"minecraft:glowingobsidian","id":246,"oldId":246},{"name":"minecraft:glowstone","id":89,"oldId":89},{"name":"minecraft:glowstone_dust","id":392,"oldId":348},{"name":"minecraft:gold_block","id":41,"oldId":41},{"name":"minecraft:gold_ingot","id":306,"oldId":266},{"name":"minecraft:gold_nugget","id":423,"oldId":371},{"name":"minecraft:gold_ore","id":14,"oldId":14},{"name":"minecraft:golden_apple","id":258,"oldId":322},{"name":"minecraft:golden_axe","id":325,"oldId":286},{"name":"minecraft:golden_boots","id":354,"oldId":317},{"name":"minecraft:golden_carrot","id":283,"oldId":396},{"name":"minecraft:golden_chestplate","id":352,"oldId":315},{"name":"minecraft:golden_helmet","id":351,"oldId":314},{"name":"minecraft:golden_hoe","id":333,"oldId":294},{"name":"minecraft:golden_horse_armor","id":522,"oldId":418},{"name":"minecraft:golden_leggings","id":353,"oldId":316},{"name":"minecraft:golden_pickaxe","id":324,"oldId":285},{"name":"minecraft:golden_rail","id":27,"oldId":27},{"name":"minecraft:golden_shovel","id":323,"oldId":284},{"name":"minecraft:golden_sword","id":322,"oldId":283},{"name":"minecraft:granite_stairs","id":-169,"oldId":-169},{"name":"minecraft:grass","id":2,"oldId":2},{"name":"minecraft:grass_path","id":198,"oldId":198},{"name":"minecraft:gravel","id":13,"oldId":13},{"oldData":8,"name":"minecraft:gray_dye","id":401,"oldId":351},{"name":"minecraft:gray_glazed_terracotta","id":227,"oldId":227},{"oldData":2,"name":"minecraft:green_dye","id":395,"oldId":351},{"name":"minecraft:green_glazed_terracotta","id":233,"oldId":233},{"name":"minecraft:grindstone","id":-195,"oldId":-195},{"name":"minecraft:guardian_spawn_egg","id":459,"oldId":383,"oldData":49},{"name":"minecraft:gunpowder","id":328,"oldId":289},{"name":"minecraft:hard_glass","id":253,"oldId":253},{"name":"minecraft:hard_glass_pane","id":190,"oldId":190},{"name":"minecraft:hard_stained_glass","id":254,"oldId":254},{"name":"minecraft:hard_stained_glass_pane","id":191,"oldId":191},{"name":"minecraft:hardened_clay","id":172,"oldId":172},{"name":"minecraft:hay_block","id":170,"oldId":170},{"name":"minecraft:heart_of_the_sea","id":561,"oldId":467},{"name":"minecraft:heavy_weighted_pressure_plate","id":148,"oldId":148},{"name":"minecraft:hoglin_spawn_egg","id":494,"oldId":383,"oldData":124},{"name":"minecraft:honey_block","id":-220,"oldId":-220},{"name":"minecraft:honey_bottle","id":582,"oldId":737},{"name":"minecraft:honeycomb","id":581,"oldId":736},{"name":"minecraft:honeycomb_block","id":-221,"oldId":-221},{"name":"minecraft:hopper","id":517,"oldId":410},{"name":"minecraft:hopper_minecart","id":516,"oldId":408},{"name":"minecraft:horse_spawn_egg","id":456,"oldId":383,"oldData":23},{"name":"minecraft:husk_spawn_egg","id":461,"oldId":383,"oldData":47},{"name":"minecraft:ice","id":79,"oldId":79},{"name":"minecraft:ice_bomb","id":585,"oldId":453},{"name":"minecraft:info_update","id":248,"oldId":248},{"name":"minecraft:info_update2","id":249,"oldId":249},{"oldData":0,"name":"minecraft:ink_sac","id":411,"oldId":351},{"name":"minecraft:invisiblebedrock","id":95,"oldId":95},{"name":"minecraft:iron_axe","id":298,"oldId":258},{"name":"minecraft:iron_bars","id":101,"oldId":101},{"name":"minecraft:iron_block","id":42,"oldId":42},{"name":"minecraft:iron_boots","id":346,"oldId":309},{"name":"minecraft:iron_chestplate","id":344,"oldId":307},{"name":"minecraft:iron_door","id":370,"oldId":330},{"name":"minecraft:iron_helmet","id":343,"oldId":306},{"name":"minecraft:iron_hoe","id":331,"oldId":292},{"name":"minecraft:iron_horse_armor","id":521,"oldId":417},{"name":"minecraft:iron_ingot","id":305,"oldId":265},{"name":"minecraft:iron_leggings","id":345,"oldId":308},{"name":"minecraft:iron_nugget","id":559,"oldId":452},{"name":"minecraft:iron_ore","id":15,"oldId":15},{"name":"minecraft:iron_pickaxe","id":297,"oldId":257},{"name":"minecraft:iron_shovel","id":296,"oldId":256},{"name":"minecraft:iron_sword","id":307,"oldId":267},{"name":"minecraft:iron_trapdoor","id":167,"oldId":167},{"name":"minecraft:item.acacia_door","id":196,"oldId":196},{"name":"minecraft:item.bed","id":26,"oldId":26},{"name":"minecraft:item.beetroot","id":244,"oldId":244},{"name":"minecraft:item.birch_door","id":194,"oldId":194},{"name":"minecraft:item.cake","id":92,"oldId":92},{"name":"minecraft:item.camera","id":242,"oldId":242},{"name":"minecraft:item.campfire","id":-209,"oldId":-209},{"name":"minecraft:item.cauldron","id":118,"oldId":118},{"name":"minecraft:item.chain","id":-286,"oldId":-286},{"name":"minecraft:item.crimson_door","id":-244,"oldId":-244},{"name":"minecraft:item.dark_oak_door","id":197,"oldId":197},{"name":"minecraft:item.flower_pot","id":140,"oldId":140},{"name":"minecraft:item.frame","id":199,"oldId":199},{"name":"minecraft:item.hopper","id":154,"oldId":154},{"name":"minecraft:item.iron_door","id":71,"oldId":71},{"name":"minecraft:item.jungle_door","id":195,"oldId":195},{"name":"minecraft:item.kelp","id":-138,"oldId":-138},{"name":"minecraft:item.nether_sprouts","id":-238,"oldId":-238},{"name":"minecraft:item.nether_wart","id":115,"oldId":115},{"name":"minecraft:item.reeds","id":83,"oldId":83},{"name":"minecraft:item.skull","id":144,"oldId":144},{"name":"minecraft:item.soul_campfire","id":-290,"oldId":-290},{"name":"minecraft:item.spruce_door","id":193,"oldId":193},{"name":"minecraft:item.warped_door","id":-245,"oldId":-245},{"name":"minecraft:item.wheat","id":59,"oldId":59},{"name":"minecraft:item.wooden_door","id":64,"oldId":64},{"name":"minecraft:jigsaw","id":-211,"oldId":-211},{"name":"minecraft:jukebox","id":84,"oldId":84},{"oldData":3,"name":"minecraft:jungle_boat","id":375,"oldId":333},{"name":"minecraft:jungle_button","id":-143,"oldId":-143},{"name":"minecraft:jungle_door","id":545,"oldId":429},{"name":"minecraft:jungle_fence_gate","id":185,"oldId":185},{"name":"minecraft:jungle_pressure_plate","id":-153,"oldId":-153},{"name":"minecraft:jungle_sign","id":568,"oldId":474},{"name":"minecraft:jungle_stairs","id":136,"oldId":136},{"name":"minecraft:jungle_standing_sign","id":-188,"oldId":-188},{"name":"minecraft:jungle_trapdoor","id":-148,"oldId":-148},{"name":"minecraft:jungle_wall_sign","id":-189,"oldId":-189},{"name":"minecraft:kelp","id":380,"oldId":335},{"name":"minecraft:ladder","id":65,"oldId":65},{"name":"minecraft:lantern","id":-208,"oldId":-208},{"name":"minecraft:lapis_block","id":22,"oldId":22},{"oldData":4,"name":"minecraft:lapis_lazuli","id":412,"oldId":351},{"name":"minecraft:lapis_ore","id":21,"oldId":21},{"name":"minecraft:lava","id":11,"oldId":11},{"oldData":10,"name":"minecraft:lava_bucket","id":363,"oldId":325},{"name":"minecraft:lava_cauldron","id":-210,"oldId":-210},{"name":"minecraft:lead","id":537,"oldId":420},{"name":"minecraft:leather","id":379,"oldId":334},{"name":"minecraft:leather_boots","id":338,"oldId":301},{"name":"minecraft:leather_chestplate","id":336,"oldId":299},{"name":"minecraft:leather_helmet","id":335,"oldId":298},{"name":"minecraft:leather_horse_armor","id":520,"oldId":416},{"name":"minecraft:leather_leggings","id":337,"oldId":300},{"name":"minecraft:leaves","id":18,"oldId":18},{"name":"minecraft:leaves2","id":161,"oldId":161},{"name":"minecraft:lectern","id":-194,"oldId":-194},{"name":"minecraft:lever","id":69,"oldId":69},{"name":"minecraft:light_block","id":-215,"oldId":-215},{"oldData":12,"name":"minecraft:light_blue_dye","id":405,"oldId":351},{"name":"minecraft:light_blue_glazed_terracotta","id":223,"oldId":223},{"oldData":7,"name":"minecraft:light_gray_dye","id":400,"oldId":351},{"name":"minecraft:light_weighted_pressure_plate","id":147,"oldId":147},{"oldData":10,"name":"minecraft:lime_dye","id":403,"oldId":351},{"name":"minecraft:lime_glazed_terracotta","id":225,"oldId":225},{"name":"minecraft:lingering_potion","id":552,"oldId":441},{"name":"minecraft:lit_blast_furnace","id":-214,"oldId":-214},{"name":"minecraft:lit_furnace","id":62,"oldId":62},{"name":"minecraft:lit_pumpkin","id":91,"oldId":91},{"name":"minecraft:lit_redstone_lamp","id":124,"oldId":124},{"name":"minecraft:lit_redstone_ore","id":74,"oldId":74},{"name":"minecraft:lit_smoker","id":-199,"oldId":-199},{"name":"minecraft:llama_spawn_egg","id":471,"oldId":383,"oldData":29},{"name":"minecraft:lodestone","id":-222,"oldId":-222},{"name":"minecraft:lodestone_compass","id":591,"oldId":741},{"name":"minecraft:log","id":17,"oldId":17},{"name":"minecraft:log2","id":162,"oldId":162},{"name":"minecraft:loom","id":-204,"oldId":-204},{"oldData":13,"name":"minecraft:magenta_dye","id":406,"oldId":351},{"name":"minecraft:magenta_glazed_terracotta","id":222,"oldId":222},{"name":"minecraft:magma","id":213,"oldId":213},{"name":"minecraft:magma_cream","id":428,"oldId":378},{"name":"minecraft:magma_cube_spawn_egg","id":453,"oldId":383,"oldData":42},{"name":"minecraft:medicine","id":589,"oldId":447},{"name":"minecraft:melon_block","id":103,"oldId":103},{"name":"minecraft:melon_seeds","id":293,"oldId":362},{"name":"minecraft:melon_slice","id":272,"oldId":360},{"name":"minecraft:melon_stem","id":105,"oldId":105},{"oldData":1,"name":"minecraft:milk_bucket","id":361,"oldId":325},{"name":"minecraft:minecart","id":368,"oldId":328},{"name":"minecraft:mob_spawner","id":52,"oldId":52},{"oldData":3,"name":"minecraft:mojang_banner_pattern","id":574,"oldId":434},{"name":"minecraft:monster_egg","id":97,"oldId":97},{"name":"minecraft:mooshroom_spawn_egg","id":438,"oldId":383,"oldData":16},{"name":"minecraft:mossy_cobblestone","id":48,"oldId":48},{"name":"minecraft:mossy_cobblestone_stairs","id":-179,"oldId":-179},{"name":"minecraft:mossy_stone_brick_stairs","id":-175,"oldId":-175},{"name":"minecraft:movingblock","id":250,"oldId":250},{"name":"minecraft:mule_spawn_egg","id":464,"oldId":383,"oldData":25},{"name":"minecraft:mushroom_stew","id":260,"oldId":282},{"name":"minecraft:music_disc_11","id":534,"oldId":510},{"name":"minecraft:music_disc_13","id":524,"oldId":500},{"name":"minecraft:music_disc_blocks","id":526,"oldId":502},{"name":"minecraft:music_disc_cat","id":525,"oldId":501},{"name":"minecraft:music_disc_chirp","id":527,"oldId":503},{"name":"minecraft:music_disc_far","id":528,"oldId":504},{"name":"minecraft:music_disc_mall","id":529,"oldId":505},{"name":"minecraft:music_disc_mellohi","id":530,"oldId":506},{"name":"minecraft:music_disc_pigstep","id":609,"oldId":759},{"name":"minecraft:music_disc_stal","id":531,"oldId":507},{"name":"minecraft:music_disc_strad","id":532,"oldId":508},{"name":"minecraft:music_disc_wait","id":535,"oldId":511},{"name":"minecraft:music_disc_ward","id":533,"oldId":509},{"name":"minecraft:mutton","id":540,"oldId":423},{"name":"minecraft:mycelium","id":110,"oldId":110},{"name":"minecraft:name_tag","id":538,"oldId":421},{"name":"minecraft:nautilus_shell","id":560,"oldId":465},{"name":"minecraft:nether_brick","id":112,"oldId":112},{"name":"minecraft:nether_brick_fence","id":113,"oldId":113},{"name":"minecraft:nether_brick_stairs","id":114,"oldId":114},{"name":"minecraft:nether_gold_ore","id":-288,"oldId":-288},{"name":"minecraft:nether_sprouts","id":610,"oldId":760},{"name":"minecraft:nether_star","id":508,"oldId":399},{"name":"minecraft:nether_wart","id":294,"oldId":372},{"name":"minecraft:nether_wart_block","id":214,"oldId":214},{"name":"minecraft:netherbrick","id":513,"oldId":405},{"name":"minecraft:netherite_axe","id":596,"oldId":746},{"name":"minecraft:netherite_block","id":-270,"oldId":-270},{"name":"minecraft:netherite_boots","id":601,"oldId":751},{"name":"minecraft:netherite_chestplate","id":599,"oldId":749},{"name":"minecraft:netherite_helmet","id":598,"oldId":748},{"name":"minecraft:netherite_hoe","id":597,"oldId":747},{"name":"minecraft:netherite_ingot","id":592,"oldId":742},{"name":"minecraft:netherite_leggings","id":600,"oldId":750},{"name":"minecraft:netherite_pickaxe","id":595,"oldId":745},{"name":"minecraft:netherite_scrap","id":602,"oldId":752},{"name":"minecraft:netherite_shovel","id":594,"oldId":744},{"name":"minecraft:netherite_sword","id":593,"oldId":743},{"name":"minecraft:netherrack","id":87,"oldId":87},{"name":"minecraft:netherreactor","id":247,"oldId":247},{"name":"minecraft:normal_stone_stairs","id":-180,"oldId":-180},{"name":"minecraft:noteblock","id":25,"oldId":25},{"name":"minecraft:npc_spawn_egg","id":468,"oldId":383,"oldData":51},{"oldData":0,"name":"minecraft:oak_boat","id":373,"oldId":333},{"name":"minecraft:oak_sign","id":358,"oldId":323},{"name":"minecraft:oak_stairs","id":53,"oldId":53},{"name":"minecraft:observer","id":251,"oldId":251},{"name":"minecraft:obsidian","id":49,"oldId":49},{"name":"minecraft:ocelot_spawn_egg","id":449,"oldId":383,"oldData":22},{"oldData":14,"name":"minecraft:orange_dye","id":407,"oldId":351},{"name":"minecraft:orange_glazed_terracotta","id":221,"oldId":221},{"name":"minecraft:packed_ice","id":174,"oldId":174},{"name":"minecraft:painting","id":357,"oldId":321},{"name":"minecraft:panda_spawn_egg","id":487,"oldId":383,"oldData":113},{"name":"minecraft:paper","id":384,"oldId":339},{"name":"minecraft:parrot_spawn_egg","id":476,"oldId":383,"oldData":30},{"name":"minecraft:phantom_membrane","id":564,"oldId":470},{"name":"minecraft:phantom_spawn_egg","id":484,"oldId":383,"oldData":58},{"name":"minecraft:pig_spawn_egg","id":435,"oldId":383,"oldData":12},{"oldData":6,"name":"minecraft:piglin_banner_pattern","id":577,"oldId":434},{"name":"minecraft:piglin_brute_spawn_egg","id":497,"oldId":383,"oldData":127},{"name":"minecraft:piglin_spawn_egg","id":495,"oldId":383,"oldData":123},{"name":"minecraft:pillager_spawn_egg","id":489,"oldId":383,"oldData":114},{"oldData":9,"name":"minecraft:pink_dye","id":402,"oldId":351},{"name":"minecraft:pink_glazed_terracotta","id":226,"oldId":226},{"name":"minecraft:piston","id":33,"oldId":33},{"name":"minecraft:pistonarmcollision","id":34,"oldId":34},{"name":"minecraft:planks","id":5,"oldId":5},{"name":"minecraft:podzol","id":243,"oldId":243},{"name":"minecraft:poisonous_potato","id":282,"oldId":394},{"name":"minecraft:polar_bear_spawn_egg","id":470,"oldId":383,"oldData":28},{"name":"minecraft:polished_andesite_stairs","id":-174,"oldId":-174},{"name":"minecraft:polished_basalt","id":-235,"oldId":-235},{"name":"minecraft:polished_blackstone","id":-291,"oldId":-291},{"name":"minecraft:polished_blackstone_brick_double_slab","id":-285,"oldId":-285},{"name":"minecraft:polished_blackstone_brick_slab","id":-284,"oldId":-284},{"name":"minecraft:polished_blackstone_brick_stairs","id":-275,"oldId":-275},{"name":"minecraft:polished_blackstone_brick_wall","id":-278,"oldId":-278},{"name":"minecraft:polished_blackstone_bricks","id":-274,"oldId":-274},{"name":"minecraft:polished_blackstone_button","id":-296,"oldId":-296},{"name":"minecraft:polished_blackstone_double_slab","id":-294,"oldId":-294},{"name":"minecraft:polished_blackstone_pressure_plate","id":-295,"oldId":-295},{"name":"minecraft:polished_blackstone_slab","id":-293,"oldId":-293},{"name":"minecraft:polished_blackstone_stairs","id":-292,"oldId":-292},{"name":"minecraft:polished_blackstone_wall","id":-297,"oldId":-297},{"name":"minecraft:polished_diorite_stairs","id":-173,"oldId":-173},{"name":"minecraft:polished_granite_stairs","id":-172,"oldId":-172},{"name":"minecraft:popped_chorus_fruit","id":549,"oldId":433},{"name":"minecraft:porkchop","id":262,"oldId":319},{"name":"minecraft:portal","id":90,"oldId":90},{"name":"minecraft:potato","id":280,"oldId":392},{"name":"minecraft:potatoes","id":142,"oldId":142},{"name":"minecraft:potion","id":424,"oldId":373},{"name":"minecraft:powered_comparator","id":150,"oldId":150},{"name":"minecraft:powered_repeater","id":94,"oldId":94},{"name":"minecraft:prismarine","id":168,"oldId":168},{"name":"minecraft:prismarine_bricks_stairs","id":-4,"oldId":-4},{"name":"minecraft:prismarine_crystals","id":539,"oldId":422},{"name":"minecraft:prismarine_shard","id":555,"oldId":409},{"name":"minecraft:prismarine_stairs","id":-2,"oldId":-2},{"name":"minecraft:pufferfish","id":267,"oldId":462},{"oldData":5,"name":"minecraft:pufferfish_bucket","id":367,"oldId":325},{"name":"minecraft:pufferfish_spawn_egg","id":479,"oldId":383,"oldData":108},{"name":"minecraft:pumpkin","id":86,"oldId":86},{"name":"minecraft:pumpkin_pie","id":284,"oldId":400},{"name":"minecraft:pumpkin_seeds","id":292,"oldId":361},{"name":"minecraft:pumpkin_stem","id":104,"oldId":104},{"oldData":5,"name":"minecraft:purple_dye","id":398,"oldId":351},{"name":"minecraft:purple_glazed_terracotta","id":219,"oldId":219},{"name":"minecraft:purpur_block","id":201,"oldId":201},{"name":"minecraft:purpur_stairs","id":203,"oldId":203},{"name":"minecraft:quartz","id":514,"oldId":406},{"name":"minecraft:quartz_block","id":155,"oldId":155},{"name":"minecraft:quartz_bricks","id":-304,"oldId":-304},{"name":"minecraft:quartz_ore","id":153,"oldId":153},{"name":"minecraft:quartz_stairs","id":156,"oldId":156},{"name":"minecraft:rabbit","id":288,"oldId":411},{"name":"minecraft:rabbit_foot","id":518,"oldId":414},{"name":"minecraft:rabbit_hide","id":519,"oldId":415},{"name":"minecraft:rabbit_spawn_egg","id":457,"oldId":383,"oldData":18},{"name":"minecraft:rabbit_stew","id":290,"oldId":413},{"name":"minecraft:rail","id":66,"oldId":66},{"name":"minecraft:rapid_fertilizer","id":587,"oldId":449},{"name":"minecraft:ravager_spawn_egg","id":491,"oldId":383,"oldData":59},{"name":"minecraft:real_double_stone_slab","id":43,"oldId":43},{"name":"minecraft:real_double_stone_slab2","id":181,"oldId":181},{"name":"minecraft:real_double_stone_slab3","id":-167,"oldId":-167},{"name":"minecraft:real_double_stone_slab4","id":-168,"oldId":-168},{"oldData":1,"name":"minecraft:red_dye","id":394,"oldId":351},{"name":"minecraft:red_flower","id":38,"oldId":38},{"name":"minecraft:red_glazed_terracotta","id":234,"oldId":234},{"name":"minecraft:red_mushroom","id":40,"oldId":40},{"name":"minecraft:red_mushroom_block","id":100,"oldId":100},{"name":"minecraft:red_nether_brick","id":215,"oldId":215},{"name":"minecraft:red_nether_brick_stairs","id":-184,"oldId":-184},{"name":"minecraft:red_sandstone","id":179,"oldId":179},{"name":"minecraft:red_sandstone_stairs","id":180,"oldId":180},{"name":"minecraft:redstone","id":371,"oldId":331},{"name":"minecraft:redstone_block","id":152,"oldId":152},{"name":"minecraft:redstone_lamp","id":123,"oldId":123},{"name":"minecraft:redstone_ore","id":73,"oldId":73},{"name":"minecraft:redstone_torch","id":76,"oldId":76},{"name":"minecraft:redstone_wire","id":55,"oldId":55},{"name":"minecraft:repeater","id":417,"oldId":356},{"name":"minecraft:repeating_command_block","id":188,"oldId":188},{"name":"minecraft:reserved6","id":255,"oldId":255},{"name":"minecraft:respawn_anchor","id":-272,"oldId":-272},{"name":"minecraft:rotten_flesh","id":277,"oldId":367},{"name":"minecraft:saddle","id":369,"oldId":329},{"name":"minecraft:salmon","id":265,"oldId":460},{"oldData":3,"name":"minecraft:salmon_bucket","id":365,"oldId":325},{"name":"minecraft:salmon_spawn_egg","id":480,"oldId":383,"oldData":109},{"name":"minecraft:sand","id":12,"oldId":12},{"name":"minecraft:sandstone","id":24,"oldId":24},{"name":"minecraft:sandstone_stairs","id":128,"oldId":128},{"name":"minecraft:sapling","id":6,"oldId":6},{"name":"minecraft:scaffolding","id":-165,"oldId":-165},{"name":"minecraft:scute","id":562,"oldId":468},{"name":"minecraft:sea_pickle","id":-156,"oldId":-156},{"name":"minecraft:seagrass","id":-130,"oldId":-130},{"name":"minecraft:sealantern","id":169,"oldId":169},{"name":"minecraft:shears","id":419,"oldId":359},{"name":"minecraft:sheep_spawn_egg","id":436,"oldId":383,"oldData":13},{"name":"minecraft:shield","id":355,"oldId":513},{"name":"minecraft:shroomlight","id":-230,"oldId":-230},{"name":"minecraft:shulker_box","id":218,"oldId":218},{"name":"minecraft:shulker_shell","id":556,"oldId":445},{"name":"minecraft:shulker_spawn_egg","id":467,"oldId":383,"oldData":54},{"name":"minecraft:silver_glazed_terracotta","id":228,"oldId":228},{"name":"minecraft:silverfish_spawn_egg","id":441,"oldId":383,"oldData":39},{"name":"minecraft:skeleton_horse_spawn_egg","id":465,"oldId":383,"oldData":26},{"name":"minecraft:skeleton_spawn_egg","id":442,"oldId":383,"oldData":34},{"name":"minecraft:skull","id":506,"oldId":397},{"oldData":1,"name":"minecraft:skull_banner_pattern","id":573,"oldId":434},{"name":"minecraft:slime","id":165,"oldId":165},{"name":"minecraft:slime_ball","id":386,"oldId":341},{"name":"minecraft:slime_spawn_egg","id":443,"oldId":383,"oldData":37},{"name":"minecraft:smithing_table","id":-202,"oldId":-202},{"name":"minecraft:smoker","id":-198,"oldId":-198},{"name":"minecraft:smooth_quartz_stairs","id":-185,"oldId":-185},{"name":"minecraft:smooth_red_sandstone_stairs","id":-176,"oldId":-176},{"name":"minecraft:smooth_sandstone_stairs","id":-177,"oldId":-177},{"name":"minecraft:smooth_stone","id":-183,"oldId":-183},{"name":"minecraft:snow","id":80,"oldId":80},{"name":"minecraft:snow_layer","id":78,"oldId":78},{"name":"minecraft:snowball","id":372,"oldId":332},{"name":"minecraft:soul_campfire","id":611,"oldId":801},{"name":"minecraft:soul_fire","id":-237,"oldId":-237},{"name":"minecraft:soul_lantern","id":-269,"oldId":-269},{"name":"minecraft:soul_sand","id":88,"oldId":88},{"name":"minecraft:soul_soil","id":-236,"oldId":-236},{"name":"minecraft:soul_torch","id":-268,"oldId":-268},{"name":"minecraft:sparkler","id":590,"oldId":442},{"name":"minecraft:spawn_egg","id":615,"oldId":383},{"name":"minecraft:spider_eye","id":278,"oldId":375},{"name":"minecraft:spider_spawn_egg","id":444,"oldId":383,"oldData":35},{"name":"minecraft:splash_potion","id":551,"oldId":438},{"name":"minecraft:sponge","id":19,"oldId":19},{"oldData":1,"name":"minecraft:spruce_boat","id":376,"oldId":333},{"name":"minecraft:spruce_button","id":-144,"oldId":-144},{"name":"minecraft:spruce_door","id":543,"oldId":427},{"name":"minecraft:spruce_fence_gate","id":183,"oldId":183},{"name":"minecraft:spruce_pressure_plate","id":-154,"oldId":-154},{"name":"minecraft:spruce_sign","id":566,"oldId":472},{"name":"minecraft:spruce_stairs","id":134,"oldId":134},{"name":"minecraft:spruce_standing_sign","id":-181,"oldId":-181},{"name":"minecraft:spruce_trapdoor","id":-149,"oldId":-149},{"name":"minecraft:spruce_wall_sign","id":-182,"oldId":-182},{"name":"minecraft:squid_spawn_egg","id":448,"oldId":383,"oldData":17},{"name":"minecraft:stained_glass","id":241,"oldId":241},{"name":"minecraft:stained_glass_pane","id":160,"oldId":160},{"name":"minecraft:stained_hardened_clay","id":159,"oldId":159},{"name":"minecraft:standing_banner","id":176,"oldId":176},{"name":"minecraft:standing_sign","id":63,"oldId":63},{"name":"minecraft:stick","id":320,"oldId":280},{"name":"minecraft:sticky_piston","id":29,"oldId":29},{"name":"minecraft:stickypistonarmcollision","id":-217,"oldId":-217},{"name":"minecraft:stone","id":1,"oldId":1},{"name":"minecraft:stone_axe","id":315,"oldId":275},{"name":"minecraft:stone_brick_stairs","id":109,"oldId":109},{"name":"minecraft:stone_button","id":77,"oldId":77},{"name":"minecraft:stone_hoe","id":330,"oldId":291},{"name":"minecraft:stone_pickaxe","id":314,"oldId":274},{"name":"minecraft:stone_pressure_plate","id":70,"oldId":70},{"name":"minecraft:stone_shovel","id":313,"oldId":273},{"name":"minecraft:stone_stairs","id":67,"oldId":67},{"name":"minecraft:stone_sword","id":312,"oldId":272},{"name":"minecraft:stonebrick","id":98,"oldId":98},{"name":"minecraft:stonecutter","id":245,"oldId":245},{"name":"minecraft:stonecutter_block","id":-197,"oldId":-197},{"name":"minecraft:stray_spawn_egg","id":460,"oldId":383,"oldData":46},{"name":"minecraft:strider_spawn_egg","id":493,"oldId":383,"oldData":125},{"name":"minecraft:string","id":326,"oldId":287},{"name":"minecraft:stripped_acacia_log","id":-8,"oldId":-8},{"name":"minecraft:stripped_birch_log","id":-6,"oldId":-6},{"name":"minecraft:stripped_crimson_hyphae","id":-300,"oldId":-300},{"name":"minecraft:stripped_crimson_stem","id":-240,"oldId":-240},{"name":"minecraft:stripped_dark_oak_log","id":-9,"oldId":-9},{"name":"minecraft:stripped_jungle_log","id":-7,"oldId":-7},{"name":"minecraft:stripped_oak_log","id":-10,"oldId":-10},{"name":"minecraft:stripped_spruce_log","id":-5,"oldId":-5},{"name":"minecraft:stripped_warped_hyphae","id":-301,"oldId":-301},{"name":"minecraft:stripped_warped_stem","id":-241,"oldId":-241},{"name":"minecraft:structure_block","id":252,"oldId":252},{"name":"minecraft:structure_void","id":217,"oldId":217},{"name":"minecraft:sugar","id":414,"oldId":353},{"name":"minecraft:sugar_cane","id":383,"oldId":338},{"name":"minecraft:suspicious_stew","id":579,"oldId":734},{"name":"minecraft:sweet_berries","id":287,"oldId":477},{"name":"minecraft:sweet_berry_bush","id":-207,"oldId":-207},{"name":"minecraft:tallgrass","id":31,"oldId":31},{"name":"minecraft:target","id":-239,"oldId":-239},{"name":"minecraft:tnt","id":46,"oldId":46},{"name":"minecraft:tnt_minecart","id":515,"oldId":407},{"name":"minecraft:torch","id":50,"oldId":50},{"name":"minecraft:totem_of_undying","id":558,"oldId":450},{"name":"minecraft:trapdoor","id":96,"oldId":96},{"name":"minecraft:trapped_chest","id":146,"oldId":146},{"name":"minecraft:trident","id":536,"oldId":455},{"name":"minecraft:tripwire","id":132,"oldId":132},{"name":"minecraft:tripwire_hook","id":131,"oldId":131},{"name":"minecraft:tropical_fish","id":266,"oldId":461},{"oldData":4,"name":"minecraft:tropical_fish_bucket","id":366,"oldId":325},{"name":"minecraft:tropical_fish_spawn_egg","id":477,"oldId":383,"oldData":111},{"name":"minecraft:turtle_egg","id":-159,"oldId":-159},{"name":"minecraft:turtle_helmet","id":563,"oldId":469},{"name":"minecraft:turtle_spawn_egg","id":483,"oldId":383,"oldData":74},{"name":"minecraft:twisting_vines","id":-287,"oldId":-287},{"name":"minecraft:underwater_torch","id":239,"oldId":239},{"name":"minecraft:undyed_shulker_box","id":205,"oldId":205},{"name":"minecraft:unknown","id":-305},{"name":"minecraft:unlit_redstone_torch","id":75,"oldId":75},{"name":"minecraft:unpowered_comparator","id":149,"oldId":149},{"name":"minecraft:unpowered_repeater","id":93,"oldId":93},{"name":"minecraft:vex_spawn_egg","id":474,"oldId":383,"oldData":105},{"name":"minecraft:villager_spawn_egg","id":447,"oldId":383,"oldData":15},{"name":"minecraft:vindicator_spawn_egg","id":472,"oldId":383,"oldData":57},{"name":"minecraft:vine","id":106,"oldId":106},{"name":"minecraft:wall_banner","id":177,"oldId":177},{"name":"minecraft:wall_sign","id":68,"oldId":68},{"name":"minecraft:wandering_trader_spawn_egg","id":490,"oldId":383,"oldData":118},{"name":"minecraft:warped_button","id":-261,"oldId":-261},{"name":"minecraft:warped_door","id":606,"oldId":756},{"name":"minecraft:warped_double_slab","id":-267,"oldId":-267},{"name":"minecraft:warped_fence","id":-257,"oldId":-257},{"name":"minecraft:warped_fence_gate","id":-259,"oldId":-259},{"name":"minecraft:warped_fungus","id":-229,"oldId":-229},{"name":"minecraft:warped_fungus_on_a_stick","id":607,"oldId":757},{"name":"minecraft:warped_hyphae","id":-298,"oldId":-298},{"name":"minecraft:warped_nylium","id":-233,"oldId":-233},{"name":"minecraft:warped_planks","id":-243,"oldId":-243},{"name":"minecraft:warped_pressure_plate","id":-263,"oldId":-263},{"name":"minecraft:warped_roots","id":-224,"oldId":-224},{"name":"minecraft:warped_sign","id":604,"oldId":754},{"name":"minecraft:warped_slab","id":-265,"oldId":-265},{"name":"minecraft:warped_stairs","id":-255,"oldId":-255},{"name":"minecraft:warped_standing_sign","id":-251,"oldId":-251},{"name":"minecraft:warped_stem","id":-226,"oldId":-226},{"name":"minecraft:warped_trapdoor","id":-247,"oldId":-247},{"name":"minecraft:warped_wall_sign","id":-253,"oldId":-253},{"name":"minecraft:warped_wart_block","id":-227,"oldId":-227},{"name":"minecraft:water","id":9,"oldId":9},{"oldData":8,"name":"minecraft:water_bucket","id":362,"oldId":325},{"name":"minecraft:waterlily","id":111,"oldId":111},{"name":"minecraft:web","id":30,"oldId":30},{"name":"minecraft:weeping_vines","id":-231,"oldId":-231},{"name":"minecraft:wheat","id":334,"oldId":296},{"name":"minecraft:wheat_seeds","id":291,"oldId":295},{"oldData":19,"name":"minecraft:white_dye","id":408,"oldId":351},{"name":"minecraft:white_glazed_terracotta","id":220,"oldId":220},{"name":"minecraft:witch_spawn_egg","id":450,"oldId":383,"oldData":45},{"name":"minecraft:wither_rose","id":-216,"oldId":-216},{"name":"minecraft:wither_skeleton_spawn_egg","id":462,"oldId":383,"oldData":48},{"name":"minecraft:wolf_spawn_egg","id":437,"oldId":383,"oldData":14},{"name":"minecraft:wood","id":-212,"oldId":-212},{"name":"minecraft:wooden_axe","id":311,"oldId":271},{"name":"minecraft:wooden_button","id":143,"oldId":143},{"name":"minecraft:wooden_door","id":359,"oldId":324},{"name":"minecraft:wooden_hoe","id":329,"oldId":290},{"name":"minecraft:wooden_pickaxe","id":310,"oldId":270},{"name":"minecraft:wooden_pressure_plate","id":72,"oldId":72},{"name":"minecraft:wooden_shovel","id":309,"oldId":269},{"name":"minecraft:wooden_slab","id":158,"oldId":158},{"name":"minecraft:wooden_sword","id":308,"oldId":268},{"name":"minecraft:wool","id":35,"oldId":35},{"name":"minecraft:writable_book","id":500,"oldId":386},{"name":"minecraft:written_book","id":501,"oldId":387},{"oldData":11,"name":"minecraft:yellow_dye","id":404,"oldId":351},{"name":"minecraft:yellow_flower","id":37,"oldId":37},{"name":"minecraft:yellow_glazed_terracotta","id":224,"oldId":224},{"name":"minecraft:zoglin_spawn_egg","id":496,"oldId":383,"oldData":126},{"name":"minecraft:zombie_horse_spawn_egg","id":466,"oldId":383,"oldData":27},{"name":"minecraft:zombie_pigman_spawn_egg","id":446,"oldId":383,"oldData":36},{"name":"minecraft:zombie_spawn_egg","id":445,"oldId":383,"oldData":32},{"name":"minecraft:zombie_villager_spawn_egg","id":475,"oldId":383,"oldData":44}] From cc74955c4e473a51bf47f1c6cdb3151fe4a4254e Mon Sep 17 00:00:00 2001 From: FlamingKnight <56657837+FlamingKnight@users.noreply.github.com> Date: Mon, 10 May 2021 19:14:04 -0700 Subject: [PATCH 52/52] Redstone update for 1.16.221 (#1837) Merge 1.16.221 into redstone --- src/main/java/cn/nukkit/Player.java | 62 +---- .../java/cn/nukkit/block/BlockItemFrame.java | 5 +- src/main/java/cn/nukkit/block/BlockRail.java | 5 +- .../cn/nukkit/block/BlockRedstoneWire.java | 89 +++---- .../java/cn/nukkit/block/BlockSapling.java | 2 + .../blockentity/BlockEntityItemFrame.java | 28 +++ .../command/defaults/GamemodeCommand.java | 4 +- src/main/java/cn/nukkit/entity/Entity.java | 39 ++- .../java/cn/nukkit/entity/EntityCreature.java | 1 - .../nukkit/entity/item/EntityEndCrystal.java | 1 + .../entity/item/EntityFallingBlock.java | 1 + .../nukkit/entity/mob/EntityEnderDragon.java | 1 - .../java/cn/nukkit/entity/mob/EntityMob.java | 3 - .../nukkit/entity/passive/EntityAnimal.java | 2 - .../nukkit/event/server/ServerStopEvent.java | 1 - src/main/java/cn/nukkit/item/Item.java | 22 +- src/main/java/cn/nukkit/item/ItemBucket.java | 48 ++-- .../java/cn/nukkit/item/ItemEndCrystal.java | 33 +-- src/main/java/cn/nukkit/item/ItemPotion.java | 2 - src/main/java/cn/nukkit/level/Sound.java | 2 + .../cn/nukkit/level/particle/Particle.java | 105 ++++---- .../LittleEndianByteBufInputStream.java | 51 ++++ .../LittleEndianByteBufOutputStream.java | 54 ++++ .../network/protocol/CraftingDataPacket.java | 22 +- .../protocol/CreativeContentPacket.java | 3 +- .../protocol/InventoryContentPacket.java | 1 - .../network/protocol/InventorySlotPacket.java | 1 - .../protocol/InventoryTransactionPacket.java | 4 - .../nukkit/network/protocol/ProtocolInfo.java | 6 +- .../network/protocol/UpdateTradePacket.java | 2 +- .../types/NetworkInventoryAction.java | 8 - .../java/cn/nukkit/utils/BinaryStream.java | 231 ++++++++++-------- 32 files changed, 510 insertions(+), 329 deletions(-) create mode 100644 src/main/java/cn/nukkit/network/LittleEndianByteBufInputStream.java create mode 100644 src/main/java/cn/nukkit/network/LittleEndianByteBufOutputStream.java diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index 28c2b77343e..869b73e3f22 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -13,7 +13,6 @@ import cn.nukkit.entity.item.*; import cn.nukkit.entity.projectile.EntityArrow; import cn.nukkit.entity.projectile.EntityThrownTrident; -import cn.nukkit.event.block.ItemFrameDropItemEvent; import cn.nukkit.event.entity.EntityDamageByBlockEvent; import cn.nukkit.event.entity.EntityDamageByEntityEvent; import cn.nukkit.event.entity.EntityDamageEvent; @@ -1254,7 +1253,6 @@ public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings n this.resetFallDistance(); this.inventory.sendContents(this); - this.inventory.sendContents(this.getViewers().values()); this.inventory.sendHeldItem(this.hasSpawned.values()); this.offhandInventory.sendContents(this); this.offhandInventory.sendContents(this.getViewers().values()); @@ -2402,8 +2400,15 @@ public void onCompletion(Server server) { ((BlockNoteblock) target).emitSound(); break actionswitch; case Block.DRAGON_EGG: - ((BlockDragonEgg) target).teleport(); - break actionswitch; + if (!this.isCreative()) { + ((BlockDragonEgg) target).teleport(); + break actionswitch; + } + case Block.ITEM_FRAME_BLOCK: + BlockEntity itemFrame = this.level.getBlockEntity(pos); + if (itemFrame instanceof BlockEntityItemFrame && ((BlockEntityItemFrame) itemFrame).dropItem(this)) { + break actionswitch; + } } Block block = target.getSide(face); if (block.getId() == Block.FIRE) { @@ -2868,24 +2873,9 @@ public void onCompletion(Server server) { case ProtocolInfo.ITEM_FRAME_DROP_ITEM_PACKET: ItemFrameDropItemPacket itemFrameDropItemPacket = (ItemFrameDropItemPacket) packet; Vector3 vector3 = this.temporalVector.setComponents(itemFrameDropItemPacket.x, itemFrameDropItemPacket.y, itemFrameDropItemPacket.z); - BlockEntity blockEntityItemFrame = this.level.getBlockEntity(vector3); - BlockEntityItemFrame itemFrame = (BlockEntityItemFrame) blockEntityItemFrame; - if (itemFrame != null) { - block = itemFrame.getBlock(); - Item itemDrop = itemFrame.getItem(); - ItemFrameDropItemEvent itemFrameDropItemEvent = new ItemFrameDropItemEvent(this, block, itemFrame, itemDrop); - this.server.getPluginManager().callEvent(itemFrameDropItemEvent); - if (!itemFrameDropItemEvent.isCancelled()) { - if (itemDrop.getId() != Item.AIR) { - vector3 = this.temporalVector.setComponents(itemFrame.x + 0.5, itemFrame.y, itemFrame.z + 0.5); - this.level.dropItem(vector3, itemDrop); - itemFrame.setItem(new ItemBlock(Block.get(BlockID.AIR))); - itemFrame.setItemRotation(0); - this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_ITEM_FRAME_ITEM_REMOVED); - } - } else { - itemFrame.spawnTo(this); - } + BlockEntity itemFrame = this.level.getBlockEntity(vector3); + if (itemFrame instanceof BlockEntityItemFrame) { + ((BlockEntityItemFrame) itemFrame).dropItem(this); } break; case ProtocolInfo.MAP_INFO_REQUEST_PACKET: @@ -3931,34 +3921,6 @@ public void kill() { ev.setKeepExperience(this.level.gameRules.getBoolean(GameRule.KEEP_INVENTORY)); ev.setKeepInventory(ev.getKeepExperience()); - if (cause != null && cause.getCause() != DamageCause.VOID && cause.getCause() != DamageCause.SUICIDE) { - PlayerOffhandInventory offhandInventory = this.getOffhandInventory(); - PlayerInventory playerInventory = this.getInventory(); - if (offhandInventory.getItem(0).getId() == Item.TOTEM || playerInventory.getItemInHand().getId() == Item.TOTEM) { - this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_TOTEM); - this.extinguish(); - this.removeAllEffects(); - this.setHealth(1); - - this.addEffect(Effect.getEffect(Effect.REGENERATION).setDuration(800).setAmplifier(1)); - this.addEffect(Effect.getEffect(Effect.FIRE_RESISTANCE).setDuration(800).setAmplifier(1)); - this.addEffect(Effect.getEffect(Effect.ABSORPTION).setDuration(100).setAmplifier(1)); - - EntityEventPacket pk = new EntityEventPacket(); - pk.eid = this.getId(); - pk.event = EntityEventPacket.CONSUME_TOTEM; - this.dataPacket(pk); - - if (offhandInventory.getItem(0).getId() == Item.TOTEM) { - offhandInventory.clear(0); - } else { - playerInventory.clear(playerInventory.getHeldItemIndex()); - } - - ev.setCancelled(true); - } - } - this.server.getPluginManager().callEvent(ev); if (!ev.isCancelled()) { diff --git a/src/main/java/cn/nukkit/block/BlockItemFrame.java b/src/main/java/cn/nukkit/block/BlockItemFrame.java index c7e0bd8216b..0203604fc7c 100644 --- a/src/main/java/cn/nukkit/block/BlockItemFrame.java +++ b/src/main/java/cn/nukkit/block/BlockItemFrame.java @@ -11,7 +11,7 @@ import cn.nukkit.nbt.tag.Tag; import cn.nukkit.network.protocol.LevelEventPacket; -import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; /** * Created by Pub4Game on 03.07.2016. @@ -114,8 +114,7 @@ public boolean onBreak(Item item) { public Item[] getDrops(Item item) { BlockEntity blockEntity = this.getLevel().getBlockEntity(this); BlockEntityItemFrame itemFrame = (BlockEntityItemFrame) blockEntity; - int chance = new Random().nextInt(100) + 1; - if (itemFrame != null && chance <= (itemFrame.getItemDropChance() * 100)) { + if (itemFrame != null && ThreadLocalRandom.current().nextFloat() <= itemFrame.getItemDropChance()) { return new Item[]{ toItem(), itemFrame.getItem().clone() }; diff --git a/src/main/java/cn/nukkit/block/BlockRail.java b/src/main/java/cn/nukkit/block/BlockRail.java index c5112c68883..bdde591d0a2 100644 --- a/src/main/java/cn/nukkit/block/BlockRail.java +++ b/src/main/java/cn/nukkit/block/BlockRail.java @@ -72,7 +72,8 @@ public int getToolType() { public int onUpdate(int type) { if (type == Level.BLOCK_UPDATE_NORMAL) { Optional ascendingDirection = this.getOrientation().ascendingDirection(); - if (this.down().isTransparent() || (ascendingDirection.isPresent() && this.getSide(ascendingDirection.get()).isTransparent())) { + Block down = this.down(); + if ((down.isTransparent() && down.getId() != HOPPER_BLOCK) || (ascendingDirection.isPresent() && this.getSide(ascendingDirection.get()).isTransparent())) { this.getLevel().useBreakOn(this); return Level.BLOCK_UPDATE_NORMAL; } @@ -99,7 +100,7 @@ public BlockColor getColor() { @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { Block down = this.down(); - if (down == null || down.isTransparent()) { + if (down == null || (down.isTransparent() && down.getId() != HOPPER_BLOCK)) { return false; } Map railsAround = this.checkRailsAroundAffected(); diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java index 61ebc8b136f..544047ff546 100644 --- a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java +++ b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java @@ -44,39 +44,30 @@ public int getId() { @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { - if (target.canBeReplaced()) { - block = target; - } - - if (!canBePlacedOn(block.down())) { + if (face != BlockFace.UP || !canBePlacedOn(target)) { return false; } - if (this.level.getServer().isRedstoneEnabled()) { - this.getLevel().setBlock(block, this, true, false); - - this.updateSurroundingRedstone(true); - Vector3 pos = getLocation(); + this.getLevel().setBlock(block, this, true, false); + this.updateSurroundingRedstone(true); + Vector3 pos = getLocation(); - for (BlockFace blockFace : Plane.VERTICAL) { - this.level.updateAroundRedstone(pos.getSide(blockFace), blockFace.getOpposite()); - } + for (BlockFace blockFace : Plane.VERTICAL) { + this.level.updateAroundRedstone(pos.getSide(blockFace), blockFace.getOpposite()); + } - for (BlockFace blockFace : Plane.VERTICAL) { - this.updateAround(pos.getSide(blockFace), blockFace.getOpposite()); - } + for (BlockFace blockFace : Plane.VERTICAL) { + this.updateAround(pos.getSide(blockFace), blockFace.getOpposite()); + } - for (BlockFace blockFace : Plane.HORIZONTAL) { - Vector3 v = pos.getSide(blockFace); + for (BlockFace blockFace : Plane.HORIZONTAL) { + Vector3 v = pos.getSide(blockFace); - if (this.level.getBlock(v).isNormalBlock()) { - this.updateAround(v.up(), BlockFace.DOWN); - } else { - this.updateAround(v.down(), BlockFace.UP); - } + if (this.level.getBlock(v).isNormalBlock()) { + this.updateAround(v.up(), BlockFace.DOWN); + } else { + this.updateAround(v.down(), BlockFace.UP); } - } else { - this.getLevel().setBlock(block, this, true, true); } return true; } @@ -176,21 +167,19 @@ public boolean onBreak(Item item) { Vector3 pos = getLocation(); - if (this.level.getServer().isRedstoneEnabled()) { - this.updateSurroundingRedstone(false); + this.updateSurroundingRedstone(false); - for (BlockFace blockFace : BlockFace.values()) { - this.level.updateAroundRedstone(pos.getSide(blockFace), null); - } + for (BlockFace blockFace : BlockFace.values()) { + this.level.updateAroundRedstone(pos.getSide(blockFace), null); + } - for (BlockFace blockFace : Plane.HORIZONTAL) { - Vector3 v = pos.getSide(blockFace); + for (BlockFace blockFace : Plane.HORIZONTAL) { + Vector3 v = pos.getSide(blockFace); - if (this.level.getBlock(v).isNormalBlock()) { - this.updateAround(v.up(), BlockFace.DOWN); - } else { - this.updateAround(v.down(), BlockFace.UP); - } + if (this.level.getBlock(v).isNormalBlock()) { + this.updateAround(v.up(), BlockFace.DOWN); + } else { + this.updateAround(v.down(), BlockFace.UP); } } return true; @@ -211,11 +200,6 @@ public int onUpdate(int type) { if (type != Level.BLOCK_UPDATE_NORMAL && type != Level.BLOCK_UPDATE_REDSTONE) { return 0; } - - if (!this.level.getServer().isRedstoneEnabled()) { - return 0; - } - // Redstone event RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this); getLevel().getServer().getPluginManager().callEvent(ev); @@ -223,7 +207,7 @@ public int onUpdate(int type) { return 0; } - if (type == Level.BLOCK_UPDATE_NORMAL && !this.canBePlacedOn(this.down())) { + if (type == Level.BLOCK_UPDATE_NORMAL && !this.canBePlacedOn(this.getLocation().down())) { this.getLevel().useBreakOn(this); return Level.BLOCK_UPDATE_NORMAL; } @@ -233,8 +217,10 @@ public int onUpdate(int type) { return Level.BLOCK_UPDATE_NORMAL; } - public boolean canBePlacedOn(Block b) { - return b.isSolid() && (!b.isTransparent() || b.getId() == Block.GLOWSTONE); + public boolean canBePlacedOn(Vector3 v) { + Block b = this.level.getBlock(v); + + return (b.isSolid() && !b.isTransparent() && b.getId() != Block.GLOWSTONE) || b.getId() == HOPPER_BLOCK; } public int getStrongPower(BlockFace side) { @@ -252,17 +238,17 @@ public int getWeakPower(BlockFace side) { } else if (side == BlockFace.UP) { return power; } else { - EnumSet faces = EnumSet.noneOf(BlockFace.class); + EnumSet enumset = EnumSet.noneOf(BlockFace.class); for (BlockFace face : Plane.HORIZONTAL) { if (this.isPowerSourceAt(face)) { - faces.add(face); + enumset.add(face); } } - if (side.getAxis().isHorizontal() && faces.isEmpty()) { + if (side.getAxis().isHorizontal() && enumset.isEmpty()) { return power; - } else if (faces.contains(side) && !faces.contains(side.rotateYCCW()) && !faces.contains(side.rotateY())) { + } else if (enumset.contains(side) && !enumset.contains(side.rotateYCCW()) && !enumset.contains(side.rotateY())) { return power; } else { return 0; @@ -294,9 +280,6 @@ protected static boolean canConnectTo(Block block, BlockFace side) { } else if (BlockRedstoneDiode.isDiode(block)) { BlockFace face = ((BlockRedstoneDiode) block).getFacing(); return face == side || face.getOpposite() == side; - } else if (block instanceof BlockPistonBase) { -// return ((BlockPistonBase) block).getBlockFace() != side.getOpposite(); - return true; } else { return block.isPowerSource() && side != null; } @@ -343,4 +326,4 @@ private int getStrongPower(Vector3 pos, BlockFace direction) { return block.getStrongPower(direction); } -} +} \ No newline at end of file diff --git a/src/main/java/cn/nukkit/block/BlockSapling.java b/src/main/java/cn/nukkit/block/BlockSapling.java index 7bfb9b1dd34..71c8bab42ee 100644 --- a/src/main/java/cn/nukkit/block/BlockSapling.java +++ b/src/main/java/cn/nukkit/block/BlockSapling.java @@ -136,10 +136,12 @@ private void grow() { if (!bigTree) { generator = new NewJungleTree(4, 7); + vector3 = this.add(0,0,0); } break; case ACACIA: generator = new ObjectSavannaTree(); + vector3 = this.add(0,0,0); break; case DARK_OAK: if ((vector2 = this.findSaplings(DARK_OAK)) != null) { diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java b/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java index 9c7e6ea86b0..fc4f8be2a24 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java @@ -1,12 +1,17 @@ package cn.nukkit.blockentity; +import cn.nukkit.Player; import cn.nukkit.block.Block; import cn.nukkit.block.BlockID; +import cn.nukkit.event.block.ItemFrameDropItemEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.network.protocol.LevelEventPacket; + +import java.util.concurrent.ThreadLocalRandom; /** * Created by Pub4Game on 03.07.2016. @@ -108,4 +113,27 @@ public CompoundTag getSpawnCompound() { public int getAnalogOutput() { return this.getItem() == null || this.getItem().getId() == 0 ? 0 : this.getItemRotation() % 8 + 1; } + + public boolean dropItem(Player player) { + Item item = this.getItem(); + if (item != null && item.getId() != Item.AIR) { + if (player != null) { + ItemFrameDropItemEvent event = new ItemFrameDropItemEvent(player, this.getBlock(), this, item); + this.level.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.spawnTo(player); + return true; + } + } + + if (this.getItemDropChance() > ThreadLocalRandom.current().nextFloat()) { + this.level.dropItem(this.add(0.5, 0, 0.5), item); + } + this.setItem(Item.get(Item.AIR)); + this.setItemRotation(0); + this.level.addLevelEvent(this, LevelEventPacket.EVENT_SOUND_ITEM_FRAME_ITEM_REMOVED); + return true; + } + return false; + } } diff --git a/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java b/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java index 417c473e4a4..3f07ae5b1a1 100644 --- a/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java @@ -79,8 +79,8 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) if (target.equals(sender)) { Command.broadcastCommandMessage(sender, new TranslationContainer("commands.gamemode.success.self", Server.getGamemodeString(gameMode))); } else { - target.sendMessage(new TranslationContainer("gameMode.changed")); - Command.broadcastCommandMessage(sender, new TranslationContainer("commands.gamemode.success.other", Server.getGamemodeString(gameMode), target.getName())); + target.sendMessage(new TranslationContainer("gameMode.changed", Server.getGamemodeString(gameMode))); + Command.broadcastCommandMessage(sender, new TranslationContainer("commands.gamemode.success.other", target.getName(), Server.getGamemodeString(gameMode))); } } diff --git a/src/main/java/cn/nukkit/entity/Entity.java b/src/main/java/cn/nukkit/entity/Entity.java index 41895e15623..9d5ecc980fe 100644 --- a/src/main/java/cn/nukkit/entity/Entity.java +++ b/src/main/java/cn/nukkit/entity/Entity.java @@ -13,6 +13,7 @@ import cn.nukkit.event.player.PlayerInteractEvent.Action; import cn.nukkit.event.player.PlayerTeleportEvent; import cn.nukkit.item.Item; +import cn.nukkit.item.ItemID; import cn.nukkit.level.*; import cn.nukkit.level.format.FullChunk; import cn.nukkit.math.*; @@ -1094,7 +1095,43 @@ public boolean attack(EntityDamageEvent source) { this.setAbsorption(Math.max(0, this.getAbsorption() + source.getDamage(EntityDamageEvent.DamageModifier.ABSORPTION))); } setLastDamageCause(source); - setHealth(getHealth() - source.getFinalDamage()); + + float newHealth = getHealth() - source.getFinalDamage(); + if (newHealth < 1 && this instanceof Player) { + if (source.getCause() != DamageCause.VOID && source.getCause() != DamageCause.SUICIDE) { + Player p = (Player) this; + boolean totem = false; + if (p.getOffhandInventory().getItem(0).getId() == ItemID.TOTEM) { + p.getOffhandInventory().clear(0); + totem = true; + } else if (p.getInventory().getItemInHand().getId() == ItemID.TOTEM) { + p.getInventory().clear(p.getInventory().getHeldItemIndex()); + totem = true; + } + if (totem) { + this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_TOTEM); + this.getLevel().addParticleEffect(this, ParticleEffect.TOTEM); + + this.extinguish(); + this.removeAllEffects(); + this.setHealth(1); + + this.addEffect(Effect.getEffect(Effect.REGENERATION).setDuration(800).setAmplifier(1)); + this.addEffect(Effect.getEffect(Effect.FIRE_RESISTANCE).setDuration(800)); + this.addEffect(Effect.getEffect(Effect.ABSORPTION).setDuration(100).setAmplifier(1)); + + EntityEventPacket pk = new EntityEventPacket(); + pk.eid = this.getId(); + pk.event = EntityEventPacket.CONSUME_TOTEM; + p.dataPacket(pk); + + source.setCancelled(true); + return false; + } + } + } + + setHealth(newHealth); return true; } diff --git a/src/main/java/cn/nukkit/entity/EntityCreature.java b/src/main/java/cn/nukkit/entity/EntityCreature.java index a8518795fd3..705468fc943 100644 --- a/src/main/java/cn/nukkit/entity/EntityCreature.java +++ b/src/main/java/cn/nukkit/entity/EntityCreature.java @@ -1,7 +1,6 @@ package cn.nukkit.entity; import cn.nukkit.Player; -import cn.nukkit.entity.mob.EntityEnderDragon; import cn.nukkit.item.Item; import cn.nukkit.level.format.FullChunk; import cn.nukkit.math.Vector3; diff --git a/src/main/java/cn/nukkit/entity/item/EntityEndCrystal.java b/src/main/java/cn/nukkit/entity/item/EntityEndCrystal.java index 9126f222e8e..992397dad99 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityEndCrystal.java +++ b/src/main/java/cn/nukkit/entity/item/EntityEndCrystal.java @@ -36,6 +36,7 @@ protected void initEntity() { } this.fireProof = true; + this.setDataFlag(DATA_FLAGS, DATA_FLAG_FIRE_IMMUNE, true); } @Override diff --git a/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java b/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java index 9ffe8bd3023..574b5be0674 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java +++ b/src/main/java/cn/nukkit/entity/item/EntityFallingBlock.java @@ -87,6 +87,7 @@ protected void initEntity() { } this.fireProof = true; + this.setDataFlag(DATA_FLAGS, DATA_FLAG_FIRE_IMMUNE, true); setDataProperty(new IntEntityData(DATA_VARIANT, GlobalBlockPalette.getOrCreateRuntimeId(this.getBlock(), this.getDamage()))); } diff --git a/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java b/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java index 0a681dd3ef6..230808a5bae 100644 --- a/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java +++ b/src/main/java/cn/nukkit/entity/mob/EntityEnderDragon.java @@ -3,7 +3,6 @@ import cn.nukkit.Player; import cn.nukkit.item.Item; import cn.nukkit.level.format.FullChunk; -import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; /** diff --git a/src/main/java/cn/nukkit/entity/mob/EntityMob.java b/src/main/java/cn/nukkit/entity/mob/EntityMob.java index 4b8497ac238..f80fb439067 100644 --- a/src/main/java/cn/nukkit/entity/mob/EntityMob.java +++ b/src/main/java/cn/nukkit/entity/mob/EntityMob.java @@ -1,10 +1,7 @@ package cn.nukkit.entity.mob; -import cn.nukkit.Player; import cn.nukkit.entity.EntityCreature; -import cn.nukkit.item.Item; import cn.nukkit.level.format.FullChunk; -import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; /** diff --git a/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java b/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java index 764434a8ee0..a8943d2b50c 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityAnimal.java @@ -1,12 +1,10 @@ package cn.nukkit.entity.passive; -import cn.nukkit.Player; import cn.nukkit.entity.Entity; import cn.nukkit.entity.EntityAgeable; import cn.nukkit.entity.EntityCreature; import cn.nukkit.item.Item; import cn.nukkit.level.format.FullChunk; -import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; /** diff --git a/src/main/java/cn/nukkit/event/server/ServerStopEvent.java b/src/main/java/cn/nukkit/event/server/ServerStopEvent.java index f0938dada84..65b46996c57 100644 --- a/src/main/java/cn/nukkit/event/server/ServerStopEvent.java +++ b/src/main/java/cn/nukkit/event/server/ServerStopEvent.java @@ -1,6 +1,5 @@ package cn.nukkit.event.server; -import cn.nukkit.event.Cancellable; import cn.nukkit.event.HandlerList; /** diff --git a/src/main/java/cn/nukkit/item/Item.java b/src/main/java/cn/nukkit/item/Item.java index 1aee75237f6..a0312e772e3 100644 --- a/src/main/java/cn/nukkit/item/Item.java +++ b/src/main/java/cn/nukkit/item/Item.java @@ -347,7 +347,10 @@ private static void initCreativeItems() { for (Map map : list) { try { - addCreativeItem(fromJson(map)); + Item item = fromJson(map, true); + if (item != null) { + addCreativeItem(item); + } } catch (Exception e) { MainLogger.getLogger().logException(e); } @@ -457,6 +460,10 @@ public static Item fromString(String str) { } public static Item fromJson(Map data) { + return fromJson(data, false); + } + + private static Item fromJson(Map data, boolean ignoreUnsupported) { String nbt = (String) data.get("nbt_b64"); byte[] nbtBytes; if (nbt != null) { @@ -470,7 +477,10 @@ public static Item fromJson(Map data) { } } - return get(Utils.toInt(data.get("id")), Utils.toInt(data.getOrDefault("damage", 0)), Utils.toInt(data.getOrDefault("count", 1)), nbtBytes); + int id = Utils.toInt(data.get("id")); + if (ignoreUnsupported && id < 0) return null; + + return get(id, Utils.toInt(data.getOrDefault("damage", 0)), Utils.toInt(data.getOrDefault("count", 1)), nbtBytes); } public static Item[] fromStringMultiple(String str) { @@ -886,6 +896,10 @@ public Block getBlock() { } } + public Block getBlockUnsafe() { + return this.block; + } + public int getId() { return id; } @@ -1093,4 +1107,8 @@ public Item clone() { return null; } } + + public final int getNetworkId() { + return RuntimeItems.getNetworkId(RuntimeItems.getRuntimeMapping().getNetworkFullId(this)); + } } diff --git a/src/main/java/cn/nukkit/item/ItemBucket.java b/src/main/java/cn/nukkit/item/ItemBucket.java index ad76a0c8a7d..2322a551ab4 100644 --- a/src/main/java/cn/nukkit/item/ItemBucket.java +++ b/src/main/java/cn/nukkit/item/ItemBucket.java @@ -7,6 +7,7 @@ import cn.nukkit.event.player.PlayerBucketFillEvent; import cn.nukkit.event.player.PlayerItemConsumeEvent; import cn.nukkit.level.Level; +import cn.nukkit.level.particle.ExplodeParticle; import cn.nukkit.math.BlockFace; import cn.nukkit.math.BlockFace.Plane; import cn.nukkit.math.Vector3; @@ -105,13 +106,17 @@ public boolean onActivate(Level level, Player player, Block block, Block target, } if (player.isSurvival()) { - Item clone = this.clone(); - clone.setCount(this.getCount() - 1); - player.getInventory().setItemInHand(clone); - if (player.getInventory().canAddItem(ev.getItem())) { - player.getInventory().addItem(ev.getItem()); + if (this.getCount() - 1 <= 0) { + player.getInventory().setItemInHand(ev.getItem()); } else { - player.dropItem(ev.getItem()); + Item clone = this.clone(); + clone.setCount(this.getCount() - 1); + player.getInventory().setItemInHand(clone); + if (player.getInventory().canAddItem(ev.getItem())) { + player.getInventory().addItem(ev.getItem()); + } else { + player.dropItem(ev.getItem()); + } } } @@ -133,8 +138,10 @@ public boolean onActivate(Level level, Player player, Block block, Block target, ev.setCancelled(true); } + boolean nether = false; if (player.getLevel().getDimension() == Level.DIMENSION_NETHER && this.getDamage() != 10) { ev.setCancelled(true); + nether = true; } player.getServer().getPluginManager().callEvent(ev); @@ -142,13 +149,17 @@ public boolean onActivate(Level level, Player player, Block block, Block target, if (!ev.isCancelled()) { player.getLevel().setBlock(block, targetBlock, true, true); if (player.isSurvival()) { - Item clone = this.clone(); - clone.setCount(this.getCount() - 1); - player.getInventory().setItemInHand(clone); - if (player.getInventory().canAddItem(ev.getItem())) { - player.getInventory().addItem(ev.getItem()); + if (this.getCount() - 1 <= 0) { + player.getInventory().setItemInHand(ev.getItem()); } else { - player.dropItem(ev.getItem()); + Item clone = this.clone(); + clone.setCount(this.getCount() - 1); + player.getInventory().setItemInHand(clone); + if (player.getInventory().canAddItem(ev.getItem())) { + player.getInventory().addItem(ev.getItem()); + } else { + player.dropItem(ev.getItem()); + } } } @@ -178,6 +189,13 @@ public boolean onActivate(Level level, Player player, Block block, Block target, } return true; + } else if (nether) { + if (!player.isCreative()) { + this.setDamage(0); // Empty bucket + player.getInventory().setItemInHand(this); + } + player.getLevel().addLevelSoundEvent(target, LevelSoundEventPacket.SOUND_FIZZ); + player.getLevel().addParticle(new ExplodeParticle(target.add(0.5, 1, 0.5))); } else { player.getLevel().sendBlocks(new Player[]{player}, new Block[]{Block.get(Block.AIR, 0, block)}, UpdateBlockPacket.FLAG_ALL_PRIORITY, 1); player.getInventory().sendContents(player); @@ -194,7 +212,7 @@ public boolean onClickAir(Player player, Vector3 directionVector) { @Override public boolean onUse(Player player, int ticksUsed) { - if (player.isSpectator()) { + if (player.isSpectator() || this.getDamage() != 1) { return false; } @@ -207,9 +225,7 @@ public boolean onUse(Player player, int ticksUsed) { } if (!player.isCreative()) { - this.count--; - player.getInventory().setItemInHand(this); - player.getInventory().addItem(new ItemBucket()); + player.getInventory().setItemInHand(new ItemBucket()); } player.removeAllEffects(); diff --git a/src/main/java/cn/nukkit/item/ItemEndCrystal.java b/src/main/java/cn/nukkit/item/ItemEndCrystal.java index 47a717a8ff0..97a6984ab7e 100644 --- a/src/main/java/cn/nukkit/item/ItemEndCrystal.java +++ b/src/main/java/cn/nukkit/item/ItemEndCrystal.java @@ -1,18 +1,21 @@ package cn.nukkit.item; -import cn.nukkit.block.BlockObsidian; +import cn.nukkit.Player; +import cn.nukkit.block.Block; import cn.nukkit.block.BlockBedrock; -import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.block.BlockID; +import cn.nukkit.block.BlockObsidian; import cn.nukkit.entity.Entity; -import java.util.Random; -import cn.nukkit.nbt.tag.FloatTag; -import cn.nukkit.nbt.tag.DoubleTag; -import cn.nukkit.nbt.tag.ListTag; -import cn.nukkit.math.BlockFace; -import cn.nukkit.block.Block; -import cn.nukkit.Player; import cn.nukkit.level.Level; import cn.nukkit.level.format.FullChunk; +import cn.nukkit.math.BlockFace; +import cn.nukkit.math.SimpleAxisAlignedBB; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.DoubleTag; +import cn.nukkit.nbt.tag.FloatTag; +import cn.nukkit.nbt.tag.ListTag; + +import java.util.Random; public class ItemEndCrystal extends Item { @@ -35,18 +38,20 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { - if ((!(target instanceof BlockBedrock) && !(target instanceof BlockObsidian)) || face != BlockFace.UP) return false; + if (!(target instanceof BlockBedrock) && !(target instanceof BlockObsidian)) return false; FullChunk chunk = level.getChunk((int) block.getX() >> 4, (int) block.getZ() >> 4); + Entity[] entities = level.getNearbyEntities(new SimpleAxisAlignedBB(target.x, target.y, target.z, target.x + 1, target.y + 2, target.z + 1)); + Block up = target.up(); - if (chunk == null) { + if (chunk == null || up.getId() != BlockID.AIR || up.up().getId() != BlockID.AIR || entities.length != 0) { return false; } CompoundTag nbt = new CompoundTag() .putList(new ListTag("Pos") - .add(new DoubleTag("", block.getX() + 0.5)) - .add(new DoubleTag("", block.getY())) - .add(new DoubleTag("", block.getZ() + 0.5))) + .add(new DoubleTag("", target.x + 0.5)) + .add(new DoubleTag("", up.y)) + .add(new DoubleTag("", target.z + 0.5))) .putList(new ListTag("Motion") .add(new DoubleTag("", 0)) .add(new DoubleTag("", 0)) diff --git a/src/main/java/cn/nukkit/item/ItemPotion.java b/src/main/java/cn/nukkit/item/ItemPotion.java index 2ad50c51983..70d9bb6d6b9 100644 --- a/src/main/java/cn/nukkit/item/ItemPotion.java +++ b/src/main/java/cn/nukkit/item/ItemPotion.java @@ -5,8 +5,6 @@ import cn.nukkit.math.Vector3; import cn.nukkit.potion.Potion; -import static cn.nukkit.Player.SURVIVAL; - public class ItemPotion extends Item { public static final int NO_EFFECTS = 0; diff --git a/src/main/java/cn/nukkit/level/Sound.java b/src/main/java/cn/nukkit/level/Sound.java index abf72ac738c..6c0c27cc608 100644 --- a/src/main/java/cn/nukkit/level/Sound.java +++ b/src/main/java/cn/nukkit/level/Sound.java @@ -735,6 +735,8 @@ public enum Sound { RESPAWN_ANCHOR_CHARGE("respawn_anchor.charge"), RESPAWN_ANCHOR_DEPLETE("respawn_anchor.deplete"), RESPAWN_ANCHOR_SET_SPAWN("respawn_anchor.set_spawn"), + SIGN_DYE_USE("sign.dye.use"), + SIGN_INK_SAC_USE("sign.ink_sac.use"), SMITHING_TABLE_USE("smithing_table.use"), STEP_ANCIENT_DEBRIS("step.ancient_debris"), STEP_BASALT("step.basalt"), diff --git a/src/main/java/cn/nukkit/level/particle/Particle.java b/src/main/java/cn/nukkit/level/particle/Particle.java index 870c253fab6..7f529f66c75 100644 --- a/src/main/java/cn/nukkit/level/particle/Particle.java +++ b/src/main/java/cn/nukkit/level/particle/Particle.java @@ -12,7 +12,7 @@ public abstract class Particle extends Vector3 { public static final int TYPE_BUBBLE = 1; - // 2 same as 1 + public static final int TYPE_BUBBLE_MANUAL = 2; public static final int TYPE_CRITICAL = 3; public static final int TYPE_BLOCK_FORCE_FIELD = 4; public static final int TYPE_SMOKE = 5; @@ -23,7 +23,6 @@ public abstract class Particle extends Vector3 { public static final int TYPE_LARGE_SMOKE = 10; public static final int TYPE_REDSTONE = 11; public static final int TYPE_RISING_RED_DUST = 12; - public static final int TYPE_ITEM_BREAK = 13; public static final int TYPE_SNOWBALL_POOF = 14; public static final int TYPE_HUGE_EXPLODE = 15; @@ -32,60 +31,70 @@ public abstract class Particle extends Vector3 { public static final int TYPE_HEART = 18; public static final int TYPE_TERRAIN = 19; public static final int TYPE_SUSPENDED_TOWN = 20, TYPE_TOWN_AURA = 20; - // 61 same as 20 public static final int TYPE_PORTAL = 21; // 22 same as 21 public static final int TYPE_SPLASH = 23, TYPE_WATER_SPLASH = 23; - // 24 same as 23 + public static final int TYPE_WATER_SPLASH_MANUAL = 24; public static final int TYPE_WATER_WAKE = 25; public static final int TYPE_DRIP_WATER = 26; public static final int TYPE_DRIP_LAVA = 27; public static final int TYPE_DRIP_HONEY = 28; - public static final int TYPE_FALLING_DUST = 29, TYPE_DUST = 29; - public static final int TYPE_MOB_SPELL = 30; - public static final int TYPE_MOB_SPELL_AMBIENT = 31; - public static final int TYPE_MOB_SPELL_INSTANTANEOUS = 32; - public static final int TYPE_NOTE_AND_DUST = 33; - - public static final int TYPE_SLIME = 34; - public static final int TYPE_RAIN_SPLASH = 35; - public static final int TYPE_VILLAGER_ANGRY = 36; - // 60 same as 36 - public static final int TYPE_VILLAGER_HAPPY = 37; - public static final int TYPE_ENCHANTMENT_TABLE = 38; - public static final int TYPE_TRACKING_EMITTER = 39; - public static final int TYPE_NOTE = 40; - public static final int TYPE_WITCH_SPELL = 41; - public static final int TYPE_CARROT = 42; - // 43 unknown - public static final int TYPE_END_ROD = 44; - // 59 same as 44 - public static final int TYPE_RISING_DRAGONS_BREATH = 45; - public static final int TYPE_SPIT = 46; - public static final int TYPE_TOTEM = 47; - public static final int TYPE_FOOD = 48; - public static final int TYPE_FIREWORKS_STARTER = 49; - public static final int TYPE_FIREWORKS_SPARK = 50; - public static final int TYPE_FIREWORKS_OVERLAY = 51; - public static final int TYPE_BALLOON_GAS = 52; - public static final int TYPE_COLORED_FLAME = 53; - public static final int TYPE_SPARKLER = 54; - public static final int TYPE_CONDUIT = 55; - public static final int TYPE_BUBBLE_COLUMN_UP = 56; - public static final int TYPE_BUBBLE_COLUMN_DOWN = 57; - public static final int TYPE_SNEEZE = 58; + public static final int TYPE_STALACTITE_DRIP_WATER = 29; + public static final int TYPE_STALACTITE_DRIP_LAVA = 30; + public static final int TYPE_FALLING_DUST = 31, TYPE_DUST = 31; + public static final int TYPE_MOB_SPELL = 32; + public static final int TYPE_MOB_SPELL_AMBIENT = 33; + public static final int TYPE_MOB_SPELL_INSTANTANEOUS = 34; + public static final int TYPE_INK = 35; + public static final int TYPE_SLIME = 36; + public static final int TYPE_RAIN_SPLASH = 37; + public static final int TYPE_VILLAGER_ANGRY = 38; + public static final int TYPE_VILLAGER_HAPPY = 39; + public static final int TYPE_ENCHANTMENT_TABLE = 40; + public static final int TYPE_TRACKING_EMITTER = 41; + public static final int TYPE_NOTE = 42; + public static final int TYPE_WITCH_SPELL = 43; + public static final int TYPE_CARROT = 44; + public static final int TYPE_MOB_APPEARANCE = 45; + public static final int TYPE_END_ROD = 46; + public static final int TYPE_RISING_DRAGONS_BREATH = 47; + public static final int TYPE_SPIT = 48; + public static final int TYPE_TOTEM = 49; + public static final int TYPE_FOOD = 50; + public static final int TYPE_FIREWORKS_STARTER = 51; + public static final int TYPE_FIREWORKS_SPARK = 52; + public static final int TYPE_FIREWORKS_OVERLAY = 53; + public static final int TYPE_BALLOON_GAS = 54; + public static final int TYPE_COLORED_FLAME = 55; + public static final int TYPE_SPARKLER = 56; + public static final int TYPE_CONDUIT = 57; + public static final int TYPE_BUBBLE_COLUMN_UP = 58; + public static final int TYPE_BUBBLE_COLUMN_DOWN = 59; + public static final int TYPE_SNEEZE = 60; + public static final int TYPE_SHULKER_BULLET = 61; + public static final int TYPE_BLEACH = 62; + public static final int TYPE_LARGE_EXPLOSION = 63; + public static final int TYPE_MYCELIUM_DUST = 64; + public static final int TYPE_FALLING_RED_DUST = 65; + public static final int TYPE_CAMPFIRE_SMOKE = 66; + public static final int TYPE_TALL_CAMPFIRE_SMOKE = 67; + public static final int TYPE_FALLING_DRAGONS_BREATH = 68; + public static final int TYPE_DRAGONS_BREATH = 69; + public static final int TYPE_BLUE_FLAME = 70; + public static final int TYPE_SOUL = 71; + public static final int TYPE_OBSIDIAN_TEAR = 72; + public static final int TYPE_PORTAL_REVERSE = 73; + public static final int TYPE_SNOWFLAKE = 74; + public static final int TYPE_VIBRATION_SIGNAL = 75; + public static final int TYPE_SCULK_SENSOR_REDSTONE = 76; + public static final int TYPE_SPORE_BLOSSOM_SHOWER = 77; + public static final int TYPE_SPORE_BLOSSOM_AMBIENT = 78; + public static final int TYPE_WAX = 79; + public static final int TYPE_ELECTRIC_SPARK = 80; - public static final int TYPE_LARGE_EXPLOSION = 61; - public static final int TYPE_INK = 62; - public static final int TYPE_FALLING_RED_DUST = 63; - public static final int TYPE_CAMPFIRE_SMOKE = 64; - //65 same as 64 - public static final int TYPE_FALLING_DRAGONS_BREATH = 66; - public static final int TYPE_DRAGONS_BREATH = 67; - public static final Integer getParticleIdByName(String name) { name = name.toUpperCase(); - + try { Field field = Particle.class.getField((name.startsWith("TYPE_") == true ? name : ("TYPE_" + name))); @@ -99,11 +108,11 @@ public static final Integer getParticleIdByName(String name) { } return null; } - + public static final boolean particleExists(String name) { return getParticleIdByName(name) != null; } - + public Particle() { super(0, 0, 0); } diff --git a/src/main/java/cn/nukkit/network/LittleEndianByteBufInputStream.java b/src/main/java/cn/nukkit/network/LittleEndianByteBufInputStream.java new file mode 100644 index 00000000000..f2194b676b3 --- /dev/null +++ b/src/main/java/cn/nukkit/network/LittleEndianByteBufInputStream.java @@ -0,0 +1,51 @@ +package cn.nukkit.network; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; + +import java.io.IOException; + +public class LittleEndianByteBufInputStream extends ByteBufInputStream { + + private final ByteBuf buffer; + + public LittleEndianByteBufInputStream(ByteBuf buffer) { + super(buffer); + this.buffer = buffer; + } + + @Override + public char readChar() throws IOException { + return Character.reverseBytes(buffer.readChar()); + } + + @Override + public double readDouble() throws IOException { + return buffer.readDoubleLE(); + } + + @Override + public float readFloat() throws IOException { + return buffer.readFloatLE(); + } + + @Override + public short readShort() throws IOException { + return buffer.readShortLE(); + } + + @Override + public int readUnsignedShort() throws IOException { + return buffer.readUnsignedShortLE(); + } + + @Override + public long readLong() throws IOException { + return buffer.readLongLE(); + } + + @Override + public int readInt() throws IOException { + return buffer.readIntLE(); + } +} diff --git a/src/main/java/cn/nukkit/network/LittleEndianByteBufOutputStream.java b/src/main/java/cn/nukkit/network/LittleEndianByteBufOutputStream.java new file mode 100644 index 00000000000..0e4c65d660a --- /dev/null +++ b/src/main/java/cn/nukkit/network/LittleEndianByteBufOutputStream.java @@ -0,0 +1,54 @@ +package cn.nukkit.network; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class LittleEndianByteBufOutputStream extends ByteBufOutputStream { + + private final ByteBuf buffer; + + public LittleEndianByteBufOutputStream(ByteBuf buffer) { + super(buffer); + this.buffer = buffer; + } + + @Override + public void writeChar(int v) throws IOException { + this.buffer.writeChar(Character.reverseBytes((char) v)); + } + + @Override + public void writeDouble(double v) throws IOException { + this.buffer.writeDoubleLE(v); + } + + @Override + public void writeFloat(float v) throws IOException { + this.buffer.writeFloatLE(v); + } + + @Override + public void writeShort(int val) throws IOException { + this.buffer.writeShortLE(val); + } + + @Override + public void writeLong(long val) throws IOException { + this.buffer.writeLongLE(val); + } + + @Override + public void writeInt(int val) throws IOException { + this.buffer.writeIntLE(val); + } + + @Override + public void writeUTF(String string) throws IOException { + byte[] bytes = string.getBytes(StandardCharsets.UTF_8); + this.writeShort(bytes.length); + this.write(bytes); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java b/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java index cc1ef660e1b..d54b04b7c5e 100644 --- a/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/CraftingDataPacket.java @@ -25,8 +25,8 @@ public class CraftingDataPacket extends DataPacket { public static final String CRAFTING_TAG_SMOKER = "smoker"; private List entries = new ArrayList<>(); - private List brewingEntries = new ArrayList<>(); - private List containerEntries = new ArrayList<>(); + private final List brewingEntries = new ArrayList<>(); + private final List containerEntries = new ArrayList<>(); public boolean cleanRecipes; public void addShapelessRecipe(ShapelessRecipe... recipe) { @@ -83,7 +83,7 @@ public void encode() { this.putRecipeIngredient(ingredient); } this.putUnsignedVarInt(1); - this.putSlot(shapeless.getResult()); + this.putSlot(shapeless.getResult(), true); this.putUUID(shapeless.getId()); this.putString(CRAFTING_TAG_CRAFTING_TABLE); this.putVarInt(shapeless.getPriority()); @@ -105,7 +105,7 @@ public void encode() { outputs.addAll(shaped.getExtraResults()); this.putUnsignedVarInt(outputs.size()); for (Item output : outputs) { - this.putSlot(output); + this.putSlot(output, true); } this.putUUID(shaped.getId()); this.putString(CRAFTING_TAG_CRAFTING_TABLE); @@ -120,7 +120,7 @@ public void encode() { if (recipe.getType() == RecipeType.FURNACE_DATA) { this.putVarInt(input.getDamage()); } - this.putSlot(furnace.getResult()); + this.putSlot(furnace.getResult(), true); this.putString(CRAFTING_TAG_FURNACE); break; case MULTI: @@ -132,19 +132,19 @@ public void encode() { this.putUnsignedVarInt(this.brewingEntries.size()); for (BrewingRecipe recipe : brewingEntries) { - this.putVarInt(recipe.getInput().getId()); + this.putVarInt(recipe.getInput().getNetworkId()); this.putVarInt(recipe.getInput().getDamage()); - this.putVarInt(recipe.getIngredient().getId()); + this.putVarInt(recipe.getIngredient().getNetworkId()); this.putVarInt(recipe.getIngredient().getDamage()); - this.putVarInt(recipe.getResult().getId()); + this.putVarInt(recipe.getResult().getNetworkId()); this.putVarInt(recipe.getResult().getDamage()); } this.putUnsignedVarInt(this.containerEntries.size()); for (ContainerRecipe recipe : containerEntries) { - this.putVarInt(recipe.getInput().getId()); - this.putVarInt(recipe.getIngredient().getId()); - this.putVarInt(recipe.getResult().getId()); + this.putVarInt(recipe.getInput().getNetworkId()); + this.putVarInt(recipe.getIngredient().getNetworkId()); + this.putVarInt(recipe.getResult().getNetworkId()); } this.putBoolean(cleanRecipes); diff --git a/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java b/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java index 2509de46f39..efc9847b8a2 100644 --- a/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/CreativeContentPacket.java @@ -25,8 +25,7 @@ public void encode() { this.putUnsignedVarInt(entries.length); for (int i = 0; i < entries.length; i++) { this.putUnsignedVarInt(i + 1); - this.putSlot(entries[i]); + this.putSlot(entries[i], true); } - } } diff --git a/src/main/java/cn/nukkit/network/protocol/InventoryContentPacket.java b/src/main/java/cn/nukkit/network/protocol/InventoryContentPacket.java index 198a6a45278..7947e92f3f6 100644 --- a/src/main/java/cn/nukkit/network/protocol/InventoryContentPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/InventoryContentPacket.java @@ -43,7 +43,6 @@ public void encode() { this.putUnsignedVarInt(this.inventoryId); this.putUnsignedVarInt(this.slots.length); for (Item slot : this.slots) { - this.putVarInt(slot.getId()); this.putSlot(slot); } } diff --git a/src/main/java/cn/nukkit/network/protocol/InventorySlotPacket.java b/src/main/java/cn/nukkit/network/protocol/InventorySlotPacket.java index 99b63119fa0..9fbde849b28 100644 --- a/src/main/java/cn/nukkit/network/protocol/InventorySlotPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/InventorySlotPacket.java @@ -32,7 +32,6 @@ public void encode() { this.reset(); this.putUnsignedVarInt(this.inventoryId); this.putUnsignedVarInt(this.slot); - this.putVarInt(this.item.getId()); this.putSlot(this.item); } } diff --git a/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java b/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java index b2403327447..f95d63292cc 100644 --- a/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java @@ -39,7 +39,6 @@ public class InventoryTransactionPacket extends DataPacket { public int transactionType; public NetworkInventoryAction[] actions; public TransactionData transactionData; - public boolean hasNetworkIds; public int legacyRequestId; /** @@ -61,7 +60,6 @@ public void encode() { this.putVarInt(this.legacyRequestId); //TODO legacySlot array this.putUnsignedVarInt(this.transactionType); - this.putBoolean(this.hasNetworkIds); this.putUnsignedVarInt(this.actions.length); for (NetworkInventoryAction action : this.actions) { action.write(this); @@ -121,8 +119,6 @@ public void decode() { this.transactionType = (int) this.getUnsignedVarInt(); - this.hasNetworkIds = this.getBoolean(); - int length = (int) this.getUnsignedVarInt(); Collection actions = new ArrayDeque<>(); for (int i = 0; i < length; i++) { diff --git a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java index 8160e816cfd..72dd02dd2ea 100644 --- a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java +++ b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java @@ -14,12 +14,12 @@ public interface ProtocolInfo { * Actual Minecraft: PE protocol version */ @SuppressWarnings("UnnecessaryBoxing") - int CURRENT_PROTOCOL = Integer.valueOf("428"); // DO NOT REMOVE BOXING + int CURRENT_PROTOCOL = Integer.valueOf("431"); // DO NOT REMOVE BOXING List SUPPORTED_PROTOCOLS = Ints.asList(CURRENT_PROTOCOL); - String MINECRAFT_VERSION = "v1.16.210"; - String MINECRAFT_VERSION_NETWORK = "1.16.210"; + String MINECRAFT_VERSION = "v1.16.220"; + String MINECRAFT_VERSION_NETWORK = "1.16.220"; byte LOGIN_PACKET = 0x01; byte PLAY_STATUS_PACKET = 0x02; diff --git a/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java b/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java index ecac51e3fa2..b93bd97d6d0 100644 --- a/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java @@ -35,8 +35,8 @@ public void encode() { this.putByte(windowType); this.putVarInt(unknownVarInt1); this.putVarInt(tradeTier); - this.putEntityUniqueId(player); this.putEntityUniqueId(trader); + this.putEntityUniqueId(player); this.putString(displayName); this.putBoolean(screen2); this.putBoolean(isWilling); 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 36b132c4af5..49943e718e4 100644 --- a/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java +++ b/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java @@ -108,10 +108,6 @@ public NetworkInventoryAction read(InventoryTransactionPacket packet) { this.inventorySlot = (int) packet.getUnsignedVarInt(); this.oldItem = packet.getSlot(); this.newItem = packet.getSlot(); - - if (packet.hasNetworkIds) { - this.stackNetworkId = packet.getVarInt(); - } return this; } @@ -134,10 +130,6 @@ public void write(InventoryTransactionPacket packet) { packet.putUnsignedVarInt(this.inventorySlot); packet.putSlot(this.oldItem); packet.putSlot(this.newItem); - - if (packet.hasNetworkIds) { - packet.putVarInt(this.stackNetworkId); - } } public InventoryAction createInventoryAction(Player player) { diff --git a/src/main/java/cn/nukkit/utils/BinaryStream.java b/src/main/java/cn/nukkit/utils/BinaryStream.java index f29d4815b00..d118b4a629d 100644 --- a/src/main/java/cn/nukkit/utils/BinaryStream.java +++ b/src/main/java/cn/nukkit/utils/BinaryStream.java @@ -1,12 +1,12 @@ package cn.nukkit.utils; +import cn.nukkit.block.Block; import cn.nukkit.entity.Attribute; import cn.nukkit.entity.data.Skin; -import cn.nukkit.item.Item; -import cn.nukkit.item.ItemDurable; -import cn.nukkit.item.RuntimeItems; +import cn.nukkit.item.*; import cn.nukkit.level.GameRule; import cn.nukkit.level.GameRules; +import cn.nukkit.level.GlobalBlockPalette; import cn.nukkit.math.BlockFace; import cn.nukkit.math.BlockVector3; import cn.nukkit.math.Vector3f; @@ -14,8 +14,12 @@ import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.nbt.tag.StringTag; +import cn.nukkit.network.LittleEndianByteBufInputStream; +import cn.nukkit.network.LittleEndianByteBufOutputStream; import cn.nukkit.network.protocol.types.EntityLink; -import it.unimi.dsi.fastutil.io.FastByteArrayInputStream; +import io.netty.buffer.AbstractByteBufAllocator; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import java.io.IOException; import java.lang.reflect.Array; @@ -370,158 +374,191 @@ public SerializedImage getImage() { } public Item getSlot() { - int networkId = this.getVarInt(); - if (networkId == 0) { + int id = getVarInt(); + if (id == 0) { return Item.get(0, 0, 0); } - int legacyFullId = RuntimeItems.getRuntimeMapping().getLegacyFullId(networkId); - int id = RuntimeItems.getId(legacyFullId); - boolean hasData = RuntimeItems.hasData(legacyFullId); + int count = getLShort(); + int damage = (int) getUnsignedVarInt(); + + int fullId = RuntimeItems.getRuntimeMapping().getLegacyFullId(id); + id = RuntimeItems.getId(fullId); - int auxValue = this.getVarInt(); - int data = auxValue >> 8; + /*boolean hasData = RuntimeItems.hasData(fullId); // Unnecessary when the damage is read from NBT if (hasData) { - // Swap data using legacy full id - data = RuntimeItems.getData(legacyFullId); - } else if (data == Short.MAX_VALUE) { - data = -1; + damage = RuntimeItems.getData(fullId); + }*/ + + if (getBoolean()) { // hasNetId + getVarInt(); // netId } - int cnt = auxValue & 0xff; - int nbtLen = this.getLShort(); + getVarInt(); // blockRuntimeId + + byte[] bytes = getByteArray(); + ByteBuf buf = AbstractByteBufAllocator.DEFAULT.ioBuffer(bytes.length); + buf.writeBytes(bytes); + byte[] nbt = new byte[0]; - if (nbtLen < Short.MAX_VALUE) { - nbt = this.get(nbtLen); - } else if (nbtLen == 65535) { - int nbtTagCount = (int) getUnsignedVarInt(); - int offset = getOffset(); - FastByteArrayInputStream stream = new FastByteArrayInputStream(get()); - for (int i = 0; i < nbtTagCount; i++) { - try { - // TODO: 05/02/2019 This hack is necessary because we keep the raw NBT tag. Try to remove it. - CompoundTag tag = NBTIO.read(stream, ByteOrder.LITTLE_ENDIAN, true); - // tool damage hack - if (tag.contains("Damage")) { - data = tag.getInt("Damage"); - tag.remove("Damage"); - } - if (tag.contains("__DamageConflict__")) { - tag.put("Damage", tag.removeAndGet("__DamageConflict__")); - } - if (tag.getAllTags().size() > 0) { - nbt = NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN, false); - } - } catch (IOException e) { - throw new RuntimeException(e); + String[] canPlace; + String[] canBreak; + + try (LittleEndianByteBufInputStream stream = new LittleEndianByteBufInputStream(buf)) { + int nbtSize = stream.readShort(); + + CompoundTag compoundTag = null; + if (nbtSize > 0) { + compoundTag = NBTIO.read(stream, ByteOrder.LITTLE_ENDIAN); + } else if (nbtSize == -1) { + int tagCount = stream.readUnsignedByte(); + if (tagCount != 1) throw new IllegalArgumentException("Expected 1 tag but got " + tagCount); + compoundTag = NBTIO.read(stream, ByteOrder.LITTLE_ENDIAN); + } + + if (compoundTag != null && compoundTag.getAllTags().size() > 0) { + if (compoundTag.contains("Damage")) { + damage = compoundTag.getInt("Damage"); + compoundTag.remove("Damage"); + } + if (compoundTag.contains("__DamageConflict__")) { + compoundTag.put("Damage", compoundTag.removeAndGet("__DamageConflict__")); + } + if (!compoundTag.isEmpty()) { + nbt = NBTIO.write(compoundTag, ByteOrder.LITTLE_ENDIAN); } } - setOffset(offset + (int) stream.position()); - } - String[] canPlaceOn = new String[this.getVarInt()]; - for (int i = 0; i < canPlaceOn.length; ++i) { - canPlaceOn[i] = this.getString(); - } + canPlace = new String[stream.readInt()]; + for (int i = 0; i < canPlace.length; i++) { + canPlace[i] = stream.readUTF(); + } - String[] canDestroy = new String[this.getVarInt()]; - for (int i = 0; i < canDestroy.length; ++i) { - canDestroy[i] = this.getString(); + canBreak = new String[stream.readInt()]; + for (int i = 0; i < canBreak.length; i++) { + canBreak[i] = stream.readUTF(); + } + + if (id == ItemID.SHIELD) { + stream.readLong(); + } + } catch (IOException e) { + throw new IllegalStateException("Unable to read item user data", e); + } finally { + buf.release(); } - Item item = Item.get( - id, data, cnt, nbt - ); + Item item = Item.get(id, damage, count, nbt); - if (canDestroy.length > 0 || canPlaceOn.length > 0) { + if (canBreak.length > 0 || canPlace.length > 0) { CompoundTag namedTag = item.getNamedTag(); if (namedTag == null) { namedTag = new CompoundTag(); } - if (canDestroy.length > 0) { + if (canBreak.length > 0) { ListTag listTag = new ListTag<>("CanDestroy"); - for (String blockName : canDestroy) { + for (String blockName : canBreak) { listTag.add(new StringTag("", blockName)); } namedTag.put("CanDestroy", listTag); } - if (canPlaceOn.length > 0) { + if (canPlace.length > 0) { ListTag listTag = new ListTag<>("CanPlaceOn"); - for (String blockName : canPlaceOn) { + for (String blockName : canPlace) { listTag.add(new StringTag("", blockName)); } namedTag.put("CanPlaceOn", listTag); } - item.setNamedTag(namedTag); - } - if (item.getId() == 513) { // TODO: Shields - this.getVarLong(); + item.setNamedTag(namedTag); } return item; } public void putSlot(Item item) { + this.putSlot(item, false); + } + + public void putSlot(Item item, boolean instanceItem) { if (item == null || item.getId() == 0) { - this.putVarInt(0); + putByte((byte) 0); return; } int networkFullId = RuntimeItems.getRuntimeMapping().getNetworkFullId(item); int networkId = RuntimeItems.getNetworkId(networkFullId); - boolean clearData = RuntimeItems.hasData(networkFullId); - this.putVarInt(networkId); - int auxValue = item.getCount(); - boolean isDurable = item instanceof ItemDurable; - if (!isDurable) { - int meta = clearData ? 0 : item.hasMeta() ? item.getDamage() : -1;; - auxValue |= ((meta & 0x7fff) << 8); + putVarInt(networkId); + putLShort(item.getCount()); + + boolean useLegacyData = false; + if (item.getId() > 256) { // Not a block + if (item instanceof ItemDurable || !RuntimeItems.hasData(networkFullId)) { + useLegacyData = true; + } } - this.putVarInt(auxValue); + putUnsignedVarInt(useLegacyData ? item.getDamage() : 0); + + if (!instanceItem) { + putBoolean(true); + putVarInt(0); //TODO + } + + Block block = item.getBlockUnsafe(); + int runtimeId = block == null ? 0 : GlobalBlockPalette.getOrCreateRuntimeId(block.getId(), block.getDamage()); + putVarInt(runtimeId); - if (item.hasCompoundTag() || isDurable) { - try { - // hack for tool damage + ByteBuf userDataBuf = ByteBufAllocator.DEFAULT.ioBuffer(); + try (LittleEndianByteBufOutputStream stream = new LittleEndianByteBufOutputStream(userDataBuf)) { + if (item.getDamage() != 0) { byte[] nbt = item.getCompoundTag(); CompoundTag tag; if (nbt == null || nbt.length == 0) { tag = new CompoundTag(); } else { - tag = NBTIO.read(nbt, ByteOrder.LITTLE_ENDIAN, false); + tag = NBTIO.read(nbt, ByteOrder.LITTLE_ENDIAN); } if (tag.contains("Damage")) { tag.put("__DamageConflict__", tag.removeAndGet("Damage")); } - if (isDurable) { - tag.putInt("Damage", item.getDamage()); - } + tag.putInt("Damage", item.getDamage()); + stream.writeShort(-1); + stream.writeByte(1); // Hardcoded in current version + stream.write(NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN)); + } else if (item.hasCompoundTag()) { + stream.writeShort(-1); + stream.writeByte(1); // Hardcoded in current version + stream.write(item.getCompoundTag()); + } else { + userDataBuf.writeShortLE(0); + } - this.putLShort(0xffff); - this.putByte((byte) 1); - this.put(NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN, true)); - } catch (IOException e) { - throw new RuntimeException(e); + List canPlaceOn = extractStringList(item, "CanPlaceOn"); + stream.writeInt(canPlaceOn.size()); + for (String string : canPlaceOn) { + stream.writeUTF(string); + } + + List canDestroy = extractStringList(item, "CanDestroy"); + stream.writeInt(canDestroy.size()); + for (String string : canDestroy) { + stream.writeUTF(string); + } + + if (item.getId() == ItemID.SHIELD) { + stream.writeLong(0); } - } else { - this.putLShort(0); - } - List canPlaceOn = extractStringList(item, "CanPlaceOn"); - List canDestroy = extractStringList(item, "CanDestroy"); - this.putVarInt(canPlaceOn.size()); - for (String block : canPlaceOn) { - this.putString(block); - } - this.putVarInt(canDestroy.size()); - for (String block : canDestroy) { - this.putString(block); - } - if (item.getId() == 513) { // TODO: Shields - this.putVarLong(0); + byte[] bytes = new byte[userDataBuf.readableBytes()]; + userDataBuf.readBytes(bytes); + putByteArray(bytes); + } catch (IOException e) { + throw new IllegalStateException("Unable to write item user data", e); + } finally { + userDataBuf.release(); } }