diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 12e754eebf..630ae59e39 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -20,7 +20,11 @@ Nukkit will create a bug report for EVERY exception and error detected, and ther In the report, you can see if the error is caused by Nukkit or a plugin. However, when "PLUGIN ERROR" is "false" and there are plugins running, it does not necessarily indicates that the error is caused by Nukkit. -While sending issues, please provide **as much information as you could**, or our developers might got stuck or confused when looking into your issue. +To sumbit bugs and problems, please upload the automaticly generated report. Make sure you have filled in all blanks in the template. Please provide **as much information as you could**, or our developers might got stuck or confused when looking into your issue. + +To submit feature requests and suggestions, please explicitly describe the feature you want or your suggestion. + +**Note that the Issues section on GitHub is not for contents that are not related to the two categories listed above. Irrelevant issues will be closed. Please visit our forums for other kinds of discussions.** Example --- diff --git a/README.md b/README.md index 25a73cec5e..ee7f80a2e4 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,12 @@ It has a few key advantages over other server software: Nukkit is **under improvement** yet, we welcome contributions. +Example servers running Nukkit +-------------------- +- **play.EaseCation.net** +- **play.GameTeam.cz** +- **MultiLabs.net** + Get Nukkit & Plugins -------------------- @@ -49,6 +55,7 @@ Get Nukkit & Plugins *These sites are provided by our users, Nukkit staff are not responsible for the reliability of these sites. Jar files downloaded here are only for reference - to try the latest update or for commercial uses, compile by yourself.* * __[Jenkins by MengCraft](http://ci.mengcraft.com:8080/job/Nukkit/lastSuccessfulBuild/)__ (**UNOFFICIAL**) +* __[Yivesmirror](https://yivesmirror.com/downloads/nukkit)__ (**UNOFFICIAL**) Build JAR file ------------- @@ -74,6 +81,10 @@ There're some tools for Nukkit developers. * __[FDevTools](https://github.com/fengberd/FDevTools)__ (**Load source and pack them easily**) * __[PocketServer](https://github.com/fengberd/MinecraftPEServer)__ (**Run Nukkit on android devices**) +Contributing +------------ +Please read the [CONTRIBUTING](https://github.com/Nukkit/Nukkit/blob/master/.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. + Discussion ------------- * __[Forums](https://forums.nukkit.io)__ diff --git a/pom.xml b/pom.xml index 12451a5dfc..a8d97a2046 100644 --- a/pom.xml +++ b/pom.xml @@ -170,6 +170,54 @@ ${project.build.directory}/dependency-reduced-pom.xml + + pl.project13.maven + git-commit-id-plugin + 2.2.1 + + + get-the-git-infos + + revision + + + + + + ${project.basedir}/.git + git + dd.MM.yyyy '@' HH:mm:ss z + ${user.timezone} + true + true + ${project.build.outputDirectory}/git.properties + + properties + true + false + false + false + false + true + + git.user.* + + + + false + 7 + flat + + false + false + 7 + -dirty + * + false + false + + + diff --git a/src/main/java/cn/nukkit/AdventureSettings.java b/src/main/java/cn/nukkit/AdventureSettings.java index 9b7d35cf76..7b62902ba8 100644 --- a/src/main/java/cn/nukkit/AdventureSettings.java +++ b/src/main/java/cn/nukkit/AdventureSettings.java @@ -2,6 +2,9 @@ import cn.nukkit.network.protocol.AdventureSettingsPacket; +import java.util.EnumMap; +import java.util.Map; + /** * Nukkit Project * Author: MagicDroidX @@ -14,27 +17,12 @@ public class AdventureSettings implements Cloneable { public static final int PERMISSION_AUTOMATION = 3; public static final int PERMISSION_ADMIN = 4; - private boolean canDestroyBlock = true; - - private boolean autoJump = true; - - private boolean canFly = false; - - private boolean flying = false; - - private boolean noclip = false; - - private boolean noPvp = false; - - private boolean noPvm = false; - - private boolean noMvp = false; - - private boolean muted = false; + private Map values = new EnumMap<>(Type.class); private Player player; - private AdventureSettings() { + public AdventureSettings(Player player) { + this.player = player; } public AdventureSettings clone(Player newPlayer) { @@ -47,129 +35,62 @@ public AdventureSettings clone(Player newPlayer) { } } - public void setCanDestroyBlock(boolean canDestroyBlock) { - this.canDestroyBlock = canDestroyBlock; - } - - public void setAutoJump(boolean autoJump) { - this.autoJump = autoJump; - } - - public void setCanFly(boolean canFly) { - this.canFly = canFly; + public AdventureSettings set(Type type, boolean value) { + this.values.put(type, value); + return this; } - public void setFlying(boolean flying) { - this.flying = flying; - } - - public void setNoclip(boolean noclip) { - this.noclip = noclip; - } - - public void setNoPvp(boolean noPvp) { - this.noPvp = noPvp; - } - - public void setNoPvm(boolean noPvm) { - this.noPvm = noPvm; - } - - public void setNoMvp(boolean noMvp) { - this.noMvp = noMvp; - } - - public void setMuted(boolean muted) { - this.muted = muted; - } - - public boolean canDestroyBlock() { - return canDestroyBlock; - } + public boolean get(Type type) { + Boolean value = this.values.get(type); - public boolean isAutoJumpEnabled() { - return autoJump; - } - - public boolean canFly() { - return canFly; - } - - public boolean isFlying() { - return flying; - } - - public boolean isNoclipEnabled() { - return noclip; - } - - public boolean isNoPvp() { - return noPvp; - } - - public boolean isNoPvm() { - return noPvm; - } - - public boolean isNoMvp() { - return noMvp; - } - - public boolean isMuted() { - return muted; + return value == null ? type.getDefaultValue() : value; } public void update() { AdventureSettingsPacket pk = new AdventureSettingsPacket(); - pk.flags = 0; - pk.worldImmutable = !canDestroyBlock; - pk.autoJump = autoJump; - pk.allowFlight = canFly; - pk.noClip = noclip; - pk.isFlying = flying; - pk.noPvp = noPvp; - pk.noPvm = noPvm; - pk.noMvp = noMvp; - pk.muted = muted; - pk.userPermission = (this.player.isOp() ? PERMISSION_OPERATOR : PERMISSION_NORMAL); - player.dataPacket(pk); - - player.resetInAirTicks(); - } - - public static class Builder { - private final AdventureSettings settings = new AdventureSettings(); - - public Builder(Player player) { - if (player == null) { - throw new IllegalArgumentException("Player can not be null."); - } - - settings.player = player; + for (Type t : Type.values()) { + pk.setFlag(t.getId(), get(t)); } - public Builder canFly(boolean can) { - settings.canFly = can; - return this; - } + pk.commandPermission = (player.isOp() ? AdventureSettingsPacket.PERMISSION_OPERATOR : AdventureSettingsPacket.PERMISSION_NORMAL); + pk.playerPermission = (player.isOp() ? Player.PERMISSION_OPERATOR : Player.PERMISSION_MEMBER); + pk.entityUniqueId = player.getId(); - public Builder noclip(boolean noclip) { - settings.noclip = noclip; - return this; - } + Server.broadcastPacket(Server.getInstance().getOnlinePlayers().values(), pk); + + player.resetInAirTicks(); + } - public Builder canDestroyBlock(boolean can) { - settings.canDestroyBlock = can; - return this; + public enum Type { + WORLD_IMMUTABLE(AdventureSettingsPacket.WORLD_IMMUTABLE, false), + AUTO_JUMP(AdventureSettingsPacket.AUTO_JUMP, true), + ALLOW_FLIGHT(AdventureSettingsPacket.ALLOW_FLIGHT, false), + NO_CLIP(AdventureSettingsPacket.NO_CLIP, false), + WORLD_BUILDER(AdventureSettingsPacket.WORLD_BUILDER, true), + FLYING(AdventureSettingsPacket.FLYING, false), + MUTED(AdventureSettingsPacket.MUTED, false), + BUILD_AND_MINE(AdventureSettingsPacket.BUILD_AND_MINE, true), + DOORS_AND_SWITCHED(AdventureSettingsPacket.DOORS_AND_SWITCHES, true), + OPEN_CONTAINERS(AdventureSettingsPacket.OPEN_CONTAINERS, true), + ATTACK_PLAYERS(AdventureSettingsPacket.ATTACK_PLAYERS, true), + ATTACK_MOBS(AdventureSettingsPacket.ATTACK_MOBS, true), + OPERATOR(AdventureSettingsPacket.OPERATOR, false), + TELEPORT(AdventureSettingsPacket.TELEPORT, false); + + private final int id; + private final boolean defaultValue; + + Type(int id, boolean defaultValue) { + this.id = id; + this.defaultValue = defaultValue; } - public Builder autoJump(boolean autoJump) { - settings.autoJump = autoJump; - return this; + public int getId() { + return id; } - public AdventureSettings build() { - return this.settings; + public boolean getDefaultValue() { + return this.defaultValue; } } } diff --git a/src/main/java/cn/nukkit/Nukkit.java b/src/main/java/cn/nukkit/Nukkit.java index 435374a0a4..03ec56f906 100644 --- a/src/main/java/cn/nukkit/Nukkit.java +++ b/src/main/java/cn/nukkit/Nukkit.java @@ -2,9 +2,14 @@ import cn.nukkit.command.CommandReader; import cn.nukkit.network.protocol.ProtocolInfo; +import cn.nukkit.utils.LogLevel; import cn.nukkit.utils.MainLogger; import cn.nukkit.utils.ServerKiller; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + /** * `_ _ _ _ _ _ * | \ | | | | | | (_) | @@ -50,16 +55,42 @@ public static void main(String[] args) { } } + LogLevel logLevel = LogLevel.DEFAULT_LEVEL; + int index = -1; + boolean skip = false; //启动参数 for (String arg : args) { + index++; + if (skip) { + skip = false; + continue; + } + switch (arg) { case "disable-ansi": ANSI = false; break; + + case "--verbosity": + case "-v": + skip = true; + try { + String levelName = args[index + 1]; + Set levelNames = Arrays.stream(LogLevel.values()).map(level -> level.name().toLowerCase()).collect(Collectors.toSet()); + if (!levelNames.contains(levelName.toLowerCase())) { + System.out.printf("'%s' is not a valid log level, using the default\n", levelName); + continue; + } + logLevel = Arrays.stream(LogLevel.values()).filter(level -> level.name().equalsIgnoreCase(levelName)).findAny().orElse(LogLevel.DEFAULT_LEVEL); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("You must enter the requested log level, using the default\n"); + } + } } - MainLogger logger = new MainLogger(DATA_PATH + "server.log"); + MainLogger logger = new MainLogger(DATA_PATH + "server.log", logLevel); + System.out.printf("Using log level '%s'\n", logLevel); try { if (ANSI) { diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java index ee5bd60006..b3a2447859 100644 --- a/src/main/java/cn/nukkit/Player.java +++ b/src/main/java/cn/nukkit/Player.java @@ -1,36 +1,42 @@ package cn.nukkit; +import cn.nukkit.AdventureSettings.Type; import cn.nukkit.block.*; import cn.nukkit.blockentity.BlockEntity; import cn.nukkit.blockentity.BlockEntityItemFrame; -import cn.nukkit.blockentity.BlockEntitySign; import cn.nukkit.blockentity.BlockEntitySpawnable; import cn.nukkit.command.Command; import cn.nukkit.command.CommandSender; import cn.nukkit.command.data.CommandDataVersions; -import cn.nukkit.command.data.CommandParameter; -import cn.nukkit.command.data.args.CommandArg; -import cn.nukkit.command.data.args.CommandArgBlockVector; -import cn.nukkit.entity.Attribute; -import cn.nukkit.entity.Entity; -import cn.nukkit.entity.EntityHuman; -import cn.nukkit.entity.EntityLiving; +import cn.nukkit.entity.*; import cn.nukkit.entity.data.*; import cn.nukkit.entity.item.*; -import cn.nukkit.entity.mob.EntityCreeper; -import cn.nukkit.entity.projectile.*; +import cn.nukkit.entity.projectile.EntityArrow; import cn.nukkit.event.block.ItemFrameDropItemEvent; -import cn.nukkit.event.block.SignChangeEvent; -import cn.nukkit.event.entity.*; +import cn.nukkit.event.entity.EntityDamageByBlockEvent; +import cn.nukkit.event.entity.EntityDamageByEntityEvent; +import cn.nukkit.event.entity.EntityDamageEvent; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; import cn.nukkit.event.entity.EntityDamageEvent.DamageModifier; -import cn.nukkit.event.inventory.*; +import cn.nukkit.event.inventory.CraftItemEvent; +import cn.nukkit.event.inventory.InventoryCloseEvent; +import cn.nukkit.event.inventory.InventoryPickupArrowEvent; +import cn.nukkit.event.inventory.InventoryPickupItemEvent; import cn.nukkit.event.player.*; import cn.nukkit.event.player.PlayerInteractEvent.Action; import cn.nukkit.event.player.PlayerTeleportEvent.TeleportCause; import cn.nukkit.event.server.DataPacketReceiveEvent; import cn.nukkit.event.server.DataPacketSendEvent; +import cn.nukkit.form.window.FormWindow; +import cn.nukkit.form.window.FormWindowCustom; import cn.nukkit.inventory.*; +import cn.nukkit.inventory.transaction.InventoryTransaction; +import cn.nukkit.inventory.transaction.SimpleInventoryTransaction; +import cn.nukkit.inventory.transaction.action.InventoryAction; +import cn.nukkit.inventory.transaction.action.SlotChangeAction; +import cn.nukkit.inventory.transaction.data.ReleaseItemData; +import cn.nukkit.inventory.transaction.data.UseItemData; +import cn.nukkit.inventory.transaction.data.UseItemOnEntityData; import cn.nukkit.item.*; import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.item.food.Food; @@ -46,13 +52,14 @@ import cn.nukkit.level.particle.PunchBlockParticle; import cn.nukkit.level.sound.ExperienceOrbSound; import cn.nukkit.level.sound.ItemFrameItemRemovedSound; -import cn.nukkit.level.sound.LaunchSound; import cn.nukkit.math.*; import cn.nukkit.metadata.MetadataValue; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.*; import cn.nukkit.network.SourceInterface; import cn.nukkit.network.protocol.*; +import cn.nukkit.network.protocol.types.ContainerIds; +import cn.nukkit.network.protocol.types.NetworkInventoryAction; import cn.nukkit.permission.PermissibleBase; import cn.nukkit.permission.Permission; import cn.nukkit.permission.PermissionAttachment; @@ -61,20 +68,17 @@ import cn.nukkit.potion.Effect; import cn.nukkit.potion.Potion; import cn.nukkit.resourcepacks.ResourcePack; -import cn.nukkit.utils.Binary; -import cn.nukkit.utils.ClientChainData; -import cn.nukkit.utils.TextFormat; -import cn.nukkit.utils.Zlib; +import cn.nukkit.utils.*; import co.aikar.timings.Timing; import co.aikar.timings.Timings; -import com.google.gson.Gson; -import com.google.gson.JsonElement; +import java.awt.*; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteOrder; import java.util.*; -import java.util.concurrent.ThreadLocalRandom; +import java.util.List; +import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicReference; /** @@ -100,6 +104,14 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde public static final float DEFAULT_SPEED = 0.1f; public static final float MAXIMUM_SPEED = 0.5f; + public static final int PERMISSION_CUSTOM = 3; + public static final int PERMISSION_OPERATOR = 2; + public static final int PERMISSION_MEMBER = 1; + public static final int PERMISSION_VISITOR = 0; + + public static final int ANVIL_WINDOW_ID = 2; + public static final int ENCHANT_WINDOW_ID = 3; + protected final SourceInterface interfaz; public boolean playedBefore; @@ -108,11 +120,12 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde public int gamemode; public long lastBreak; - protected int windowCnt = 2; + protected int windowCnt = 4; protected Map windows; protected Map windowIndex = new HashMap<>(); + protected Set permanentWindows = new HashSet<>(); protected int messageCounter = 2; @@ -122,10 +135,11 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde public final HashSet achievements = new HashSet<>(); - protected SimpleTransactionGroup currentTransaction = null; - public int craftingType = CRAFTING_SMALL; + protected PlayerCursorInventory cursorInventory; + protected CraftingGrid craftingGrid; + public long creationTime = 0; protected long randomClientId; @@ -201,7 +215,37 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde protected int lastEnderPearl = -1; - private ClientChainData loginChainData; + private LoginChainData loginChainData; + + public Block breakingBlock = null; + + public int pickedXPOrb = 0; + + protected int formWindowCount = 0; + protected Map formWindows = new HashMap<>(); + protected Map serverSettings = new HashMap<>(); + + protected Map dummyBossBars = new HashMap<>(); + + public int getStartActionTick() { + return startAction; + } + + public void startAction() { + this.startAction = this.server.getTick(); + } + + public void stopAction() { + this.startAction = -1; + } + + public int getLastEnderPearlThrowingTick() { + return lastEnderPearl; + } + + public void onThrowEnderPearl() { + this.lastEnderPearl = this.server.getTick(); + } public BlockEnderChest getViewingEnderChest() { return viewingEnderChest; @@ -297,24 +341,42 @@ public void resetInAirTicks() { @Deprecated public void setAllowFlight(boolean value) { - this.getAdventureSettings().setCanFly(value); + this.getAdventureSettings().set(Type.ALLOW_FLIGHT, value); this.getAdventureSettings().update(); } @Deprecated public boolean getAllowFlight() { - return this.getAdventureSettings().canFly(); + return this.getAdventureSettings().get(Type.ALLOW_FLIGHT); + } + + public void setAllowModifyWorld(boolean value) { + this.getAdventureSettings().set(Type.WORLD_IMMUTABLE, !value); + this.getAdventureSettings().set(Type.BUILD_AND_MINE, value); + this.getAdventureSettings().set(Type.WORLD_BUILDER, value); + this.getAdventureSettings().update(); + } + + public void setAllowInteract(boolean value) { + setAllowInteract(value, value); + } + + public void setAllowInteract(boolean value, boolean containers) { + this.getAdventureSettings().set(Type.WORLD_IMMUTABLE, !value); + this.getAdventureSettings().set(Type.DOORS_AND_SWITCHED, value); + this.getAdventureSettings().set(Type.OPEN_CONTAINERS, containers); + this.getAdventureSettings().update(); } @Deprecated public void setAutoJump(boolean value) { - this.getAdventureSettings().setAutoJump(value); + this.getAdventureSettings().set(Type.AUTO_JUMP, value); this.getAdventureSettings().update(); } @Deprecated public boolean hasAutoJump() { - return this.getAdventureSettings().isAutoJumpEnabled(); + return this.getAdventureSettings().get(Type.AUTO_JUMP); } @Override @@ -493,7 +555,7 @@ public void sendCommandData() { } if (count > 0) { //TODO: structure checking - pk.commands = new Gson().toJson(data); + pk.commands = data; int identifier = this.dataPacket(pk, true); // We *need* ACK so we can be sure that the client received the packet or not Thread t = new Thread() { public void run() { @@ -505,7 +567,8 @@ public void run() { sendCommandData(); return; } - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + } } }; t.start(); @@ -544,6 +607,13 @@ public Player(SourceInterface interfaz, Long clientID, String ip, int port) { this.creationTime = System.currentTimeMillis(); } + @Override + protected void initEntity() { + super.initEntity(); + + this.addDefaultWindows(); + } + public boolean isPlayer() { return true; } @@ -567,7 +637,7 @@ public String getDisplayName() { public void setDisplayName(String displayName) { this.displayName = displayName; if (this.spawned) { - this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getDisplayName(), this.getSkin()); + this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getDisplayName(), this.getSkin(), this.getLoginChainData().getXUID()); } } @@ -575,7 +645,7 @@ public void setDisplayName(String displayName) { public void setSkin(Skin skin) { super.setSkin(skin); if (this.spawned) { - this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getDisplayName(), skin); + this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getDisplayName(), skin, this.getLoginChainData().getXUID()); } } @@ -599,6 +669,20 @@ public int getInAirTicks() { return this.inAirTicks; } + /** + * Returns whether the player is currently using an item (right-click and hold). + * + * @return bool + */ + public boolean isUsingItem() { + return this.getDataFlag(DATA_FLAGS, DATA_FLAG_ACTION) && this.startAction > -1; + } + + public void setUsingItem(boolean value) { + this.startAction = value ? this.server.getTick() : -1; + this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, value); + } + public String getButtonText() { return this.buttonText; } @@ -752,7 +836,6 @@ protected void sendNextChunk() { protected void doFirstSpawn() { this.spawned = true; - this.server.sendRecipeList(this); this.getAdventureSettings().update(); this.sendPotionEffects(this); @@ -778,9 +861,7 @@ protected void doFirstSpawn() { respawnPacket.z = (float) pos.z; this.dataPacket(respawnPacket); - PlayStatusPacket playStatusPacket = new PlayStatusPacket(); - playStatusPacket.status = PlayStatusPacket.PLAYER_SPAWN; - this.dataPacket(playStatusPacket); + this.sendPlayStatus(PlayStatusPacket.PLAYER_SPAWN); PlayerJoinEvent playerJoinEvent = new PlayerJoinEvent(this, new TranslationContainer(TextFormat.YELLOW + "%multiplayer.player.joined", new String[]{ @@ -796,6 +877,16 @@ protected void doFirstSpawn() { this.noDamageTicks = 60; + this.getServer().sendRecipeList(this); + + if (this.gamemode == Player.SPECTATOR) { + InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); + inventoryContentPacket.inventoryId = ContainerIds.CREATIVE; + this.dataPacket(inventoryContentPacket); + } else { + inventory.sendCreativeContents(); + } + for (long index : this.usedChunks.keySet()) { int chunkX = Level.getHashX(index); int chunkZ = Level.getHashZ(index); @@ -847,7 +938,6 @@ protected boolean orderChunks() { int centerX = (int) this.x >> 4; int centerZ = (int) this.z >> 4; - int count = 0; for (int x = -this.chunkRadius; x <= this.chunkRadius; x++) { for (int z = -this.chunkRadius; z <= this.chunkRadius; z++) { @@ -858,7 +948,6 @@ protected boolean orderChunks() { long index; if (!(this.usedChunks.containsKey(index = Level.chunkHash(chunkX, chunkZ))) || !this.usedChunks.get(index)) { newOrder.put(index, distance); - count++; } lastChunk.remove(index); } @@ -1065,14 +1154,12 @@ public int getGamemode() { } /** - * * Returns a client-friendly gamemode of the specified real gamemode * This function takes care of handling gamemodes known to MCPE (as of 1.1.0.3, that includes Survival, Creative and Adventure) - * + *

* TODO: remove this when Spectator Mode gets added properly to MCPE - * */ - private static int getClientFriendlyGamemode(int gamemode){ + private static int getClientFriendlyGamemode(int gamemode) { gamemode &= 0x03; if (gamemode == Player.SPECTATOR) { return Player.CREATIVE; @@ -1095,10 +1182,12 @@ public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings n if (newSettings == null) { newSettings = this.getAdventureSettings().clone(this); - newSettings.setCanDestroyBlock(gamemode != 3); - newSettings.setCanFly((gamemode & 0x01) > 0); - newSettings.setNoclip(gamemode == 0x03); - newSettings.setFlying(gamemode == 0x03); + newSettings.set(Type.WORLD_IMMUTABLE, (gamemode & 0x02) > 0); + newSettings.set(Type.BUILD_AND_MINE, (gamemode & 0x02) <= 0); + newSettings.set(Type.WORLD_BUILDER, (gamemode & 0x02) <= 0); + newSettings.set(Type.ALLOW_FLIGHT, (gamemode & 0x01) > 0); + newSettings.set(Type.NO_CLIP, gamemode == 0x03); + newSettings.set(Type.FLYING, gamemode == 0x03); } PlayerGameModeChangeEvent ev; @@ -1129,22 +1218,20 @@ public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings n this.setAdventureSettings(ev.getNewAdventureSettings()); if (this.isSpectator()) { - this.getAdventureSettings().setFlying(true); + this.getAdventureSettings().set(Type.FLYING, true); this.teleport(this.temporalVector.setComponents(this.x, this.y + 0.1, this.z)); - ContainerSetContentPacket containerSetContentPacket = new ContainerSetContentPacket(); - containerSetContentPacket.windowid = ContainerSetContentPacket.SPECIAL_CREATIVE; - containerSetContentPacket.eid = this.id; - this.dataPacket(containerSetContentPacket); + InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); + inventoryContentPacket.inventoryId = InventoryContentPacket.SPECIAL_CREATIVE; + this.dataPacket(inventoryContentPacket); } else { if (this.isSurvival()) { - this.getAdventureSettings().setFlying(false); + this.getAdventureSettings().set(Type.FLYING, false); } - ContainerSetContentPacket containerSetContentPacket = new ContainerSetContentPacket(); - containerSetContentPacket.windowid = ContainerSetContentPacket.SPECIAL_CREATIVE; - containerSetContentPacket.eid = this.id; - containerSetContentPacket.slots = Item.getCreativeItems().stream().toArray(Item[]::new); - this.dataPacket(containerSetContentPacket); + InventoryContentPacket inventoryContentPacket = new InventoryContentPacket(); + inventoryContentPacket.inventoryId = InventoryContentPacket.SPECIAL_CREATIVE; + inventoryContentPacket.slots = Item.getCreativeItems().stream().toArray(Item[]::new); + this.dataPacket(inventoryContentPacket); } this.resetFallDistance(); @@ -1153,6 +1240,7 @@ public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings n this.inventory.sendContents(this.getViewers().values()); this.inventory.sendHeldItem(this.hasSpawned.values()); + this.inventory.sendCreativeContents(); return true; } @@ -1254,6 +1342,8 @@ protected void checkBlockCollision() { if (portal) { inPortalTicks++; + } else { + this.inPortalTicks = 0; } } @@ -1265,95 +1355,7 @@ protected void checkNearEntities() { continue; } - if (entity instanceof EntityArrow && ((EntityArrow) entity).hadCollision) { - ItemArrow item = new ItemArrow(); - if (this.isSurvival() && !this.inventory.canAddItem(item)) { - continue; - } - - InventoryPickupArrowEvent ev; - this.server.getPluginManager().callEvent(ev = new InventoryPickupArrowEvent(this.inventory, (EntityArrow) entity)); - if (ev.isCancelled()) { - continue; - } - - TakeItemEntityPacket pk = new TakeItemEntityPacket(); - pk.entityId = this.getId(); - pk.target = entity.getId(); - Server.broadcastPacket(entity.getViewers().values(), pk); - - pk = new TakeItemEntityPacket(); - pk.entityId = this.id; - pk.target = entity.getId(); - this.dataPacket(pk); - - this.inventory.addItem(item.clone()); - entity.kill(); - } else if (entity instanceof EntityItem) { - if (((EntityItem) entity).getPickupDelay() <= 0) { - Item item = ((EntityItem) entity).getItem(); - - if (item != null) { - if (this.isSurvival() && !this.inventory.canAddItem(item)) { - continue; - } - - InventoryPickupItemEvent ev; - this.server.getPluginManager().callEvent(ev = new InventoryPickupItemEvent(this.inventory, (EntityItem) entity)); - if (ev.isCancelled()) { - continue; - } - - switch (item.getId()) { - case Item.WOOD: - case Item.WOOD2: - this.awardAchievement("mineWood"); - break; - case Item.DIAMOND: - this.awardAchievement("diamond"); - break; - } - /*switch (item.getId()) { - case Item.WOOD: - this.awardAchievement("mineWood"); - break; - case Item.DIAMOND: - this.awardAchievement("diamond"); - break; - }*/ - - TakeItemEntityPacket pk = new TakeItemEntityPacket(); - pk.entityId = this.getId(); - pk.target = entity.getId(); - Server.broadcastPacket(entity.getViewers().values(), pk); - - pk = new TakeItemEntityPacket(); - pk.entityId = this.id; - pk.target = entity.getId(); - this.dataPacket(pk); - - this.inventory.addItem(item.clone()); - entity.kill(); - } - } - } - } - for (Entity entity : this.level.getNearbyEntities(this.boundingBox.grow(3.5, 2, 3.5), this)) { - entity.scheduleUpdate(); - - if (!entity.isAlive() || !this.isAlive()) { - continue; - } - if (entity instanceof EntityXPOrb) { - EntityXPOrb xpOrb = (EntityXPOrb) entity; - if (xpOrb.getPickupDelay() <= 0) { - int exp = xpOrb.getExp(); - this.addExperience(exp); - entity.kill(); - this.getLevel().addSound(new ExperienceOrbSound(this)); - break; - } - } + this.pickupEntity(entity, true); } } @@ -1391,6 +1393,9 @@ protected void processMovement(int tickDiff) { double dz = newPos.z - this.z; this.fastMove(dx, dy, dz); + if (this.newPosition == null) { + return; //maybe solve that in better way + } double diffX = this.x - newPos.x; double diffY = this.y - newPos.y; @@ -1403,7 +1408,9 @@ protected void processMovement(int tickDiff) { if (diffX != 0 || diffY != 0 || diffZ != 0) { if (this.checkMovement && !server.getAllowFlight() && this.isSurvival()) { - if (!this.isSleeping()) { + // Some say: I cant move my head when riding because the server + // blocked my movement + if (!this.isSleeping() && this.riding == null) { double diffHorizontalSqr = (diffX * diffX + diffZ * diffZ) / ((double) (tickDiff * tickDiff)); if (diffHorizontalSqr > 0.125) { PlayerInvalidMoveEvent ev; @@ -1625,7 +1632,7 @@ public boolean onUpdate(int currentTick) { this.inAirTicks = 0; this.highestPosition = this.y; } else { - if (!this.isGliding() && !server.getAllowFlight() && !this.getAdventureSettings().canFly() && this.inAirTicks > 10 && !this.isSleeping() && !this.isImmobile()) { + if (!this.isGliding() && !server.getAllowFlight() && !this.getAdventureSettings().get(Type.ALLOW_FLIGHT) && this.inAirTicks > 10 && !this.isSleeping() && !this.isImmobile()) { 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); @@ -1656,19 +1663,77 @@ public boolean onUpdate(int currentTick) { } this.checkTeleportPosition(); + this.checkInteractNearby(); - // TODO: remove this workaround (broken client MCPE 1.0.0) - if (!messageQueue.isEmpty()) { - TextPacket pk = new TextPacket(); - pk.type = TextPacket.TYPE_RAW; - pk.message = String.join("\n", messageQueue); - this.dataPacket(pk); - messageQueue.clear(); + if (this.spawned && this.dummyBossBars.size() > 0 && currentTick % 100 == 0) { + this.dummyBossBars.values().forEach(DummyBossBar::updateBossEntityPosition); } + return true; } - private ArrayList messageQueue = new ArrayList<>(); + public void checkInteractNearby() { + int interactDistance = isCreative() ? 5 : 3; + if (canInteract(this, interactDistance)) { + if (getEntityPlayerLookingAt(interactDistance) != null) { + EntityInteractable onInteract = getEntityPlayerLookingAt(interactDistance); + setButtonText(onInteract.getInteractButtonText()); + } else { + setButtonText(""); + } + } else { + setButtonText(""); + } + } + + /** + * Returns the Entity the player is looking at currently + * + * @param maxDistance the maximum distance to check for entities + * @return Entity|null either NULL if no entity is found or an instance of the entity + */ + public EntityInteractable getEntityPlayerLookingAt(int maxDistance) { + timing.startTiming(); + + EntityInteractable entity = null; + + // just a fix because player MAY not be fully initialized + if (temporalVector != null) { + Entity[] nearbyEntities = level.getNearbyEntities(boundingBox.grow(maxDistance, maxDistance, maxDistance), this); + + // get all blocks in looking direction until the max interact distance is reached (it's possible that startblock isn't found!) + try { + BlockIterator itr = new BlockIterator(level, getPosition(), getDirectionVector(), getEyeHeight(), maxDistance); + if (itr.hasNext()) { + Block block; + while (itr.hasNext()) { + block = itr.next(); + entity = getEntityAtPosition(nearbyEntities, block.getFloorX(), block.getFloorY(), block.getFloorZ()); + if (entity != null) { + break; + } + } + } + } catch (Exception ex) { + // nothing to log here! + } + } + + timing.stopTiming(); + + return entity; + } + + private EntityInteractable getEntityAtPosition(Entity[] nearbyEntities, int x, int y, int z) { + for (Entity nearestEntity : nearbyEntities) { + if (nearestEntity.getFloorX() == x && nearestEntity.getFloorY() == y && nearestEntity.getFloorZ() == z + && nearestEntity instanceof EntityInteractable + && ((EntityInteractable) nearestEntity).canDoInteraction()) { + return (EntityInteractable) nearestEntity; + } + } + return null; + } public void checkNetwork() { if (!this.isOnline()) { @@ -1768,12 +1833,12 @@ protected void processLogin() { nbt.putInt("playerGameType", this.gamemode); } - this.adventureSettings = new AdventureSettings.Builder(this) - .canDestroyBlock(!isAdventure()) - .autoJump(true) - .canFly(isCreative()) - .noclip(isSpectator()) - .build(); + this.adventureSettings = new AdventureSettings(this) + .set(Type.WORLD_IMMUTABLE, isAdventure()) + .set(Type.WORLD_BUILDER, !isAdventure()) + .set(Type.AUTO_JUMP, true) + .set(Type.ALLOW_FLIGHT, isCreative()) + .set(Type.NO_CLIP, isSpectator()); Level level; if ((level = this.server.getLevelByName(nbt.getString("Level"))) == null || !alive) { @@ -1803,6 +1868,9 @@ protected void processLogin() { this.server.saveOfflinePlayerData(this.username, nbt, true); } + this.sendPlayStatus(PlayStatusPacket.LOGIN_SUCCESS); + this.server.onPlayerLogin(this); + ListTag posList = nbt.getList("Pos", DoubleTag.class); super.init(this.level.getChunk((int) posList.get(0).data >> 4, (int) posList.get(2).data >> 4, true), nbt); @@ -1817,30 +1885,31 @@ protected void processLogin() { float foodSaturationLevel = this.namedTag.getFloat("foodSaturationLevel"); this.foodData = new PlayerFood(this, foodLevel, foodSaturationLevel); + if (this.isSpectator()) this.keepMovement = true; + + this.forceMovement = this.teleportPosition = this.getPosition(); + + ResourcePacksInfoPacket infoPacket = new ResourcePacksInfoPacket(); + infoPacket.resourcePackEntries = this.server.getResourcePackManager().getResourceStack(); + infoPacket.mustAccept = this.server.getForceResources(); + this.dataPacket(infoPacket); + } + + protected void completeLoginSequence() { PlayerLoginEvent ev; this.server.getPluginManager().callEvent(ev = new PlayerLoginEvent(this, "Plugin reason")); if (ev.isCancelled()) { this.close(this.getLeaveMessage(), ev.getKickMessage()); - return; } - this.server.addOnlinePlayer(this); - this.loggedIn = true; - - if (this.isCreative()) { - this.inventory.setHeldItemSlot(0); - } else { - this.inventory.setHeldItemSlot(this.inventory.getHotbarSlotIndex(0)); - } - - if (this.isSpectator()) this.keepMovement = true; - + Level level; if (this.spawnPosition == null && this.namedTag.contains("SpawnLevel") && (level = this.server.getLevelByName(this.namedTag.getString("SpawnLevel"))) != null) { this.spawnPosition = new Position(this.namedTag.getInt("SpawnX"), this.namedTag.getInt("SpawnY"), this.namedTag.getInt("SpawnZ"), level); } Position spawnPosition = this.getSpawn(); + StartGamePacket startGamePacket = new StartGamePacket(); startGamePacket.entityUniqueId = this.id; startGamePacket.entityRuntimeId = this.id; @@ -1851,8 +1920,8 @@ protected void processLogin() { startGamePacket.yaw = (float) this.yaw; startGamePacket.pitch = (float) this.pitch; startGamePacket.seed = -1; - startGamePacket.dimension = (byte) (this.level.getDimension() & 0xff); - startGamePacket.gamemode = getClientFriendlyGamemode(this.gamemode); + startGamePacket.dimension = (byte) (spawnPosition.level.getDimension() & 0xff); + startGamePacket.worldGamemode = getClientFriendlyGamemode(this.gamemode); startGamePacket.difficulty = this.server.getDifficulty(); startGamePacket.spawnX = (int) spawnPosition.x; startGamePacket.spawnY = (int) spawnPosition.y; @@ -1868,9 +1937,9 @@ protected void processLogin() { startGamePacket.generator = 1; //0 old, 1 infinite, 2 flat this.dataPacket(startGamePacket); - SetTimePacket setTimePacket = new SetTimePacket(); - setTimePacket.time = this.level.getTime(); - this.dataPacket(setTimePacket); + this.loggedIn = true; + + spawnPosition.level.sendTime(this); this.setMovementSpeed(DEFAULT_SPEED); this.sendAttributes(); @@ -1892,26 +1961,10 @@ protected void processLogin() { this.setRemoveFormat(false); } - if (this.gamemode == Player.SPECTATOR) { - ContainerSetContentPacket containerSetContentPacket = new ContainerSetContentPacket(); - containerSetContentPacket.windowid = ContainerSetContentPacket.SPECIAL_CREATIVE; - containerSetContentPacket.eid = this.id; - this.dataPacket(containerSetContentPacket); - } else { - ContainerSetContentPacket containerSetContentPacket = new ContainerSetContentPacket(); - containerSetContentPacket.windowid = ContainerSetContentPacket.SPECIAL_CREATIVE; - containerSetContentPacket.eid = this.id; - containerSetContentPacket.slots = Item.getCreativeItems().stream().toArray(Item[]::new); - this.dataPacket(containerSetContentPacket); - } - this.setEnableClientCommand(true); - this.server.sendFullPlayerListData(this); - - this.forceMovement = this.teleportPosition = this.getPosition(); - - this.server.onPlayerLogin(this); + this.server.addOnlinePlayer(this); + this.server.onPlayerCompleteLoginSequence(this); } public void handleDataPacket(DataPacket packet) { @@ -1943,19 +1996,15 @@ public void handleDataPacket(DataPacket packet) { LoginPacket loginPacket = (LoginPacket) packet; String message; - if (loginPacket.getProtocol() != ProtocolInfo.CURRENT_PROTOCOL) { + if (loginPacket.getProtocol() < ProtocolInfo.CURRENT_PROTOCOL) { if (loginPacket.getProtocol() < ProtocolInfo.CURRENT_PROTOCOL) { message = "disconnectionScreen.outdatedClient"; - PlayStatusPacket pk = new PlayStatusPacket(); - pk.status = PlayStatusPacket.LOGIN_FAILED_CLIENT; - this.directDataPacket(pk); + this.sendPlayStatus(PlayStatusPacket.LOGIN_FAILED_CLIENT); } else { message = "disconnectionScreen.outdatedServer"; - PlayStatusPacket pk = new PlayStatusPacket(); - pk.status = PlayStatusPacket.LOGIN_FAILED_SERVER; - this.directDataPacket(pk); + this.sendPlayStatus(PlayStatusPacket.LOGIN_FAILED_SERVER); } this.close("", message, false); break; @@ -2018,15 +2067,7 @@ public void handleDataPacket(DataPacket packet) { break; } - PlayStatusPacket statusPacket = new PlayStatusPacket(); - statusPacket.status = PlayStatusPacket.LOGIN_SUCCESS; - this.dataPacket(statusPacket); - - ResourcePacksInfoPacket infoPacket = new ResourcePacksInfoPacket(); - infoPacket.resourcePackEntries = this.server.getResourcePackManager().getResourceStack(); - infoPacket.mustAccept = this.server.getForceResources(); - this.dataPacket(infoPacket); - + this.processLogin(); break; case ProtocolInfo.RESOURCE_PACK_CLIENT_RESPONSE_PACKET: ResourcePackClientResponsePacket responsePacket = (ResourcePackClientResponsePacket) packet; @@ -2058,7 +2099,7 @@ public void handleDataPacket(DataPacket packet) { this.dataPacket(stackPacket); break; case ResourcePackClientResponsePacket.STATUS_COMPLETED: - this.processLogin(); + this.completeLoginSequence(); break; } break; @@ -2077,6 +2118,15 @@ public void handleDataPacket(DataPacket packet) { dataPacket.progress = 1048576 * requestPacket.chunkIndex; this.dataPacket(dataPacket); break; + case ProtocolInfo.PLAYER_INPUT_PACKET: + if (!this.isAlive() || !this.spawned) { + break; + } + PlayerInputPacket ipk = (PlayerInputPacket) packet; + if (riding instanceof EntityMinecartAbstract) { + ((EntityMinecartEmpty) riding).setCurrentSpeed(ipk.motionY); + } + break; case ProtocolInfo.MOVE_PLAYER_PACKET: if (this.teleportPosition != null) { break; @@ -2121,16 +2171,16 @@ public void handleDataPacket(DataPacket packet) { case ProtocolInfo.ADVENTURE_SETTINGS_PACKET: //TODO: player abilities, check for other changes AdventureSettingsPacket adventureSettingsPacket = (AdventureSettingsPacket) packet; - if (adventureSettingsPacket.isFlying && !this.getAdventureSettings().canFly()) { + if (adventureSettingsPacket.getFlag(AdventureSettingsPacket.ALLOW_FLIGHT) && !this.getAdventureSettings().get(Type.ALLOW_FLIGHT)) { this.kick(PlayerKickEvent.Reason.FLYING_DISABLED, "Flying is not enabled on this server"); break; } - PlayerToggleFlightEvent playerToggleFlightEvent = new PlayerToggleFlightEvent(this, adventureSettingsPacket.isFlying); + PlayerToggleFlightEvent playerToggleFlightEvent = new PlayerToggleFlightEvent(this, adventureSettingsPacket.getFlag(AdventureSettingsPacket.ALLOW_FLIGHT)); this.server.getPluginManager().callEvent(playerToggleFlightEvent); if (playerToggleFlightEvent.isCancelled()) { this.getAdventureSettings().update(); } else { - this.getAdventureSettings().setFlying(playerToggleFlightEvent.isFlying()); + this.getAdventureSettings().set(Type.FLYING, playerToggleFlightEvent.isFlying()); } break; case ProtocolInfo.MOB_EQUIPMENT_PACKET: @@ -2140,486 +2190,89 @@ public void handleDataPacket(DataPacket packet) { MobEquipmentPacket mobEquipmentPacket = (MobEquipmentPacket) packet; - if (mobEquipmentPacket.slot == 0x28 || mobEquipmentPacket.slot == 0 || mobEquipmentPacket.slot == 255) { - mobEquipmentPacket.slot = -1; - } else { - mobEquipmentPacket.slot -= 9; - } - - Item item; - int slot; - if (this.isCreative()) { - item = mobEquipmentPacket.item; - slot = Item.getCreativeItemIndex(item); - } else { - item = this.inventory.getItem(mobEquipmentPacket.slot); - slot = mobEquipmentPacket.slot; - } - - if (mobEquipmentPacket.slot == -1) { - if (this.isCreative()) { - boolean found = false; - for (int i = 0; i < this.inventory.getHotbarSize(); ++i) { - if (this.inventory.getHotbarSlotIndex(i) == -1) { - this.inventory.setHeldItemIndex(i); - found = true; - break; - } + Item item = this.inventory.getItem(mobEquipmentPacket.hotbarSlot); - } - if (!found) { - this.inventory.sendContents(this); - break; - } - } else { - if (mobEquipmentPacket.selectedSlot >= 0 && mobEquipmentPacket.selectedSlot < 9) { - this.inventory.setHeldItemIndex(mobEquipmentPacket.selectedSlot); - this.inventory.setHeldItemSlot(mobEquipmentPacket.slot); - } else { - this.inventory.sendContents(this); - break; - } - } - } else if (item == null || slot == -1 || !item.deepEquals(mobEquipmentPacket.item)) { + if (!item.equals(mobEquipmentPacket.item)) { + this.server.getLogger().debug("Tried to equip " + mobEquipmentPacket.item + " but have " + item + " in target slot"); this.inventory.sendContents(this); - break; - } else if (this.isCreative()) { - this.inventory.setHeldItemIndex(mobEquipmentPacket.selectedSlot); - this.inventory.setItem(mobEquipmentPacket.selectedSlot, item); - this.inventory.setHeldItemSlot(mobEquipmentPacket.selectedSlot); - } else { - if (mobEquipmentPacket.selectedSlot >= 0 && mobEquipmentPacket.selectedSlot < this.inventory.getHotbarSize()) { - this.inventory.setHeldItemIndex(mobEquipmentPacket.selectedSlot); - this.inventory.setHeldItemSlot(slot); - } else { - this.inventory.sendContents(this); - break; - } + return; } - this.inventory.sendHeldItem(this.hasSpawned.values()); + this.inventory.equipItem(mobEquipmentPacket.hotbarSlot); this.setDataFlag(Player.DATA_FLAGS, Player.DATA_FLAG_ACTION, false); + break; - case ProtocolInfo.USE_ITEM_PACKET: - if (!this.spawned || !this.isAlive()) { + case ProtocolInfo.PLAYER_ACTION_PACKET: + PlayerActionPacket playerActionPacket = (PlayerActionPacket) packet; + if (!this.spawned || (!this.isAlive() && playerActionPacket.action != PlayerActionPacket.ACTION_RESPAWN && playerActionPacket.action != PlayerActionPacket.ACTION_DIMENSION_CHANGE_REQUEST)) { break; } - UseItemPacket useItemPacket = (UseItemPacket) packet; - - Vector3 blockVector = new Vector3(useItemPacket.x, useItemPacket.y, useItemPacket.z); - - this.craftingType = CRAFTING_SMALL; - - if (useItemPacket.face >= 0 && useItemPacket.face <= 5) { - BlockFace face = BlockFace.fromIndex(useItemPacket.face); - this.setDataFlag(Player.DATA_FLAGS, Player.DATA_FLAG_ACTION, false); + playerActionPacket.entityId = this.id; + Vector3 pos = new Vector3(playerActionPacket.x, playerActionPacket.y, playerActionPacket.z); + BlockFace face = BlockFace.fromIndex(playerActionPacket.face); - if (!this.canInteract(blockVector.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7)) { - } else if (this.isCreative()) { - Item i = this.inventory.getItemInHand(); - if (this.level.useItemOn(blockVector, i, face, useItemPacket.fx, useItemPacket.fy, useItemPacket.fz, this, true) != null) { + switch (playerActionPacket.action) { + case PlayerActionPacket.ACTION_START_BREAK: + if (this.lastBreak != Long.MAX_VALUE || pos.distanceSquared(this) > 100) { break; } - } else if (!this.inventory.getItemInHand().deepEquals(useItemPacket.item)) { - this.inventory.sendHeldItem(this); - } else { - item = this.inventory.getItemInHand(); - Item oldItem = item.clone(); - //TODO: Implement adventure mode checks - if ((item = this.level.useItemOn(blockVector, item, face, useItemPacket.fx, useItemPacket.fy, useItemPacket.fz, this, true)) != null) { - if (!item.deepEquals(oldItem) || item.getCount() != oldItem.getCount()) { - this.inventory.setItemInHand(item); - this.inventory.sendHeldItem(this.hasSpawned.values()); - } + Block target = this.level.getBlock(pos); + PlayerInteractEvent playerInteractEvent = new PlayerInteractEvent(this, this.inventory.getItemInHand(), target, face, target.getId() == 0 ? Action.LEFT_CLICK_AIR : Action.LEFT_CLICK_BLOCK); + this.getServer().getPluginManager().callEvent(playerInteractEvent); + if (playerInteractEvent.isCancelled()) { + this.inventory.sendHeldItem(this); break; } - } - - this.inventory.sendHeldItem(this); + if (target.getId() == Block.NOTEBLOCK) { + ((BlockNoteblock) target).emitSound(); + break; + } + Block block = target.getSide(face); + if (block.getId() == Block.FIRE) { + this.level.setBlock(block, new BlockAir(), true); + break; + } + if (!this.isCreative()) { + //improved this to take stuff like swimming, ladders, enchanted tools into account, fix wrong tool break time calculations for bad tools (pmmp/PocketMine-MP#211) + //Done by lmlstarqaq + double breakTime = Math.ceil(target.getBreakTime(this.inventory.getItemInHand(), this) * 20); + if (breakTime > 0) { + LevelEventPacket pk = new LevelEventPacket(); + pk.evid = LevelEventPacket.EVENT_BLOCK_START_BREAK; + pk.x = (float) pos.x; + pk.y = (float) pos.y; + pk.z = (float) pos.z; + pk.data = (int) (65535 / breakTime); + this.getLevel().addChunkPacket(pos.getFloorX() >> 4, pos.getFloorZ() >> 4, pk); + } + } - if (blockVector.distanceSquared(this) > 10000) { - break; - } - - Block target = this.level.getBlock(blockVector); - Block block = target.getSide(face); - - if (target instanceof BlockDoor) { - BlockDoor door = (BlockDoor) target; - - Block part; - - if ((door.getDamage() & 0x08) > 0) { //up - part = target.down(); - - if (part.getId() == target.getId()) { - target = part; - } - } - } - - this.level.sendBlocks(new Player[]{this}, new Block[]{target, block}, UpdateBlockPacket.FLAG_ALL_PRIORITY); - break; - } else if (useItemPacket.face == -1) { - Vector3 aimPos = new Vector3( - -Math.sin(this.yaw / 180d * Math.PI) * Math.cos(this.pitch / 180d * Math.PI), - -Math.sin(this.pitch / 180d * Math.PI), - Math.cos(this.yaw / 180d * Math.PI) * Math.cos(this.pitch / 180d * Math.PI) - ); - - if (this.isCreative()) { - item = this.inventory.getItemInHand(); - } else if (!this.inventory.getItemInHand().deepEquals(useItemPacket.item)) { - this.inventory.sendHeldItem(this); - break; - } else { - item = this.inventory.getItemInHand(); - } - - PlayerInteractEvent playerInteractEvent = new PlayerInteractEvent(this, item, aimPos, null, Action.RIGHT_CLICK_AIR); - - this.server.getPluginManager().callEvent(playerInteractEvent); - - if (playerInteractEvent.isCancelled()) { - this.inventory.sendHeldItem(this); - break; - } - - if (item.getId() == Item.SNOWBALL) { - CompoundTag nbt = new CompoundTag() - .putList(new ListTag("Pos") - .add(new DoubleTag("", x)) - .add(new DoubleTag("", y + this.getEyeHeight())) - .add(new DoubleTag("", z))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", -Math.sin(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI))) - .add(new DoubleTag("", -Math.sin(pitch / 180 * Math.PI))) - .add(new DoubleTag("", Math.cos(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI)))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", (float) yaw)) - .add(new FloatTag("", (float) pitch))); - - float f = 1.5f; - EntitySnowball snowball = new EntitySnowball(this.chunk, nbt, this); - - snowball.setMotion(snowball.getMotion().multiply(f)); - if (this.isSurvival()) { - item.setCount(item.getCount() - 1); - this.inventory.setItemInHand(item.getCount() > 0 ? item : Item.get(Item.AIR)); - } - if (snowball instanceof EntityProjectile) { - ProjectileLaunchEvent projectileLaunchEvent = new ProjectileLaunchEvent(snowball); - this.server.getPluginManager().callEvent(projectileLaunchEvent); - if (projectileLaunchEvent.isCancelled()) { - snowball.kill(); - } else { - snowball.spawnToAll(); - this.level.addSound(new LaunchSound(this), this.getViewers().values()); - } - } else { - snowball.spawnToAll(); - } - } else if (item.getId() == Item.EGG) { - CompoundTag nbt = new CompoundTag() - .putList(new ListTag("Pos") - .add(new DoubleTag("", x)) - .add(new DoubleTag("", y + this.getEyeHeight())) - .add(new DoubleTag("", z))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", -Math.sin(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI))) - .add(new DoubleTag("", -Math.sin(pitch / 180 * Math.PI))) - .add(new DoubleTag("", Math.cos(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI)))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", (float) yaw)) - .add(new FloatTag("", (float) pitch))); - - float f = 1.5f; - EntityEgg egg = new EntityEgg(this.chunk, nbt, this); - - egg.setMotion(egg.getMotion().multiply(f)); - if (this.isSurvival()) { - item.setCount(item.getCount() - 1); - this.inventory.setItemInHand(item.getCount() > 0 ? item : Item.get(Item.AIR)); - } - if (egg instanceof EntityProjectile) { - ProjectileLaunchEvent projectileLaunchEvent = new ProjectileLaunchEvent(egg); - this.server.getPluginManager().callEvent(projectileLaunchEvent); - if (projectileLaunchEvent.isCancelled()) { - egg.kill(); - } else { - egg.spawnToAll(); - this.level.addSound(new LaunchSound(this), this.getViewers().values()); - } - } else { - egg.spawnToAll(); - } - } else if (item.getId() == Item.ENDER_PEARL && (this.server.getTick() - this.lastEnderPearl) >= 20) { - CompoundTag nbt = new CompoundTag() - .putList(new ListTag("Pos") - .add(new DoubleTag("", x)) - .add(new DoubleTag("", y + this.getEyeHeight())) - .add(new DoubleTag("", z))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", -Math.sin(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI))) - .add(new DoubleTag("", -Math.sin(pitch / 180 * Math.PI))) - .add(new DoubleTag("", Math.cos(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI)))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", (float) yaw)) - .add(new FloatTag("", (float) pitch))); - - float f = 1.5f; - EntityEnderPearl enderPearl = new EntityEnderPearl(this.chunk, nbt, this); - - enderPearl.setMotion(enderPearl.getMotion().multiply(f)); - if (this.isSurvival()) { - item.setCount(item.getCount() - 1); - this.inventory.setItemInHand(item.getCount() > 0 ? item : Item.get(Item.AIR)); - } - if (enderPearl instanceof EntityProjectile) { - ProjectileLaunchEvent projectileLaunchEvent = new ProjectileLaunchEvent(enderPearl); - this.server.getPluginManager().callEvent(projectileLaunchEvent); - if (projectileLaunchEvent.isCancelled()) { - enderPearl.kill(); - } else { - enderPearl.spawnToAll(); - this.level.addSound(new LaunchSound(this), this.getViewers().values()); - } - } else { - enderPearl.spawnToAll(); - } - this.lastEnderPearl = this.server.getTick(); - } else if (item.getId() == Item.EXPERIENCE_BOTTLE) { - CompoundTag nbt = new CompoundTag() - .putList(new ListTag("Pos") - .add(new DoubleTag("", x)) - .add(new DoubleTag("", y + this.getEyeHeight())) - .add(new DoubleTag("", z))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", -Math.sin(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI))) - .add(new DoubleTag("", -Math.sin(pitch / 180 * Math.PI))) - .add(new DoubleTag("", Math.cos(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI)))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", (float) yaw)) - .add(new FloatTag("", (float) pitch))) - .putInt("Potion", item.getDamage()); - double f = 1.5; - Entity bottle = new EntityExpBottle(this.chunk, nbt, this); - bottle.setMotion(bottle.getMotion().multiply(f)); - if (this.isSurvival()) { - item.setCount(item.getCount() - 1); - this.inventory.setItemInHand(item.getCount() > 0 ? item : Item.get(Item.AIR)); - } - if (bottle instanceof EntityProjectile) { - EntityProjectile bottleEntity = (EntityProjectile) bottle; - ProjectileLaunchEvent projectileEv = new ProjectileLaunchEvent(bottleEntity); - this.server.getPluginManager().callEvent(projectileEv); - if (projectileEv.isCancelled()) { - bottle.kill(); - } else { - bottle.spawnToAll(); - this.level.addSound(new LaunchSound(this), this.getViewers().values()); - } - } else { - bottle.spawnToAll(); - } - } else if (item.getId() == Item.SPLASH_POTION) { - CompoundTag nbt = new CompoundTag() - .putList(new ListTag("Pos") - .add(new DoubleTag("", x)) - .add(new DoubleTag("", y + this.getEyeHeight())) - .add(new DoubleTag("", z))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", -Math.sin(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI))) - .add(new DoubleTag("", -Math.sin(pitch / 180 * Math.PI))) - .add(new DoubleTag("", Math.cos(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI)))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", (float) yaw)) - .add(new FloatTag("", (float) pitch))) - .putShort("PotionId", item.getDamage()); - double f = 1.5; - Entity bottle = new EntityPotion(this.chunk, nbt, this); - bottle.setMotion(bottle.getMotion().multiply(f)); - if (this.isSurvival()) { - item.setCount(item.getCount() - 1); - this.inventory.setItemInHand(item.getCount() > 0 ? item : Item.get(Item.AIR)); - } - if (bottle instanceof EntityPotion) { - EntityPotion bottleEntity = (EntityPotion) bottle; - ProjectileLaunchEvent projectileEv = new ProjectileLaunchEvent(bottleEntity); - this.server.getPluginManager().callEvent(projectileEv); - if (projectileEv.isCancelled()) { - bottle.kill(); - } else { - bottle.spawnToAll(); - this.level.addSound(new LaunchSound(this), this.getViewers().values()); - } - } else { - bottle.spawnToAll(); - } - } - - this.setDataFlag(Player.DATA_FLAGS, Player.DATA_FLAG_ACTION, true); - this.startAction = this.server.getTick(); - } - break; - case ProtocolInfo.PLAYER_ACTION_PACKET: - PlayerActionPacket playerActionPacket = (PlayerActionPacket) packet; - if (!this.spawned || (!this.isAlive() && playerActionPacket.action != PlayerActionPacket.ACTION_RESPAWN && playerActionPacket.action != PlayerActionPacket.ACTION_DIMENSION_CHANGE)) { - break; - } - - playerActionPacket.entityId = this.id; - Vector3 pos = new Vector3(playerActionPacket.x, playerActionPacket.y, playerActionPacket.z); - BlockFace face = BlockFace.fromIndex(playerActionPacket.face); - - switch (playerActionPacket.action) { - case PlayerActionPacket.ACTION_START_BREAK: - if (this.lastBreak != Long.MAX_VALUE || pos.distanceSquared(this) > 10000) { - break; - } - Block target = this.level.getBlock(pos); - PlayerInteractEvent playerInteractEvent = new PlayerInteractEvent(this, this.inventory.getItemInHand(), target, face, target.getId() == 0 ? Action.LEFT_CLICK_AIR : Action.LEFT_CLICK_BLOCK); - this.getServer().getPluginManager().callEvent(playerInteractEvent); - if (playerInteractEvent.isCancelled()) { - this.inventory.sendHeldItem(this); - break; - } - if (target.getId() == Block.NOTEBLOCK) { - ((BlockNoteblock)target).emitSound(); - break; - } - Block block = target.getSide(face); - if (block.getId() == Block.FIRE) { - this.level.setBlock(block, new BlockAir(), true); - break; - } - if(!this.isCreative()){ - //improved this to take stuff like swimming, ladders, enchanted tools into account, fix wrong tool break time calculations for bad tools (pmmp/PocketMine-MP#211) - //Done by lmlstarqaq - double breakTime = Math.ceil(target.getBreakTime(this.inventory.getItemInHand(), this) * 20); - if (breakTime > 0) { - LevelEventPacket pk = new LevelEventPacket(); - pk.evid = LevelEventPacket.EVENT_BLOCK_START_BREAK; - pk.x = (float)pos.x; - pk.y = (float)pos.y; - pk.z = (float)pos.z; - pk.data = (int)(65535 / breakTime); - this.getLevel().addChunkPacket(pos.getFloorX() >> 4, pos.getFloorZ() >> 4, pk); - } - } - this.lastBreak = System.currentTimeMillis(); + this.breakingBlock = target; + this.lastBreak = System.currentTimeMillis(); break; case PlayerActionPacket.ACTION_ABORT_BREAK: this.lastBreak = Long.MAX_VALUE; - + this.breakingBlock = null; case PlayerActionPacket.ACTION_STOP_BREAK: LevelEventPacket pk = new LevelEventPacket(); pk.evid = LevelEventPacket.EVENT_BLOCK_STOP_BREAK; - pk.x = (float)pos.x; - pk.y = (float)pos.y; - pk.z = (float)pos.z; + pk.x = (float) pos.x; + pk.y = (float) pos.y; + pk.z = (float) pos.z; pk.data = 0; this.getLevel().addChunkPacket(pos.getFloorX() >> 4, pos.getFloorZ() >> 4, pk); + this.breakingBlock = null; break; - - case PlayerActionPacket.ACTION_RELEASE_ITEM: - if (this.startAction > -1 && this.getDataFlag(Player.DATA_FLAGS, Player.DATA_FLAG_ACTION)) { - if (this.inventory.getItemInHand().getId() == Item.BOW) { - - Item bow = this.inventory.getItemInHand(); - ItemArrow itemArrow = new ItemArrow(); - if (this.isSurvival() && !this.inventory.contains(itemArrow)) { - this.inventory.sendContents(this); - break; - } - - double damage = 2; - boolean flame = false; - - if (bow.hasEnchantments()) { - Enchantment bowDamage = bow.getEnchantment(Enchantment.ID_BOW_POWER); - - if (bowDamage != null && bowDamage.getLevel() > 0) { - damage += 0.25 * (bowDamage.getLevel() + 1); - } - - Enchantment flameEnchant = bow.getEnchantment(Enchantment.ID_BOW_FLAME); - flame = flameEnchant != null && flameEnchant.getLevel() > 0; - } - - CompoundTag nbt = new CompoundTag() - .putList(new ListTag("Pos") - .add(new DoubleTag("", x)) - .add(new DoubleTag("", y + this.getEyeHeight())) - .add(new DoubleTag("", z))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", -Math.sin(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI))) - .add(new DoubleTag("", -Math.sin(pitch / 180 * Math.PI))) - .add(new DoubleTag("", Math.cos(yaw / 180 * Math.PI) * Math.cos(pitch / 180 * Math.PI)))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", (yaw > 180 ? 360 : 0) - (float) yaw)) - .add(new FloatTag("", (float) -pitch))) - .putShort("Fire", this.isOnFire() || flame ? 45 * 60 : 0) - .putDouble("damage", damage); - - int diff = (this.server.getTick() - this.startAction); - double p = (double) diff / 20; - - double f = Math.min((p * p + p * 2) / 3, 1) * 2; - EntityShootBowEvent entityShootBowEvent = new EntityShootBowEvent(this, bow, new EntityArrow(this.chunk, nbt, this, f == 2), f); - - if (f < 0.1 || diff < 5) { - entityShootBowEvent.setCancelled(); - } - - this.server.getPluginManager().callEvent(entityShootBowEvent); - if (entityShootBowEvent.isCancelled()) { - entityShootBowEvent.getProjectile().kill(); - this.inventory.sendContents(this); - } else { - entityShootBowEvent.getProjectile().setMotion(entityShootBowEvent.getProjectile().getMotion().multiply(entityShootBowEvent.getForce())); - if (this.isSurvival()) { - Enchantment infinity; - - if (!bow.hasEnchantments() || (infinity = bow.getEnchantment(Enchantment.ID_BOW_INFINITY)) == null || infinity.getLevel() <= 0) - this.inventory.removeItem(itemArrow); - - if (!bow.isUnbreakable()) { - Enchantment durability = bow.getEnchantment(Enchantment.ID_DURABILITY); - if (!(durability != null && durability.getLevel() > 0 && (100 / (durability.getLevel() + 1)) <= new Random().nextInt(100))) { - bow.setDamage(bow.getDamage() + 1); - if (bow.getDamage() >= 385) { - this.inventory.setItemInHand(new ItemBlock(new BlockAir(), 0, 0)); - } else { - this.inventory.setItemInHand(bow); - } - } - } - } - if (entityShootBowEvent.getProjectile() instanceof EntityProjectile) { - ProjectileLaunchEvent projectev = new ProjectileLaunchEvent(entityShootBowEvent.getProjectile()); - this.server.getPluginManager().callEvent(projectev); - if (projectev.isCancelled()) { - entityShootBowEvent.getProjectile().kill(); - } else { - entityShootBowEvent.getProjectile().spawnToAll(); - this.level.addSound(new LaunchSound(this), this.getViewers().values()); - } - } else { - entityShootBowEvent.getProjectile().spawnToAll(); - } - } - } - } - //milk removed here, see the section of food - + case PlayerActionPacket.ACTION_GET_UPDATED_BLOCK: + break; //TODO + case PlayerActionPacket.ACTION_DROP_ITEM: + break; //TODO case PlayerActionPacket.ACTION_STOP_SLEEPING: this.stopSleep(); break; - case PlayerActionPacket.ACTION_RESPAWN: if (!this.spawned || this.isAlive() || !this.isOnline()) { break; @@ -2631,6 +2284,7 @@ public void handleDataPacket(DataPacket packet) { } this.craftingType = CRAFTING_SMALL; + this.resetCraftingGridType(); PlayerRespawnEvent playerRespawnEvent = new PlayerRespawnEvent(this, this.getSpawn()); this.server.getPluginManager().callEvent(playerRespawnEvent); @@ -2668,10 +2322,8 @@ public void handleDataPacket(DataPacket packet) { this.spawnToAll(); this.scheduleUpdate(); break; - case PlayerActionPacket.ACTION_JUMP: break packetswitch; - case PlayerActionPacket.ACTION_START_SPRINT: PlayerToggleSprintEvent playerToggleSprintEvent = new PlayerToggleSprintEvent(this, true); this.server.getPluginManager().callEvent(playerToggleSprintEvent); @@ -2681,7 +2333,6 @@ public void handleDataPacket(DataPacket packet) { this.setSprinting(true); } break packetswitch; - case PlayerActionPacket.ACTION_STOP_SPRINT: playerToggleSprintEvent = new PlayerToggleSprintEvent(this, false); this.server.getPluginManager().callEvent(playerToggleSprintEvent); @@ -2691,7 +2342,6 @@ public void handleDataPacket(DataPacket packet) { this.setSprinting(false); } break packetswitch; - case PlayerActionPacket.ACTION_START_SNEAK: PlayerToggleSneakEvent playerToggleSneakEvent = new PlayerToggleSneakEvent(this, true); this.server.getPluginManager().callEvent(playerToggleSneakEvent); @@ -2701,7 +2351,6 @@ public void handleDataPacket(DataPacket packet) { this.setSneaking(true); } break packetswitch; - case PlayerActionPacket.ACTION_STOP_SNEAK: playerToggleSneakEvent = new PlayerToggleSneakEvent(this, false); this.server.getPluginManager().callEvent(playerToggleSneakEvent); @@ -2711,7 +2360,8 @@ public void handleDataPacket(DataPacket packet) { this.setSneaking(false); } break packetswitch; - + case PlayerActionPacket.ACTION_DIMENSION_CHANGE_ACK: + break; //TODO case PlayerActionPacket.ACTION_START_GLIDE: PlayerToggleGlideEvent playerToggleGlideEvent = new PlayerToggleGlideEvent(this, true); this.server.getPluginManager().callEvent(playerToggleGlideEvent); @@ -2721,7 +2371,6 @@ public void handleDataPacket(DataPacket packet) { this.setGliding(true); } break packetswitch; - case PlayerActionPacket.ACTION_STOP_GLIDE: playerToggleGlideEvent = new PlayerToggleGlideEvent(this, false); this.server.getPluginManager().callEvent(playerToggleGlideEvent); @@ -2731,58 +2380,47 @@ public void handleDataPacket(DataPacket packet) { this.setGliding(false); } break packetswitch; - case PlayerActionPacket.ACTION_WORLD_IMMUTABLE: - break; //TODO case PlayerActionPacket.ACTION_CONTINUE_BREAK: - block = this.level.getBlock(pos); - this.level.addParticle(new PunchBlockParticle(pos, block, face)); + if (this.isBreakingBlock()) { + block = this.level.getBlock(pos); + this.level.addParticle(new PunchBlockParticle(pos, block, face)); + } break; } this.startAction = -1; this.setDataFlag(Player.DATA_FLAGS, Player.DATA_FLAG_ACTION, false); break; - case ProtocolInfo.REMOVE_BLOCK_PACKET: + case ProtocolInfo.MOB_ARMOR_EQUIPMENT_PACKET: + break; + + case ProtocolInfo.MODAL_FORM_RESPONSE_PACKET: if (!this.spawned || !this.isAlive()) { break; } - this.craftingType = CRAFTING_SMALL; - - Vector3 vector = new Vector3(((RemoveBlockPacket) packet).x, ((RemoveBlockPacket) packet).y, ((RemoveBlockPacket) packet).z); - - if (this.isCreative()) { - item = this.inventory.getItemInHand(); - } else { - item = this.inventory.getItemInHand(); - } - Item oldItem = item.clone(); + ModalFormResponsePacket modalFormPacket = (ModalFormResponsePacket) packet; - if (this.canInteract(vector.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7) && (item = this.level.useBreakOn(vector, item, this, true)) != null) { - if (this.isSurvival()) { - this.getFoodData().updateFoodExpLevel(0.025); - if (!item.deepEquals(oldItem) || item.getCount() != oldItem.getCount()) { - this.inventory.setItemInHand(item); - this.inventory.sendHeldItem(this.hasSpawned.values()); - } - } - break; - } + if (formWindows.containsKey(modalFormPacket.formId)) { + FormWindow window = formWindows.get(modalFormPacket.formId); + window.setResponse(modalFormPacket.data.trim()); - this.inventory.sendContents(this); - Block target = this.level.getBlock(vector); - BlockEntity blockEntity = this.level.getBlockEntity(vector); + PlayerFormRespondedEvent event = new PlayerFormRespondedEvent(this, modalFormPacket.formId, window); + getServer().getPluginManager().callEvent(event); - this.level.sendBlocks(new Player[]{this}, new Block[]{target}, UpdateBlockPacket.FLAG_ALL_PRIORITY); + formWindows.remove(modalFormPacket.formId); + } else if (serverSettings.containsKey(modalFormPacket.formId)) { + FormWindow window = serverSettings.get(modalFormPacket.formId); + window.setResponse(modalFormPacket.data.trim()); - this.inventory.sendHeldItem(this); + PlayerSettingsRespondedEvent event = new PlayerSettingsRespondedEvent(this, modalFormPacket.formId, window); + getServer().getPluginManager().callEvent(event); - if (blockEntity instanceof BlockEntitySpawnable) { - ((BlockEntitySpawnable) blockEntity).spawnTo(this); + //Set back new settings if not been cancelled + if (!event.isCancelled() && window instanceof FormWindowCustom) + ((FormWindowCustom) window).setElementsFromResponse(); } - break; - case ProtocolInfo.MOB_ARMOR_EQUIPMENT_PACKET: break; case ProtocolInfo.INTERACT_PACKET: @@ -2790,18 +2428,19 @@ public void handleDataPacket(DataPacket packet) { break; } + this.craftingType = CRAFTING_SMALL; + this.resetCraftingGridType(); + InteractPacket interactPacket = (InteractPacket) packet; - + Entity targetEntity = this.level.getEntity(interactPacket.target); if (targetEntity == null || !this.isAlive() || !targetEntity.isAlive()) { break; } - this.craftingType = CRAFTING_SMALL; - if (targetEntity instanceof EntityItem || targetEntity instanceof EntityArrow || targetEntity instanceof EntityXPOrb) { - this.kick(PlayerKickEvent.Reason.INVALID_PVE, "Attempting to attack an invalid entity"); + this.kick(PlayerKickEvent.Reason.INVALID_PVE, "Attempting to interact with an invalid entity"); this.server.getLogger().warning(this.getServer().getLanguage().translateString("nukkit.player.invalidEntity", this.getName())); break; } @@ -2812,106 +2451,25 @@ public void handleDataPacket(DataPacket packet) { case InteractPacket.ACTION_MOUSEOVER: this.getServer().getPluginManager().callEvent(new PlayerMouseOverEntityEvent(this, targetEntity)); break; - case InteractPacket.ACTION_LEFT_CLICK: - if (this.getGamemode() == Player.VIEW) { - break; - } - - float itemDamage = item.getAttackDamage(); - - for (Enchantment enchantment : item.getEnchantments()) { - itemDamage += enchantment.getDamageBonus(targetEntity); - } - - Map damage = new EnumMap<>(DamageModifier.class); - damage.put(DamageModifier.BASE, itemDamage); - - if (!this.canInteract(targetEntity, isCreative() ? 8 : 5)) { - break; - } else if (targetEntity instanceof Player) { - if ((((Player) targetEntity).getGamemode() & 0x01) > 0) { - break; - } else if (!this.server.getPropertyBoolean("pvp") || this.server.getDifficulty() == 0) { - break; - } - } - - if (!targetEntity.attack(new EntityDamageByEntityEvent(this, targetEntity, DamageCause.ENTITY_ATTACK, damage))) { - if (item.isTool() && this.isSurvival()) { - this.inventory.sendContents(this); - } - break; - } - - for (Enchantment enchantment : item.getEnchantments()) { - enchantment.doPostAttack(this, targetEntity); - } - - if (item.isTool() && this.isSurvival()) { - if (item.useOn(targetEntity) && item.getDamage() >= item.getMaxDurability()) { - this.inventory.setItemInHand(new ItemBlock(new BlockAir())); - } else { - this.inventory.setItemInHand(item); - } - } - - break; - case InteractPacket.ACTION_RIGHT_CLICK: - PlayerInteractEntityEvent playerInteractEntityEvent = new PlayerInteractEntityEvent(this, targetEntity, item); - getServer().getPluginManager().callEvent(playerInteractEntityEvent); - - if (playerInteractEntityEvent.isCancelled()) { - break; - } - - if (targetEntity.onInteract(this, item) && this.isSurvival()) { - if (item.isTool()) { - if (item.useOn(targetEntity) && item.getDamage() >= item.getMaxDurability()) { - item = new ItemBlock(new BlockAir()); - } - } else { - if (item.count > 1) { - item.count--; - } else { - item = new ItemBlock(new BlockAir()); - } - } - - this.inventory.setItemInHand(item); - } - break; case InteractPacket.ACTION_VEHICLE_EXIT: - if (!(targetEntity instanceof EntityVehicle)) { + if (!(targetEntity instanceof EntityRideable) || this.riding == null) { break; } - SetEntityLinkPacket pk; - - pk = new SetEntityLinkPacket(); - pk.rider = targetEntity.getId(); - pk.riding = this.id; - pk.type = 3; - Server.broadcastPacket(this.hasSpawned.values(), pk); - - pk = new SetEntityLinkPacket(); - pk.rider = targetEntity.getId(); - pk.riding = this.getId(); - pk.type = 3; - dataPacket(pk); - - riding = null; - ((EntityVehicle) targetEntity).linkedEntity = null; - this.setDataFlag(DATA_FLAGS, DATA_FLAG_RIDING, false); + ((EntityRideable) riding).mountEntity(this); break; } break; case ProtocolInfo.BLOCK_PICK_REQUEST_PACKET: BlockPickRequestPacket pickRequestPacket = (BlockPickRequestPacket) packet; - if (this.isCreative()) { - BlockEntity be = this.getLevel().getBlockEntity(new Vector3(pickRequestPacket.x, pickRequestPacket.y, pickRequestPacket.z)); - if (be != null) { - CompoundTag nbt = be.getCleanedNBT(); - if(nbt != null){ + Block block = this.level.getBlock(this.temporalVector.setComponents(pickRequestPacket.x, pickRequestPacket.y, pickRequestPacket.z)); + item = block.toItem(); + + if (pickRequestPacket.addUserData) { + BlockEntity blockEntity = this.getLevel().getBlockEntity(new Vector3(pickRequestPacket.x, pickRequestPacket.y, pickRequestPacket.z)); + if (blockEntity != null) { + CompoundTag nbt = blockEntity.getCleanedNBT(); + if (nbt != null) { Item item1 = this.getInventory().getItemInHand(); item1.setCustomBlockData(nbt); item1.setLore("+(DATA)"); @@ -2919,6 +2477,18 @@ public void handleDataPacket(DataPacket packet) { } } } + + PlayerBlockPickEvent pickEvent = new PlayerBlockPickEvent(this, block, item); + if (!this.isCreative()) { + this.server.getLogger().debug("Got block-pick request from " + this.getName() + " when not in creative mode (gamemode " + this.getGamemode() + ")"); + pickEvent.setCancelled(); + } + + this.server.getPluginManager().callEvent(pickEvent); + + if (!pickEvent.isCancelled()) { + this.inventory.setItemInHand(pickEvent.getItem()); + } break; case ProtocolInfo.ANIMATE_PACKET: if (!this.spawned || !this.isAlive()) { @@ -2945,126 +2515,28 @@ public void handleDataPacket(DataPacket packet) { break; } this.craftingType = CRAFTING_SMALL; + this.resetCraftingGridType(); - this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false); //TODO: check if this should be true EntityEventPacket entityEventPacket = (EntityEventPacket) packet; switch (entityEventPacket.event) { - case EntityEventPacket.USE_ITEM: //Eating - Item itemInHand = this.inventory.getItemInHand(); - PlayerItemConsumeEvent consumeEvent = new PlayerItemConsumeEvent(this, itemInHand); - this.server.getPluginManager().callEvent(consumeEvent); - if (consumeEvent.isCancelled()) { - this.inventory.sendContents(this); + case EntityEventPacket.EATING_ITEM: + if (entityEventPacket.data == 0) { break; } - if (itemInHand.getId() == Item.POTION) { - Potion potion = Potion.getPotion(itemInHand.getDamage()).setSplash(false); - - if (this.getGamemode() == SURVIVAL) { - if (itemInHand.getCount() > 1) { - ItemGlassBottle bottle = new ItemGlassBottle(); - if (this.inventory.canAddItem(bottle)) { - this.inventory.addItem(bottle); - } - --itemInHand.count; - } else { - itemInHand = new ItemGlassBottle(); - } - } - - if (potion != null) { - potion.applyPotion(this); - } - - } else { - EntityEventPacket pk = new EntityEventPacket(); - pk.eid = this.getId(); - pk.event = EntityEventPacket.USE_ITEM; - this.dataPacket(pk); - Server.broadcastPacket(this.getViewers().values(), pk); - - Food food = Food.getByRelative(itemInHand); - if (food != null) if (food.eatenBy(this)) --itemInHand.count; - - } - - this.inventory.setItemInHand(itemInHand); - this.inventory.sendHeldItem(this); - + /*this.dataPacket(packet); //bug? + Server.broadcastPacket(this.getViewers().values(), packet);*/ break; } break; - case ProtocolInfo.DROP_ITEM_PACKET: - if (!this.spawned || !this.isAlive()) { - break; - } - DropItemPacket dropItem = (DropItemPacket) packet; - - if (dropItem.item.getId() <= 0) { - break; - } - - item = (this.isCreative() || this.inventory.contains(dropItem.item)) ? dropItem.item : this.inventory.getItemInHand(); - PlayerDropItemEvent dropItemEvent = new PlayerDropItemEvent(this, item); - this.server.getPluginManager().callEvent(dropItemEvent); - if (dropItemEvent.isCancelled()) { - this.inventory.sendContents(this); - break; - } - - if (!this.isCreative()) { - this.inventory.removeItem(item); - } - Vector3 motion = this.getDirectionVector().multiply(0.4); - - this.level.dropItem(this.add(0, 1.3, 0), item, motion, 40); - - this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false); - break; - case ProtocolInfo.COMMAND_STEP_PACKET: + case ProtocolInfo.COMMAND_REQUEST_PACKET: if (!this.spawned || !this.isAlive()) { break; } this.craftingType = 0; - CommandStepPacket commandStepPacket = (CommandStepPacket) packet; - String commandText = commandStepPacket.command; - Command command = this.getServer().getCommandMap().getCommand(commandText); - if (command != null) { - if (commandStepPacket.args != null && commandStepPacket.args.size() > 0) { - CommandParameter[] pars = command.getCommandParameters(commandStepPacket.overload); - if (pars != null) { - for (CommandParameter par : pars) { - JsonElement arg = commandStepPacket.args.get(par.name); - if (arg != null) { - switch (par.type) { - case CommandParameter.ARG_TYPE_TARGET: - CommandArg rules = new Gson().fromJson(arg, CommandArg.class); - commandText += " " + rules.getRules()[0].getValue(); - break; - case CommandParameter.ARG_TYPE_BLOCK_POS: - CommandArgBlockVector bv = new Gson().fromJson(arg, CommandArgBlockVector.class); - commandText += " " + bv.getX() + " " + bv.getY() + " " + bv.getZ(); - break; - case CommandParameter.ARG_TYPE_STRING: - case CommandParameter.ARG_TYPE_STRING_ENUM: - case CommandParameter.ARG_TYPE_RAW_TEXT: - String string = new Gson().fromJson(arg, String.class); - commandText += " " + string; - break; - default: - commandText += " " + arg.toString(); - break; - } - } - } - } else { - this.sendMessage(this.getServer().getLanguage().translateString(command.getUsage())); - } - } - } - PlayerCommandPreprocessEvent playerCommandPreprocessEvent = new PlayerCommandPreprocessEvent(this, "/" + commandText); + CommandRequestPacket commandRequestPacket = (CommandRequestPacket) packet; + PlayerCommandPreprocessEvent playerCommandPreprocessEvent = new PlayerCommandPreprocessEvent(this, commandRequestPacket.command); this.server.getPluginManager().callEvent(playerCommandPreprocessEvent); if (playerCommandPreprocessEvent.isCancelled()) { break; @@ -3079,34 +2551,25 @@ public void handleDataPacket(DataPacket packet) { break; } - this.craftingType = CRAFTING_SMALL; TextPacket textPacket = (TextPacket) packet; if (textPacket.type == TextPacket.TYPE_CHAT) { - textPacket.message = this.removeFormat ? TextFormat.clean(textPacket.message) : textPacket.message; - for (String msg : textPacket.message.split("\n")) { - if (!"".equals(msg.trim()) && msg.length() <= 255 && this.messageCounter-- > 0) { - PlayerChatEvent chatEvent = new PlayerChatEvent(this, msg); - this.server.getPluginManager().callEvent(chatEvent); - if (!chatEvent.isCancelled()) { - this.server.broadcastMessage(this.getServer().getLanguage().translateString(chatEvent.getFormat(), new String[]{chatEvent.getPlayer().getDisplayName(), chatEvent.getMessage()}), chatEvent.getRecipients()); - } - } - } + this.chat(textPacket.message); } break; case ProtocolInfo.CONTAINER_CLOSE_PACKET: ContainerClosePacket containerClosePacket = (ContainerClosePacket) packet; - if (!this.spawned || containerClosePacket.windowid == 0) { + if (!this.spawned || containerClosePacket.windowId == 0) { break; } this.craftingType = CRAFTING_SMALL; - this.currentTransaction = null; - if (this.windowIndex.containsKey(containerClosePacket.windowid)) { - this.server.getPluginManager().callEvent(new InventoryCloseEvent(this.windowIndex.get(containerClosePacket.windowid), this)); - this.removeWindow(this.windowIndex.get(containerClosePacket.windowid)); + this.resetCraftingGridType(); + + if (this.windowIndex.containsKey(containerClosePacket.windowId)) { + this.server.getPluginManager().callEvent(new InventoryCloseEvent(this.windowIndex.get(containerClosePacket.windowId), this)); + this.removeWindow(this.windowIndex.get(containerClosePacket.windowId)); } else { - this.windowIndex.remove(containerClosePacket.windowid); + this.windowIndex.remove(containerClosePacket.windowId); } break; @@ -3118,48 +2581,26 @@ public void handleDataPacket(DataPacket packet) { } Recipe recipe = this.server.getCraftingManager().getRecipe(craftingEventPacket.id); - - if (this.craftingType == CRAFTING_ANVIL) { - Inventory inv = this.windowIndex.get(craftingEventPacket.windowId); - AnvilInventory anvilInventory = inv instanceof AnvilInventory ? (AnvilInventory) inv : null; - - if (anvilInventory == null) { - anvilInventory = null; - - for (Inventory window : this.windowIndex.values()) { - if (window instanceof AnvilInventory) { - anvilInventory = (AnvilInventory) window; - break; - } - } - - if (anvilInventory == null) { //If it'sf _still_ null, then the player doesn't have a valid anvil window, cannot proceed. - this.getServer().getLogger().debug("Couldn't find an anvil window for " + this.getName() + ", exiting"); - this.inventory.sendContents(this); - break; - } + Recipe[] recipes = this.server.getCraftingManager().getRecipesByResult(craftingEventPacket.output[0]); + + boolean isValid = false; + for (Recipe rec : recipes){ + if (rec.getId().equals(recipe.getId())) { + isValid = true; + break; } + } + if (isValid) recipes = new Recipe[]{recipe}; - if (recipe == null) { - //Item renamed - - if (!anvilInventory.onRename(this, craftingEventPacket.output[0])) { - this.getServer().getLogger().debug(this.getName() + " failed to rename an item in an anvil"); - this.inventory.sendContents(this); - } - } else { - //TODO: Anvil crafting recipes - } - break; - } else if (!this.windowIndex.containsKey(craftingEventPacket.windowId)) { + if (!this.windowIndex.containsKey(craftingEventPacket.windowId)) { this.inventory.sendContents(this); containerClosePacket = new ContainerClosePacket(); - containerClosePacket.windowid = craftingEventPacket.windowId; + containerClosePacket.windowId = craftingEventPacket.windowId; this.dataPacket(containerClosePacket); break; } - - if ((recipe == null) || (((recipe instanceof BigShapelessRecipe) || (recipe instanceof BigShapedRecipe)) && this.craftingType == CRAFTING_SMALL)) { + + if (isValid && (recipe == null || (((recipe instanceof BigShapelessRecipe) || (recipe instanceof BigShapedRecipe)) && this.craftingType == CRAFTING_SMALL))) { this.inventory.sendContents(this); break; } @@ -3176,253 +2617,87 @@ public void handleDataPacket(DataPacket packet) { } boolean canCraft = true; - - if (craftingEventPacket.input.length == 0) { - Recipe[] recipes = getServer().getCraftingManager().getRecipesByResult(craftingEventPacket.output[0]); - - recipe = null; - + Map realSerialized = new HashMap<>(); + + for (Recipe rec : recipes) { ArrayList ingredientz = new ArrayList<>(); - - for (Recipe r : recipes) { - if (r instanceof ShapedRecipe) { - Map> ingredients = ((ShapedRecipe) r).getIngredientMap(); - for (Map map : ingredients.values()) { - for (Item ingredient : map.values()) { - if (ingredient != null && ingredient.getId() != Item.AIR) { - if (!this.inventory.contains(ingredient)) { - canCraft = false; - break; - } - - ingredientz.add(ingredient); - this.inventory.removeItem(ingredient); - } - } - } - - if (canCraft) { - recipe = r; - break; - } - } + + if (rec == null || (((rec instanceof BigShapelessRecipe) || (rec instanceof BigShapedRecipe)) && this.craftingType == CRAFTING_SMALL)) { + continue; } - if (recipe != null) { - CraftItemEvent craftItemEvent = new CraftItemEvent(this, ingredientz.stream().toArray(Item[]::new), recipe); - getServer().getPluginManager().callEvent(craftItemEvent); - - if (craftItemEvent.isCancelled()) { - this.inventory.sendContents(this); - break; - } - - this.inventory.addItem(recipe.getResult()); - } else { - this.server.getLogger().debug("(1) Unmatched desktop recipe " + craftingEventPacket.id + " from player " + this.getName()); - this.inventory.sendContents(this); - } - } else { - - if (recipe instanceof ShapedRecipe) { - int offsetX = 0; - int offsetY = 0; - - if (this.craftingType == CRAFTING_BIG) { - int minX = -1, minY = -1, maxX = 0, maxY = 0; - for (int x = 0; x < 3 && canCraft; ++x) { - for (int y = 0; y < 3; ++y) { - Item readItem = craftingEventPacket.input[y * 3 + x]; - if (readItem.getId() != Item.AIR) { - if (minY == -1 || minY > y) { - minY = y; - } - if (maxY < y) { - maxY = y; - } - if (minX == -1) { - minX = x; - } - if (maxX < x) { - maxX = x; - } - } + if (rec instanceof ShapedRecipe) { + Map> ingredients = ((ShapedRecipe) rec).getIngredientMap(); + + for (Map map : ingredients.values()) { + for (Item ingredient : map.values()) { + if (ingredient != null && ingredient.getId() != Item.AIR) { + ingredientz.add(ingredient); } } - if (maxX == minX) { - offsetX = minX; - } - if (maxY == minY) { - offsetY = minY; - } } - - //To fix some items can't craft - for (int x = 0; x < 3 - offsetX && canCraft; ++x) { - for (int y = 0; y < 3 - offsetY; ++y) { - item = craftingEventPacket.input[(y + offsetY) * 3 + (x + offsetX)]; - Item ingredient = ((ShapedRecipe) recipe).getIngredient(x, y); - //todo: check this https://github.com/PocketMine/PocketMine-MP/commit/58709293cf4eee2e836a94226bbba4aca0f53908 - if (item.getCount() > 0) { - if (ingredient == null || !ingredient.deepEquals(item, ingredient.hasMeta(), ingredient.getCompoundTag() != null)) { - canCraft = false; - break; - } - - } - } - } - - //If can't craft by auto resize, will try to craft this item in another way - if (!canCraft) { - canCraft = true; - for (int x = 0; x < 3 && canCraft; ++x) { - for (int y = 0; y < 3; ++y) { - item = craftingEventPacket.input[y * 3 + x]; - Item ingredient = ((ShapedRecipe) recipe).getIngredient(x, y); - if (item.getCount() > 0) { - if (ingredient == null || !ingredient.deepEquals(item, ingredient.hasMeta(), ingredient.getCompoundTag() != null)) { - canCraft = false; - break; - } - - } - } - } - } - } else if (recipe instanceof ShapelessRecipe) { - List needed = ((ShapelessRecipe) recipe).getIngredientList(); + ShapelessRecipe recipe0 = (ShapelessRecipe) recipe; - for (int x = 0; x < 3 && canCraft; ++x) { - for (int y = 0; y < 3; ++y) { - item = craftingEventPacket.input[y * 3 + x].clone(); - - for (Item n : new ArrayList<>(needed)) { - if (n.deepEquals(item, n.hasMeta(), n.getCompoundTag() != null)) { - int remove = Math.min(n.getCount(), item.getCount()); - n.setCount(n.getCount() - remove); - item.setCount(item.getCount() - remove); - - if (n.getCount() == 0) { - needed.remove(n); - } - } - } - - if (item.getCount() > 0) { - canCraft = false; - break; - } + for (Item ingredient : recipe0.getIngredientList()) { + if (ingredient != null && ingredient.getId() != Item.AIR) { + ingredientz.add(ingredient); } } - - if (!needed.isEmpty()) { - canCraft = false; - } - } else { - canCraft = false; } - List ingredientsList = new ArrayList<>(); - if (recipe instanceof ShapedRecipe) { - for (int x = 0; x < 3; x++) { - for (int y = 0; y < 3; y++) { - Item need = ((ShapedRecipe) recipe).getIngredient(x, y); - if (need.getId() == 0) { - continue; - } - for (int count = need.getCount(); count > 0; count--) { - Item needAdd = need.clone(); - //todo: check if there need to set item's count to 1, I'm too tired to check that today =w= - needAdd.setCount(1); - ingredientsList.add(needAdd); - } - } - } - } - if (recipe instanceof ShapelessRecipe) { - List recipeItem = ((ShapelessRecipe) recipe).getIngredientList(); - for (Item need : recipeItem) { - if (need.getId() == 0) { - continue; - } - Item needAdd = need.clone(); - //todo: check if there need to set item's count to 1, I'm too tired to check that today =w= - needAdd.setCount(1); - ingredientsList.add(needAdd); - } - } + Map serialized = new HashMap<>(); - Item[] ingredients = ingredientsList.stream().toArray(Item[]::new); + for (Item ingredient : ingredientz) { + String hash = ingredient.getId() + ":" + ingredient.getDamage(); + Item r = serialized.get(hash); - Item result = craftingEventPacket.output[0]; + if (r != null) { + r.count += ingredient.getCount(); + continue; + } - if (!canCraft || !recipe.getResult().deepEquals(result)) { - this.server.getLogger().debug("(2) Unmatched recipe " + recipe.getId() + " from player " + this.getName() + ": expected " + recipe.getResult() + ", got " + result + ", using: " + Arrays.asList(ingredients).toString()); - this.inventory.sendContents(this); - break; + serialized.put(hash, ingredient); } - int[] used = new int[this.inventory.getSize()]; - - for (Item ingredient : ingredients) { - slot = -1; - for (int index : this.inventory.getContents().keySet()) { - Item i = this.inventory.getContents().get(index); - if (ingredient.getId() != 0 && ingredient.deepEquals(i, ingredient.hasMeta()) && (i.getCount() - used[index]) >= 1) { - slot = index; - used[index]++; + boolean isPossible = true; + for (Item ingredient : serialized.values()) { + if (!this.craftingGrid.contains(ingredient)) { + if (isValid) { + canCraft = false; + break; + } + else { + isPossible = false; break; } } - - if (ingredient.getId() != 0 && slot == -1) { - canCraft = false; - break; - } - } - - if (!canCraft) { - this.server.getLogger().debug("(3) Unmatched recipe " + recipe.getId() + " from player " + this.getName() + ": client does not have enough items, using: " + Arrays.asList(ingredients).toString()); - this.inventory.sendContents(this); - break; } - CraftItemEvent craftItemEvent; - this.server.getPluginManager().callEvent(craftItemEvent = new CraftItemEvent(this, ingredients, recipe)); - - if (craftItemEvent.isCancelled()) { - this.inventory.sendContents(this); - break; - } - - for (int i = 0; i < used.length; i++) { - int count = used[i]; - if (count == 0) { - continue; - } + if (!isPossible) continue; + recipe = rec; + realSerialized = serialized; + break; + } - item = this.inventory.getItem(i); + if (!canCraft) { + this.server.getLogger().debug("(1) Unmatched recipe " + craftingEventPacket.id + " from player " + this.getName() + " not anough ingredients"); + return; + } - Item newItem; - if (item.getCount() > count) { - newItem = item.clone(); - newItem.setCount(item.getCount() - count); - } else { - newItem = new ItemBlock(new BlockAir(), 0, 0); - } + CraftItemEvent craftItemEvent = new CraftItemEvent(this, realSerialized.values().stream().toArray(Item[]::new), recipe); + getServer().getPluginManager().callEvent(craftItemEvent); - this.inventory.setItem(i, newItem); - } + if (craftItemEvent.isCancelled()) { + this.inventory.sendContents(this); + break; + } - Item[] extraItem = this.inventory.addItem(recipe.getResult()); - if (extraItem.length > 0) { - for (Item i : extraItem) { - this.level.dropItem(this, i); - } - } + for (Item ingredient : realSerialized.values()) { + this.craftingGrid.removeFromAll(ingredient); } + this.inventory.addItem(recipe.getResult()); + switch (recipe.getResult().getId()) { case Item.WORKBENCH: this.awardAchievement("buildWorkBench"); @@ -3460,115 +2735,6 @@ public void handleDataPacket(DataPacket packet) { break; } - break; - case ProtocolInfo.CONTAINER_SET_SLOT_PACKET: - if (!this.spawned || !this.isAlive()) { - break; - } - - ContainerSetSlotPacket containerSetSlotPacket = (ContainerSetSlotPacket) packet; - if (containerSetSlotPacket.slot < 0) { - break; - } - - Inventory inv; - BaseTransaction transaction; - if (containerSetSlotPacket.windowid == 0) { //Our inventory - inv = this.inventory; - if (containerSetSlotPacket.slot >= this.inventory.getSize()) { - break; - } - if (this.isCreative()) { - if (Item.getCreativeItemIndex(containerSetSlotPacket.item) != -1) { - inv.setItem(containerSetSlotPacket.slot, containerSetSlotPacket.item); - this.inventory.setHotbarSlotIndex(containerSetSlotPacket.slot, containerSetSlotPacket.slot); //links hotbar[packet.slot] to slots[packet.slot] - } - } - transaction = new BaseTransaction(this.inventory, containerSetSlotPacket.slot, this.inventory.getItem(containerSetSlotPacket.slot), containerSetSlotPacket.item); - } else if (containerSetSlotPacket.windowid == ContainerSetContentPacket.SPECIAL_ARMOR) { //Our armor - inv = this.inventory; - if (containerSetSlotPacket.slot >= 4) { - break; - } - transaction = new BaseTransaction(this.inventory, containerSetSlotPacket.slot + this.inventory.getSize(), this.inventory.getArmorItem(containerSetSlotPacket.slot), containerSetSlotPacket.item); - } else if (this.windowIndex.containsKey(containerSetSlotPacket.windowid)) { - inv = this.windowIndex.get(containerSetSlotPacket.windowid); - - if (!(inv instanceof AnvilInventory)) { - this.craftingType = CRAFTING_SMALL; - } - - if (inv instanceof EnchantInventory && containerSetSlotPacket.item.hasEnchantments()) { - ((EnchantInventory) inv).onEnchant(this, inv.getItem(containerSetSlotPacket.slot), containerSetSlotPacket.item); - } - - transaction = new BaseTransaction(inv, containerSetSlotPacket.slot, inv.getItem(containerSetSlotPacket.slot), containerSetSlotPacket.item); - } else { - break; - } - - if (inv != null) { - Item sourceItem = inv.getItem(containerSetSlotPacket.slot); - Item heldItem = sourceItem.clone(); - heldItem.setCount(sourceItem.getCount() - containerSetSlotPacket.item.getCount()); - if (heldItem.getCount() > 0) { //In win10, click mouse and hold on item - InventoryClickEvent inventoryClickEvent = new InventoryClickEvent(inv, containerSetSlotPacket.slot, sourceItem, heldItem, containerSetSlotPacket.item); - this.getServer().getPluginManager().callEvent(inventoryClickEvent); - //TODO Fix hold on bug and support Cancellable - } - } - - if (transaction.getSourceItem().deepEquals(transaction.getTargetItem()) && transaction.getTargetItem().getCount() == transaction.getSourceItem().getCount()) { //No changes! - //No changes, just a local inventory update sent by the server - break; - } - - - if (this.currentTransaction == null || this.currentTransaction.getCreationTime() < (System.currentTimeMillis() - 8 * 1000)) { - if (this.currentTransaction != null) { - for (Inventory inventory : this.currentTransaction.getInventories()) { - if (inventory instanceof PlayerInventory) { - ((PlayerInventory) inventory).sendArmorContents(this); - } - inventory.sendContents(this); - } - } - this.currentTransaction = new SimpleTransactionGroup(this); - } - - this.currentTransaction.addTransaction(transaction); - - if (this.currentTransaction.canExecute() || this.isCreative()) { - HashSet achievements = new HashSet<>(); - - for (Transaction tr : this.currentTransaction.getTransactions()) { - Inventory inv1 = tr.getInventory(); - - if (inv1 instanceof FurnaceInventory) { - if (tr.getSlot() == 2) { - switch (((FurnaceInventory) inv1).getResult().getId()) { - case Item.IRON_INGOT: - achievements.add("acquireIron"); - break; - } - } - } - } - - if (this.currentTransaction.execute(this.isCreative())) { - for (String achievement : achievements) { - this.awardAchievement(achievement); - } - } - - this.currentTransaction = null; - } else { - if (containerSetSlotPacket.item.getId() != 0) { - inventory.sendSlot(containerSetSlotPacket.hotbarSlot, this); - inventory.sendSlot(containerSetSlotPacket.slot, this); - } - } - break; case ProtocolInfo.BLOCK_ENTITY_DATA_PACKET: if (!this.spawned || !this.isAlive()) { @@ -3576,6 +2742,7 @@ public void handleDataPacket(DataPacket packet) { } BlockEntityDataPacket blockEntityDataPacket = (BlockEntityDataPacket) packet; this.craftingType = CRAFTING_SMALL; + this.resetCraftingGridType(); pos = new Vector3(blockEntityDataPacket.x, blockEntityDataPacket.y, blockEntityDataPacket.z); if (pos.distanceSquared(this) > 10000) { @@ -3583,7 +2750,7 @@ public void handleDataPacket(DataPacket packet) { } BlockEntity t = this.level.getBlockEntity(pos); - if (t instanceof BlockEntitySign) { + if (t instanceof BlockEntitySpawnable) { CompoundTag nbt; try { nbt = NBTIO.read(blockEntityDataPacket.namedTag, ByteOrder.LITTLE_ENDIAN, true); @@ -3591,35 +2758,15 @@ public void handleDataPacket(DataPacket packet) { throw new RuntimeException(e); } - if (!BlockEntity.SIGN.equals(nbt.getString("id"))) { - ((BlockEntitySign) t).spawnTo(this); - } else { - SignChangeEvent signChangeEvent = new SignChangeEvent(t.getBlock(), this, new String[]{ - this.removeFormat ? TextFormat.clean(nbt.getString("Text1")) : nbt.getString("Text1"), - this.removeFormat ? TextFormat.clean(nbt.getString("Text2")) : nbt.getString("Text2"), - this.removeFormat ? TextFormat.clean(nbt.getString("Text3")) : nbt.getString("Text3"), - this.removeFormat ? TextFormat.clean(nbt.getString("Text4")) : nbt.getString("Text4") - }); - - if (!t.namedTag.contains("Creator") || !Objects.equals(this.getUniqueId().toString(), t.namedTag.getString("Creator"))) { - signChangeEvent.setCancelled(); - } - - this.server.getPluginManager().callEvent(signChangeEvent); - - if (!signChangeEvent.isCancelled()) { - ((BlockEntitySign) t).setText(signChangeEvent.getLine(0), signChangeEvent.getLine(1), signChangeEvent.getLine(2), signChangeEvent.getLine(3)); - } else { - ((BlockEntitySign) t).spawnTo(this); - } - + if (!((BlockEntitySpawnable) t).updateCompoundTag(nbt, this)) { + ((BlockEntitySpawnable) t).spawnTo(this); } } break; case ProtocolInfo.REQUEST_CHUNK_RADIUS_PACKET: RequestChunkRadiusPacket requestChunkRadiusPacket = (RequestChunkRadiusPacket) packet; ChunkRadiusUpdatedPacket chunkRadiusUpdatePacket = new ChunkRadiusUpdatedPacket(); - this.chunkRadius = Math.max(5, Math.min(requestChunkRadiusPacket.radius, this.viewDistance)); + this.chunkRadius = Math.max(3, Math.min(requestChunkRadiusPacket.radius, this.viewDistance)); chunkRadiusUpdatePacket.radius = this.chunkRadius; this.dataPacket(chunkRadiusUpdatePacket); break; @@ -3643,7 +2790,7 @@ public void handleDataPacket(DataPacket packet) { BlockEntity blockEntityItemFrame = this.level.getBlockEntity(vector3); BlockEntityItemFrame itemFrame = (BlockEntityItemFrame) blockEntityItemFrame; if (itemFrame != null) { - Block block = itemFrame.getBlock(); + block = itemFrame.getBlock(); Item itemDrop = itemFrame.getItem(); ItemFrameDropItemEvent itemFrameDropItemEvent = new ItemFrameDropItemEvent(this, block, itemFrame, itemDrop); this.server.getPluginManager().callEvent(itemFrameDropItemEvent); @@ -3675,21 +2822,404 @@ public void handleDataPacket(DataPacket packet) { if (be instanceof BlockEntityItemFrame) { BlockEntityItemFrame itemFrame1 = (BlockEntityItemFrame) be; - if (itemFrame1.getItem() instanceof ItemMap && ((ItemMap) itemFrame1.getItem()).getMapId() == pk.mapId) { - ((ItemMap) itemFrame1.getItem()).sendImage(this); - break; + if (itemFrame1.getItem() instanceof ItemMap && ((ItemMap) itemFrame1.getItem()).getMapId() == pk.mapId) { + ((ItemMap) itemFrame1.getItem()).sendImage(this); + break; + } + } + } + } + + if (mapItem != null) { + PlayerMapInfoRequestEvent event; + getServer().getPluginManager().callEvent(event = new PlayerMapInfoRequestEvent(this, mapItem)); + + if (!event.isCancelled()) { + ((ItemMap) mapItem).sendImage(this); + } + } + + break; + case ProtocolInfo.LEVEL_SOUND_EVENT_PACKET: + //LevelSoundEventPacket levelSoundEventPacket = (LevelSoundEventPacket) packet; + //We just need to broadcast this packet to all viewers. + this.level.addChunkPacket(this.getFloorX() >> 4, this.getFloorZ() >> 4, packet); + break; + case ProtocolInfo.INVENTORY_TRANSACTION_PACKET: + InventoryTransactionPacket transactionPacket = (InventoryTransactionPacket) packet; + + boolean isCrafting = false; + List actions = new ArrayList<>(); + for (NetworkInventoryAction networkInventoryAction : transactionPacket.actions) { + try { + InventoryAction a = networkInventoryAction.createInventoryAction(this); + if (a != null) { + if (a instanceof SlotChangeAction) { + if (((SlotChangeAction) a).getInventory() instanceof CraftingGrid) + isCrafting = true; + } + actions.add(a); + } + } catch (Throwable e) { + MainLogger.getLogger().debug("Unhandled inventory action from " + this.getName() + ": " + e.getMessage()); + this.sendAllInventories(); + break packetswitch; + } + } + + switch (transactionPacket.transactionType) { + case InventoryTransactionPacket.TYPE_NORMAL: + if (this.isSpectator()) { + this.sendAllInventories(); + break; + } + InventoryTransaction transaction = new SimpleInventoryTransaction(this, actions); + + if (!transaction.execute() && !isCrafting) { + for (Inventory inventory : transaction.getInventories()) { + inventory.sendContents(this); + if (inventory instanceof PlayerInventory) { + ((PlayerInventory) inventory).sendArmorContents(this); + } + } + + MainLogger.getLogger().debug("Failed to execute inventory transaction from " + this.getName() + " with actions: " + Arrays.toString(actions.stream().toArray())); + + //TODO: check more stuff that might need reversion + break packetswitch; //oops! + } + + //TODO: fix achievement for getting iron from furnace + + break packetswitch; + case InventoryTransactionPacket.TYPE_MISMATCH: + if (transactionPacket.actions.length > 0) { + this.server.getLogger().debug("Expected 0 actions for mismatch, got " + transactionPacket.actions.length + ", " + Arrays.toString(transactionPacket.actions)); + } + this.sendAllInventories(); + + break packetswitch; + case InventoryTransactionPacket.TYPE_USE_ITEM: + UseItemData useItemData = (UseItemData) transactionPacket.transactionData; + + BlockVector3 blockVector = useItemData.blockPos; + face = useItemData.face; + + int type = useItemData.actionType; + switch (type) { + case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_BLOCK: + this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false); + + if (this.canInteract(blockVector.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7)) { + if (this.isCreative()) { + Item i = inventory.getItemInHand(); + if (this.level.useItemOn(blockVector.asVector3(), i, face, useItemData.clickPos.x, useItemData.clickPos.y, useItemData.clickPos.z, this) != null) { + break packetswitch; + } + } else if (inventory.getItemInHand().equals(useItemData.itemInHand)) { + Item i = inventory.getItemInHand(); + Item oldItem = i.clone(); + //TODO: Implement adventure mode checks + if ((i = this.level.useItemOn(blockVector.asVector3(), i, face, useItemData.clickPos.x, useItemData.clickPos.y, useItemData.clickPos.z, this)) != null) { + if (!i.equals(oldItem) || i.getCount() != oldItem.getCount()) { + inventory.setItemInHand(i); + inventory.sendHeldItem(this.getViewers().values()); + } + break packetswitch; + } + } + } + + inventory.sendHeldItem(this); + + if (blockVector.distanceSquared(this) > 10000) { + break packetswitch; + } + + Block target = this.level.getBlock(blockVector.asVector3()); + block = target.getSide(face); + + this.level.sendBlocks(new Player[]{this}, new Block[]{target, block}, UpdateBlockPacket.FLAG_ALL_PRIORITY); + + if (target instanceof BlockDoor) { + BlockDoor door = (BlockDoor) target; + + Block part; + + if ((door.getDamage() & 0x08) > 0) { //up + part = target.down(); + + if (part.getId() == target.getId()) { + target = part; + + this.level.sendBlocks(new Player[]{this}, new Block[]{target}, UpdateBlockPacket.FLAG_ALL_PRIORITY); + } + } + } + break packetswitch; + case InventoryTransactionPacket.USE_ITEM_ACTION_BREAK_BLOCK: + if (!this.spawned || !this.isAlive()) { + break packetswitch; + } + + this.resetCraftingGridType(); + + Item i = this.getInventory().getItemInHand(); + + Item oldItem = i.clone(); + + if (this.canInteract(blockVector.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7) && (i = this.level.useBreakOn(blockVector.asVector3(), i, this, true)) != null) { + if (this.isSurvival()) { + this.getFoodData().updateFoodExpLevel(0.025); + if (!i.equals(oldItem) || i.getCount() != oldItem.getCount()) { + inventory.setItemInHand(i); + inventory.sendHeldItem(this.getViewers().values()); + } + } + break packetswitch; + } + + inventory.sendContents(this); + target = this.level.getBlock(blockVector.asVector3()); + BlockEntity blockEntity = this.level.getBlockEntity(blockVector.asVector3()); + + this.level.sendBlocks(new Player[]{this}, new Block[]{target}, UpdateBlockPacket.FLAG_ALL_PRIORITY); + + inventory.sendHeldItem(this); + + if (blockEntity instanceof BlockEntitySpawnable) { + ((BlockEntitySpawnable) blockEntity).spawnTo(this); + } + + break packetswitch; + case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_AIR: + Vector3 directionVector = this.getDirectionVector(); + + if (this.isCreative()) { + item = this.inventory.getItemInHand(); + } else if (!this.inventory.getItemInHand().equals(useItemData.itemInHand)) { + this.inventory.sendHeldItem(this); + break packetswitch; + } else { + item = this.inventory.getItemInHand(); + } + + PlayerInteractEvent interactEvent = new PlayerInteractEvent(this, item, directionVector, face, Action.RIGHT_CLICK_AIR); + + this.server.getPluginManager().callEvent(interactEvent); + + if (interactEvent.isCancelled()) { + this.inventory.sendHeldItem(this); + break packetswitch; + } + + if (item.onClickAir(this, directionVector) && this.isSurvival()) { + this.inventory.setItemInHand(item); + } + + this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, true); + this.startAction = this.server.getTick(); + + break packetswitch; + default: + //unknown + break; + } + break; + case InventoryTransactionPacket.TYPE_USE_ITEM_ON_ENTITY: + UseItemOnEntityData useItemOnEntityData = (UseItemOnEntityData) transactionPacket.transactionData; + + Entity target = this.level.getEntity(useItemOnEntityData.entityRuntimeId); + if (target == null) { + return; + } + + type = useItemOnEntityData.actionType; + + if (!useItemOnEntityData.itemInHand.equalsExact(this.inventory.getItemInHand())) { + this.inventory.sendHeldItem(this); + } + + item = this.inventory.getItemInHand(); + + switch (type) { + case InventoryTransactionPacket.USE_ITEM_ON_ENTITY_ACTION_INTERACT: + PlayerInteractEntityEvent playerInteractEntityEvent = new PlayerInteractEntityEvent(this, target, item); + if (this.isSpectator()) playerInteractEntityEvent.setCancelled(); + getServer().getPluginManager().callEvent(playerInteractEntityEvent); + + if (playerInteractEntityEvent.isCancelled()) { + break; + } + + if (target.onInteract(this, item) && this.isSurvival()) { + if (item.isTool()) { + if (item.useOn(target) && item.getDamage() >= item.getMaxDurability()) { + item = new ItemBlock(new BlockAir()); + } + } else { + if (item.count > 1) { + item.count--; + } else { + item = new ItemBlock(new BlockAir()); + } + } + + this.inventory.setItemInHand(item); + } + break; + case InventoryTransactionPacket.USE_ITEM_ON_ENTITY_ACTION_ATTACK: + float itemDamage = item.getAttackDamage(); + + for (Enchantment enchantment : item.getEnchantments()) { + itemDamage += enchantment.getDamageBonus(target); + } + + Map damage = new EnumMap<>(DamageModifier.class); + damage.put(DamageModifier.BASE, itemDamage); + + if (!this.canInteract(target, isCreative() ? 8 : 5)) { + break; + } else if (target instanceof Player) { + if ((((Player) target).getGamemode() & 0x01) > 0) { + break; + } else if (!this.server.getPropertyBoolean("pvp") || this.server.getDifficulty() == 0) { + break; + } + } + + EntityDamageByEntityEvent entityDamageByEntityEvent = new EntityDamageByEntityEvent(this, target, DamageCause.ENTITY_ATTACK, damage); + if (this.isSpectator()) entityDamageByEntityEvent.setCancelled(); + if (!target.attack(entityDamageByEntityEvent)) { + if (item.isTool() && this.isSurvival()) { + this.inventory.sendContents(this); + } + break; + } + + for (Enchantment enchantment : item.getEnchantments()) { + enchantment.doPostAttack(this, target); + } + + if (item.isTool() && this.isSurvival()) { + if (item.useOn(target) && item.getDamage() >= item.getMaxDurability()) { + this.inventory.setItemInHand(new ItemBlock(new BlockAir())); + } else { + this.inventory.setItemInHand(item); + } + } + return; + default: + break; //unknown + } + + break; + case InventoryTransactionPacket.TYPE_RELEASE_ITEM: + if (this.isSpectator()) { + this.sendAllInventories(); + break packetswitch; + } + ReleaseItemData releaseItemData = (ReleaseItemData) transactionPacket.transactionData; + + try { + type = releaseItemData.actionType; + switch (type) { + case InventoryTransactionPacket.RELEASE_ITEM_ACTION_RELEASE: + if (this.isUsingItem()) { + item = this.inventory.getItemInHand(); + if (item.onReleaseUsing(this)) { + this.inventory.setItemInHand(item); + } + } else { + this.inventory.sendContents(this); + } + return; + case InventoryTransactionPacket.RELEASE_ITEM_ACTION_CONSUME: + Item itemInHand = this.inventory.getItemInHand(); + PlayerItemConsumeEvent consumeEvent = new PlayerItemConsumeEvent(this, itemInHand); + + if (itemInHand.getId() == Item.POTION) { + this.server.getPluginManager().callEvent(consumeEvent); + if (consumeEvent.isCancelled()) { + this.inventory.sendContents(this); + break; + } + Potion potion = Potion.getPotion(itemInHand.getDamage()).setSplash(false); + + if (this.getGamemode() == SURVIVAL) { + --itemInHand.count; + this.inventory.setItemInHand(itemInHand); + this.inventory.addItem(new ItemGlassBottle()); + } + + 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: + break; } + } finally { + this.setUsingItem(false); } - } + break; + default: + this.inventory.sendContents(this); + break; } + break; + case ProtocolInfo.PLAYER_HOTBAR_PACKET: + PlayerHotbarPacket hotbarPacket = (PlayerHotbarPacket) packet; - if (mapItem != null) { - PlayerMapInfoRequestEvent event; - getServer().getPluginManager().callEvent(event = new PlayerMapInfoRequestEvent(this, mapItem)); + if (hotbarPacket.windowId != ContainerIds.INVENTORY) { + return; //In PE this should never happen + } - if (!event.isCancelled()) { - ((ItemMap) mapItem).sendImage(this); - } + this.inventory.equipItem(hotbarPacket.selectedHotbarSlot); + break; + case ProtocolInfo.SERVER_SETTINGS_REQUEST_PACKET: + PlayerServerSettingsRequestEvent settingsRequestEvent = new PlayerServerSettingsRequestEvent(this, new HashMap<>(this.serverSettings)); + this.getServer().getPluginManager().callEvent(settingsRequestEvent); + + if (!settingsRequestEvent.isCancelled()) { + settingsRequestEvent.getSettings().forEach((id, window) -> { + ServerSettingsResponsePacket re = new ServerSettingsResponsePacket(); + re.formId = id; + re.data = window.getJSONData(); + this.dataPacket(re); + }); } break; default: @@ -3698,6 +3228,35 @@ public void handleDataPacket(DataPacket packet) { } } + /** + * Sends a chat message as this player. If the message begins with a / (forward-slash) it will be treated + * as a command. + */ + public boolean chat(String message) { + if (!this.spawned || !this.isAlive()) { + return false; + } + + this.resetCraftingGridType(); + this.craftingType = CRAFTING_SMALL; + + if (this.removeFormat) { + message = TextFormat.clean(message); + } + + for (String msg : message.split("\n")) { + if (!msg.trim().isEmpty() && msg.length() <= 255 && this.messageCounter-- > 0) { + PlayerChatEvent chatEvent = new PlayerChatEvent(this, msg); + this.server.getPluginManager().callEvent(chatEvent); + if (!chatEvent.isCancelled()) { + this.server.broadcastMessage(this.getServer().getLanguage().translateString(chatEvent.getFormat(), new String[]{chatEvent.getPlayer().getDisplayName(), chatEvent.getMessage()}), chatEvent.getRecipients()); + } + } + } + + return true; + } + public boolean kick() { return this.kick(""); } @@ -3729,12 +3288,12 @@ public boolean kick(PlayerKickEvent.Reason reason, String reasonString, boolean String message; if (isAdmin) { if (!this.isBanned()) { - message = "Kicked by admin." + (!"".equals(reasonString) ? " Reason: " + reasonString : ""); + message = "Kicked by admin." + (!reasonString.isEmpty() ? " Reason: " + reasonString : ""); } else { message = reasonString; } } else { - if ("".equals(reasonString)) { + if (reasonString.isEmpty()) { message = "disconnectionScreen.noReason"; } else { message = reasonString; @@ -3749,17 +3308,25 @@ public boolean kick(PlayerKickEvent.Reason reason, String reasonString, boolean return false; } + public void setViewDistance(int distance) { + this.chunkRadius = distance; + + ChunkRadiusUpdatedPacket pk = new ChunkRadiusUpdatedPacket(); + pk.radius = distance; + + this.dataPacket(pk); + } + + public int getViewDistance() { + return this.chunkRadius; + } + @Override public void sendMessage(String message) { - // TODO: Remove this workaround (broken client MCPE 1.0.0) - messageQueue.add(this.server.getLanguage().translateString(message)); - - /* TextPacket pk = new TextPacket(); pk.type = TextPacket.TYPE_RAW; pk.message = this.server.getLanguage().translateString(message); this.dataPacket(pk); - */ } @Override @@ -3792,6 +3359,18 @@ public void sendTranslation(String message, String[] parameters) { this.dataPacket(pk); } + public void sendChat(String message) { + this.sendChat("", message); + } + + public void sendChat(String source, String message) { + TextPacket pk = new TextPacket(); + pk.type = TextPacket.TYPE_CHAT; + pk.source = source; + pk.message = this.server.getLanguage().translateString(message); + this.dataPacket(pk); + } + public void sendPopup(String message) { this.sendPopup(message, ""); } @@ -3826,43 +3405,60 @@ public void resetTitleSettings() { this.dataPacket(pk); } - public void sendTitle(String text) { + public void setSubtitle(String subtitle) { SetTitlePacket pk = new SetTitlePacket(); - pk.type = SetTitlePacket.TYPE_TITLE; - pk.text = text; + pk.type = SetTitlePacket.TYPE_SUBTITLE; + pk.text = subtitle; this.dataPacket(pk); } - /** - * Sets a subtitle for the next shown title - * @param text Subtitle text - */ - public void setSubtitle(String text) { + public void setTitleAnimationTimes(int fadein, int duration, int fadeout) { SetTitlePacket pk = new SetTitlePacket(); - pk.type = SetTitlePacket.TYPE_SUBTITLE; - pk.text = text; + pk.type = SetTitlePacket.TYPE_ANIMATION_TIMES; + pk.fadeInTime = fadein; + pk.stayTime = duration; + pk.fadeOutTime = fadeout; this.dataPacket(pk); } - public void sendActionBarTitle(String text) { - SetTitlePacket pk = new SetTitlePacket(); - pk.type = SetTitlePacket.TYPE_ACTION_BAR; - pk.text = text; - this.dataPacket(pk); + public void sendTitle(String title) { + this.sendTitle(title, "", 20, 20, 5); } - /** - * Sets times for title animations - * @param fadeInTime For how long title fades in - * @param stayTime For how long title is shown - * @param fadeOutTime For how long title fades out - */ - public void setTitleAnimationTimes(int fadeInTime, int stayTime, int fadeOutTime) { + public void sendTitle(String title, String subtitle) { + this.sendTitle(title, subtitle, 20, 20, 5); + } + + public void sendTitle(String title, String subtitle, int fadein, int duration, int fadeout) { + if (!subtitle.equals("")) { + SetTitlePacket pk = new SetTitlePacket(); + pk.type = SetTitlePacket.TYPE_SUBTITLE; + pk.text = subtitle; + pk.fadeInTime = fadein; + pk.stayTime = duration; + pk.fadeOutTime = fadeout; + this.dataPacket(pk); + } + SetTitlePacket pk2 = new SetTitlePacket(); + pk2.type = SetTitlePacket.TYPE_TITLE; + pk2.text = title; + pk2.fadeInTime = fadein; + pk2.stayTime = duration; + pk2.fadeOutTime = fadeout; + this.dataPacket(pk2); + } + + public void sendActionBar(String title) { + this.sendActionBar(title, 1, 0, 1); + } + + public void sendActionBar(String title, int fadein, int duration, int fadeout) { SetTitlePacket pk = new SetTitlePacket(); - pk.type = SetTitlePacket.TYPE_ANIMATION_TIMES; - pk.fadeInTime = fadeInTime; - pk.stayTime = stayTime; - pk.fadeOutTime = fadeOutTime; + pk.type = SetTitlePacket.TYPE_ACTION_BAR; + pk.text = title; + pk.fadeInTime = fadein; + pk.stayTime = duration; + pk.fadeOutTime = fadeout; this.dataPacket(pk); } @@ -3916,9 +3512,7 @@ public void close(TextContainer message, String reason, boolean notify) { this.hiddenPlayers = new HashMap<>(); - for (Inventory window : new ArrayList<>(this.windowIndex.values())) { - this.removeWindow(window); - } + this.removeAllWindows(true); for (long index : new ArrayList<>(this.usedChunks.keySet())) { int chunkX = Level.getHashX(index); @@ -3955,8 +3549,8 @@ public void close(TextContainer message, String reason, boolean notify) { this.hasSpawned = new HashMap<>(); this.spawnPosition = null; - if (this.riding instanceof EntityVehicle) { - ((EntityVehicle) this.riding).linkedEntity = null; + if (this.riding instanceof EntityRideable) { + this.riding.linkedEntity = null; } this.riding = null; @@ -3969,7 +3563,6 @@ public void close(TextContainer message, String reason, boolean notify) { if (this.inventory != null) { this.inventory = null; - this.currentTransaction = null; } this.chunk = null; @@ -4015,7 +3608,7 @@ public void save(boolean async) { this.namedTag.putInt("foodLevel", this.getFoodData().getLevel()); this.namedTag.putFloat("foodSaturationLevel", this.getFoodData().getFoodSaturationLevel()); - if (!"".equals(this.username) && this.namedTag != null) { + if (!this.username.isEmpty() && this.namedTag != null) { this.server.saveOfflinePlayerData(this.username, this.namedTag, async); } } @@ -4204,7 +3797,8 @@ public void setHealth(float health) { } super.setHealth(health); - Attribute attr = Attribute.getAttribute(Attribute.MAX_HEALTH).setMaxValue(this.getMaxHealth()).setValue(health > 0 ? (health < getMaxHealth() ? health : getMaxHealth()) : 0); + //TODO: Remove it in future! This a hack to solve the client-side absorption bug! WFT Mojang (Half a yellow heart cannot be shown, we can test it in local gaming) + Attribute attr = Attribute.getAttribute(Attribute.MAX_HEALTH).setMaxValue(this.getAbsorption() % 2 != 0 ? this.getMaxHealth() + 1 : this.getMaxHealth()).setValue(health > 0 ? (health < getMaxHealth() ? health : getMaxHealth()) : 0); if (this.spawned) { UpdateAttributesPacket pk = new UpdateAttributesPacket(); pk.entries = new Attribute[]{attr}; @@ -4313,7 +3907,7 @@ public boolean attack(EntityDamageEvent source) { ) { //source.setCancelled(); return false; - } else if (this.getAdventureSettings().canFly() && source.getCause() == DamageCause.FALL) { + } else if (this.getAdventureSettings().get(Type.ALLOW_FLIGHT) && source.getCause() == DamageCause.FALL) { //source.setCancelled(); return false; } else if (source.getCause() == DamageCause.FALL) { @@ -4359,6 +3953,29 @@ public boolean attack(EntityDamageEvent source) { } } + /** + * Drops an item on the ground in front of the player. Returns if the item drop was successful. + * + * @return bool if the item was dropped or if the item was null + */ + public boolean dropItem(Item item) { + if (!this.spawned || !this.isAlive()) { + return false; + } + + if (item.isNull()) { + this.server.getLogger().debug(this.getName() + " attempted to drop a null item (" + item + ")"); + return true; + } + + Vector3 motion = this.getDirectionVector().multiply(0.4); + + this.level.dropItem(this.add(0, 1.3, 0), item, motion, 40); + + this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false); + return true; + } + public void sendPosition(Vector3 pos) { this.sendPosition(pos, this.yaw); } @@ -4371,11 +3988,11 @@ public void sendPosition(Vector3 pos, double yaw, double pitch) { this.sendPosition(pos, yaw, pitch, MovePlayerPacket.MODE_NORMAL); } - public void sendPosition(Vector3 pos, double yaw, double pitch, byte mode) { + public void sendPosition(Vector3 pos, double yaw, double pitch, int mode) { this.sendPosition(pos, yaw, pitch, mode, null); } - public void sendPosition(Vector3 pos, double yaw, double pitch, byte mode, Player[] targets) { + public void sendPosition(Vector3 pos, double yaw, double pitch, int mode, Player[] targets) { MovePlayerPacket pk = new MovePlayerPacket(); pk.eid = this.getId(); pk.x = (float) pos.x; @@ -4443,22 +4060,7 @@ protected boolean checkTeleportPosition() { } } - if (this.isLevelChange) { //TODO: remove this - PlayStatusPacket statusPacket0 = new PlayStatusPacket();//Weather - this.getLevel().sendWeather(this); - //Update time - this.getLevel().sendTime(this); - statusPacket0.status = PlayStatusPacket.PLAYER_SPAWN; - this.dataPacket(statusPacket0); - - //Weather - this.getLevel().sendWeather(this); - //Update time - this.getLevel().sendTime(this); - } - this.spawnToAll(); - this.isLevelChange = false; this.forceMovement = this.teleportPosition; this.teleportPosition = null; return true; @@ -4467,7 +4069,20 @@ protected boolean checkTeleportPosition() { return false; } - private boolean isLevelChange = false; + protected void sendPlayStatus(int status) { + sendPlayStatus(status, false); + } + + protected void sendPlayStatus(int status, boolean immediate) { + PlayStatusPacket pk = new PlayStatusPacket(); + pk.status = status; + + if (immediate) { + this.directDataPacket(pk); + } else { + this.dataPacket(pk); + } + } @Override public boolean teleport(Location location, TeleportCause cause) { @@ -4494,18 +4109,13 @@ public boolean teleport(Location location, TeleportCause cause) { } } - if (super.teleport(to, null)) { // null to prevent fire of duplicate EntityTeleportEvent - - for (Inventory window : new ArrayList<>(this.windowIndex.values())) { - if (window == this.inventory) { - continue; - } - this.removeWindow(window); - } + //TODO Remove it! A hack to solve the client-side teleporting bug! (inside into the block) + if (super.teleport(to.getY() == to.getFloorY() ? to.add(0, 0.00001, 0) : to, null)) { // null to prevent fire of duplicate EntityTeleportEvent + this.removeAllWindows(); this.teleportPosition = new Vector3(this.x, this.y, this.z); this.forceMovement = this.teleportPosition; - this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_RESET); + this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_TELEPORT); this.checkTeleportPosition(); @@ -4513,42 +4123,12 @@ public boolean teleport(Location location, TeleportCause cause) { this.nextChunkOrderRun = 0; this.newPosition = null; + //DummyBossBar + this.getDummyBossBars().values().forEach(DummyBossBar::reshow); //Weather this.getLevel().sendWeather(this); //Update time this.getLevel().sendTime(this); - - if (from.getLevel().getId() != to.level.getId()) { - if (this.spawned) { - //TODO: remove this in future version - this.isLevelChange = true; - this.nextChunkOrderRun = 10000; - - ChangeDimensionPacket changeDimensionPacket1 = new ChangeDimensionPacket(); - changeDimensionPacket1.dimension = 1; - changeDimensionPacket1.x = (float) this.getX(); - changeDimensionPacket1.y = (float) this.getY(); - changeDimensionPacket1.z = (float) this.getZ(); - this.dataPacket(changeDimensionPacket1); - - this.forceSendEmptyChunks(); - this.getServer().getScheduler().scheduleDelayedTask(() -> { - PlayStatusPacket statusPacket0 = new PlayStatusPacket(); - statusPacket0.status = PlayStatusPacket.PLAYER_SPAWN; - dataPacket(statusPacket0); - }, 8); - - this.getServer().getScheduler().scheduleDelayedTask(() -> { - ChangeDimensionPacket changeDimensionPacket = new ChangeDimensionPacket(); - changeDimensionPacket.dimension = 0; - changeDimensionPacket.x = (float) this.getX(); - changeDimensionPacket.y = (float) this.getY(); - changeDimensionPacket.z = (float) this.getZ(); - dataPacket(changeDimensionPacket); - nextChunkOrderRun = 0; - }, 9); - } - } return true; } @@ -4609,103 +4189,107 @@ public void teleportImmediate(Location location, TeleportCause cause) { } } + /** + * Shows a new FormWindow to the player + * You can find out FormWindow result by listening to PlayerFormRespondedEvent + */ + public int showFormWindow(FormWindow window) { + int id = this.formWindowCount++; + + ModalFormRequestPacket packet = new ModalFormRequestPacket(); + packet.formId = id; + packet.data = window.getJSONData(); + this.formWindows.put(packet.formId, window); + + this.dataPacket(packet); + return id; + } + + /** + * Shows a new setting page in game settings + * You can find out settings result by listening to PlayerFormRespondedEvent + */ + public int addServerSettings(FormWindow window) { + int id = this.formWindowCount++; + + this.serverSettings.put(id, window); + return id; + } + /** * Creates and sends a BossBar to the player * - * @param text The BossBar message - * @param length The BossBar percentage + * @param text The BossBar message + * @param length The BossBar percentage * @return bossBarId The BossBar ID, you should store it if you want to remove or update the BossBar later */ + @Deprecated public long createBossBar(String text, int length) { - // First we spawn a entity - long bossBarId = 1095216660480L + ThreadLocalRandom.current().nextLong(0, 0x7fffffffL); - AddEntityPacket pkAdd = new AddEntityPacket(); - pkAdd.type = EntityCreeper.NETWORK_ID; - pkAdd.entityUniqueId = bossBarId; - pkAdd.entityRuntimeId = bossBarId; - pkAdd.x = (float) this.x; - pkAdd.y = (float) -10; // Below the bedrock - pkAdd.z = (float) this.z; - pkAdd.speedX = (float) this.motionX; - pkAdd.speedY = (float) this.motionY; - pkAdd.speedZ = (float) this.motionZ; - EntityMetadata metadata = new EntityMetadata() - // Default Metadata tags - .putLong(DATA_FLAGS, 0) - .putShort(DATA_AIR, 400) - .putShort(DATA_MAX_AIR, 400) - .putLong(DATA_LEAD_HOLDER_EID, -1) - .putFloat(DATA_SCALE, 1f) - .putString(Entity.DATA_NAMETAG, text) // Set the entity name - .putInt(Entity.DATA_SCALE, 0); // And make it invisible - pkAdd.metadata = metadata; - this.dataPacket(pkAdd); - - // Now we send the entity attributes - // TODO: Attributes should be sent on AddEntityPacket, however it doesn't work (client bug?) - UpdateAttributesPacket pkAttributes = new UpdateAttributesPacket(); - pkAttributes.entityId = bossBarId; - Attribute attr = Attribute.getAttribute(Attribute.MAX_HEALTH); - attr.setMaxValue(100); // Max value - We need to change the max value first, or else the "setValue" will return a IllegalArgumentException - attr.setValue(length); // Entity health - pkAttributes.entries = new Attribute[] { attr }; - this.dataPacket(pkAttributes); - - // And now we send the bossbar packet - BossEventPacket pkBoss = new BossEventPacket(); - pkBoss.eid = bossBarId; - pkBoss.type = BossEventPacket.ADD; - this.dataPacket(pkBoss); - return bossBarId; + DummyBossBar bossBar = new DummyBossBar.Builder(this).text(text).length(length).build(); + return this.createBossBar(bossBar); + } + + /** + * Creates and sends a BossBar to the player + * + * @param dummyBossBar DummyBossBar Object (Instantiate it by the Class Builder) + * @return bossBarId The BossBar ID, you should store it if you want to remove or update the BossBar later + * @see DummyBossBar.Builder + */ + public long createBossBar(DummyBossBar dummyBossBar) { + this.dummyBossBars.put(dummyBossBar.getBossBarId(), dummyBossBar); + dummyBossBar.create(); + return dummyBossBar.getBossBarId(); + } + + /** + * Get a DummyBossBar object + * + * @param bossBarId The BossBar ID + * @return DummyBossBar object + * @see DummyBossBar#setText(String) Set BossBar text + * @see DummyBossBar#setLength(float) Set BossBar length + * @see DummyBossBar#setColor(Color) Set BossBar color + */ + public DummyBossBar getDummyBossBar(long bossBarId) { + return this.dummyBossBars.getOrDefault(bossBarId, null); + } + + /** + * Get all DummyBossBar objects + * + * @return DummyBossBars Map + */ + public Map getDummyBossBars() { + return dummyBossBars; } /** * Updates a BossBar * - * @param text The new BossBar message - * @param length The new BossBar length - * @param bossBarId The BossBar ID + * @param text The new BossBar message + * @param length The new BossBar length + * @param bossBarId The BossBar ID */ + @Deprecated public void updateBossBar(String text, int length, long bossBarId) { - // First we update the boss bar length - UpdateAttributesPacket pkAttributes = new UpdateAttributesPacket(); - pkAttributes.entityId = bossBarId; - Attribute attr = Attribute.getAttribute(Attribute.MAX_HEALTH); - attr.setMaxValue(100); // Max value - We need to change the max value first, or else the "setValue" will return a IllegalArgumentException - attr.setValue(length); // Entity health - pkAttributes.entries = new Attribute[] { attr }; - this.dataPacket(pkAttributes); - // And then the boss bar text - SetEntityDataPacket pkMetadata = new SetEntityDataPacket(); - pkMetadata.eid = bossBarId; - pkMetadata.metadata = new EntityMetadata() - // Default Metadata tags - .putLong(DATA_FLAGS, 0) - .putShort(DATA_AIR, 400) - .putShort(DATA_MAX_AIR, 400) - .putLong(DATA_LEAD_HOLDER_EID, -1) - .putFloat(DATA_SCALE, 1f) - .putString(Entity.DATA_NAMETAG, text) // Set the entity name - .putInt(Entity.DATA_SCALE, 0); // And make it invisible - this.dataPacket(pkMetadata); - - // And now we send the bossbar packet - BossEventPacket pkBoss = new BossEventPacket(); - pkBoss.eid = bossBarId; - pkBoss.type = BossEventPacket.UPDATE; - this.dataPacket(pkBoss); - return; + if (this.dummyBossBars.containsKey(bossBarId)) { + DummyBossBar bossBar = this.dummyBossBars.get(bossBarId); + bossBar.setText(text); + bossBar.setLength(length); + } } /** * Removes a BossBar * - * @param bossBarId The BossBar ID + * @param bossBarId The BossBar ID */ public void removeBossBar(long bossBarId) { - RemoveEntityPacket pkRemove = new RemoveEntityPacket(); - pkRemove.eid = bossBarId; - this.dataPacket(pkRemove); + if (this.dummyBossBars.containsKey(bossBarId)) { + this.dummyBossBars.get(bossBarId).destroy(); + this.dummyBossBars.remove(bossBarId); + } } public int getWindowId(Inventory inventory) { @@ -4716,22 +4300,35 @@ public int getWindowId(Inventory inventory) { return -1; } + public Inventory getWindowById(int id) { + return this.windowIndex.get(id); + } + public int addWindow(Inventory inventory) { return this.addWindow(inventory, null); } public int addWindow(Inventory inventory, Integer forceId) { + return addWindow(inventory, forceId, false); + } + + public int addWindow(Inventory inventory, Integer forceId, boolean isPermanent) { if (this.windows.containsKey(inventory)) { return this.windows.get(inventory); } int cnt; if (forceId == null) { - this.windowCnt = cnt = Math.max(2, ++this.windowCnt % 99); + this.windowCnt = cnt = Math.max(4, ++this.windowCnt % 99); } else { cnt = forceId; } this.windowIndex.put(cnt, inventory); this.windows.put(inventory, cnt); + + if (isPermanent) { + this.permanentWindows.add(cnt); + } + if (inventory.open(this)) { return cnt; } else { @@ -4750,6 +4347,65 @@ public void removeWindow(Inventory inventory) { } } + public void sendAllInventories() { + for (Inventory inv : this.windowIndex.values()) { + inv.sendContents(this); + + if (inv instanceof PlayerInventory) { + ((PlayerInventory) inv).sendArmorContents(this); + } + } + } + + protected void addDefaultWindows() { + this.addWindow(this.getInventory(), ContainerIds.INVENTORY, true); + + this.cursorInventory = new PlayerCursorInventory(this); + this.addWindow(this.cursorInventory, ContainerIds.CURSOR, true); + + this.craftingGrid = new CraftingGrid(this); + + //TODO: more windows + } + + public PlayerCursorInventory getCursorInventory() { + return this.cursorInventory; + } + + public CraftingGrid getCraftingGrid() { + return this.craftingGrid; + } + + public void setCraftingGrid(CraftingGrid grid) { + this.craftingGrid = grid; + } + + public void resetCraftingGridType() { + if (this.craftingGrid instanceof BigCraftingGrid) { + Item[] drops = this.inventory.addItem(this.craftingGrid.getContents().values().stream().toArray(Item[]::new)); + for (Item drop : drops) { + this.dropItem(drop); + } + + this.craftingGrid = new CraftingGrid(this); + this.craftingType = 0; + } + } + + public void removeAllWindows() { + removeAllWindows(false); + } + + public void removeAllWindows(boolean permanent) { + for (Entry entry : new ArrayList<>(this.windowIndex.entrySet())) { + if (!permanent && this.permanentWindows.contains(entry.getKey())) { + continue; + } + + this.removeWindow(entry.getValue()); + } + } + @Override public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { this.server.getPlayerMetadata().setMetadata(this, metadataKey, newMetadataValue); @@ -4874,20 +4530,110 @@ public void setSprinting(boolean value, boolean setDefault) { } public void transfer(InetSocketAddress address) { - String hostName = address.getHostName(); + String hostName = address.getAddress().getHostAddress(); int port = address.getPort(); TransferPacket pk = new TransferPacket(); pk.address = hostName; pk.port = port; this.dataPacket(pk); - String message = "Transferred to " + address + ":" + port; + String message = "Transferred to " + hostName + ":" + port; this.close(message, message, false); } - public ClientChainData getLoginChainData() { + public LoginChainData getLoginChainData() { return this.loginChainData; } + public boolean pickupEntity(Entity entity, boolean near) { + if (!this.spawned || !this.isAlive() || !this.isOnline() || this.getGamemode() == SPECTATOR) { + return false; + } + + if (near) { + if (entity instanceof EntityArrow && ((EntityArrow) entity).hadCollision) { + ItemArrow item = new ItemArrow(); + if (this.isSurvival() && !this.inventory.canAddItem(item)) { + return false; + } + + InventoryPickupArrowEvent ev; + this.server.getPluginManager().callEvent(ev = new InventoryPickupArrowEvent(this.inventory, (EntityArrow) entity)); + if (ev.isCancelled()) { + return false; + } + + TakeItemEntityPacket pk = new TakeItemEntityPacket(); + pk.entityId = this.getId(); + pk.target = entity.getId(); + Server.broadcastPacket(entity.getViewers().values(), pk); + this.dataPacket(pk); + + this.inventory.addItem(item.clone()); + entity.kill(); + return true; + } else if (entity instanceof EntityItem) { + if (((EntityItem) entity).getPickupDelay() <= 0) { + Item item = ((EntityItem) entity).getItem(); + + if (item != null) { + if (this.isSurvival() && !this.inventory.canAddItem(item)) { + return false; + } + + InventoryPickupItemEvent ev; + this.server.getPluginManager().callEvent(ev = new InventoryPickupItemEvent(this.inventory, (EntityItem) entity)); + if (ev.isCancelled()) { + return false; + } + + switch (item.getId()) { + case Item.WOOD: + case Item.WOOD2: + this.awardAchievement("mineWood"); + break; + case Item.DIAMOND: + this.awardAchievement("diamond"); + break; + } + /*switch (item.getId()) { + case Item.WOOD: + this.awardAchievement("mineWood"); + break; + case Item.DIAMOND: + this.awardAchievement("diamond"); + break; + }*/ + + TakeItemEntityPacket pk = new TakeItemEntityPacket(); + pk.entityId = this.getId(); + pk.target = entity.getId(); + Server.broadcastPacket(entity.getViewers().values(), pk); + this.dataPacket(pk); + + this.inventory.addItem(item.clone()); + entity.kill(); + return true; + } + } + } + } + + int tick = this.getServer().getTick(); + if (pickedXPOrb < tick && entity instanceof EntityXPOrb && this.boundingBox.isVectorInside(entity)) { + EntityXPOrb xpOrb = (EntityXPOrb) entity; + if (xpOrb.getPickupDelay() <= 0) { + int exp = xpOrb.getExp(); + this.addExperience(exp); + entity.kill(); + this.getLevel().addSound(new ExperienceOrbSound(this)); + pickedXPOrb = tick; + return true; + } + } + + return false; + } + @Override public int hashCode() { if ((this.hash == 0) || (this.hash == 485)) { @@ -4914,4 +4660,18 @@ public boolean equals(Object obj) { public void notifyACK(int identification) { needACK.put(identification, true); } + + public boolean isBreakingBlock() { + return this.breakingBlock != null; + } + + /** + * Show a window of a XBOX account's profile + * @param xuid XUID + */ + public void showXboxProfile(String xuid) { + ShowProfilePacket pk = new ShowProfilePacket(); + pk.xuid = xuid; + this.dataPacket(pk); + } } diff --git a/src/main/java/cn/nukkit/PlayerFood.java b/src/main/java/cn/nukkit/PlayerFood.java index 9ab78ac94e..166ba513b4 100644 --- a/src/main/java/cn/nukkit/PlayerFood.java +++ b/src/main/java/cn/nukkit/PlayerFood.java @@ -136,37 +136,37 @@ public void sendFoodLevel(int foodLevel) { public void update(int tickDiff) { if (!this.getPlayer().isFoodEnabled()) return; - if (this.getPlayer().isAlive()) { - int diff = Server.getInstance().getDifficulty(); - if (this.getLevel() > 17) { - this.foodTickTimer += tickDiff; - if (this.foodTickTimer >= 80) { - if (this.getPlayer().getHealth() < this.getPlayer().getMaxHealth()) { - EntityRegainHealthEvent ev = new EntityRegainHealthEvent(this.getPlayer(), 1, EntityRegainHealthEvent.CAUSE_EATING); - this.getPlayer().heal(ev); - //this.updateFoodExpLevel(3); - } - this.foodTickTimer = 0; - } - } else if (this.getLevel() == 0) { - this.foodTickTimer += tickDiff; - if (this.foodTickTimer >= 80) { - EntityDamageEvent ev = new EntityDamageEvent(this.getPlayer(), DamageCause.VOID, 1); - float now = this.getPlayer().getHealth(); - if (diff == 1) { - if (now > 10) this.getPlayer().attack(ev); - } else if (diff == 2) { - if (now > 1) this.getPlayer().attack(ev); - } else { - this.getPlayer().attack(ev); - } - - this.foodTickTimer = 0; - } - } - if (this.getPlayer().hasEffect(Effect.HUNGER)) { - this.updateFoodExpLevel(0.025); - } + if (this.getPlayer().isAlive()) { + int diff = Server.getInstance().getDifficulty(); + if (this.getLevel() > 17) { + this.foodTickTimer += tickDiff; + if (this.foodTickTimer >= 80) { + if (this.getPlayer().getHealth() < this.getPlayer().getMaxHealth()) { + EntityRegainHealthEvent ev = new EntityRegainHealthEvent(this.getPlayer(), 1, EntityRegainHealthEvent.CAUSE_EATING); + this.getPlayer().heal(ev); + //this.updateFoodExpLevel(3); + } + this.foodTickTimer = 0; + } + } else if (this.getLevel() == 0) { + this.foodTickTimer += tickDiff; + if (this.foodTickTimer >= 80) { + EntityDamageEvent ev = new EntityDamageEvent(this.getPlayer(), DamageCause.VOID, 1); + float now = this.getPlayer().getHealth(); + if (diff == 1) { + if (now > 10) this.getPlayer().attack(ev); + } else if (diff == 2) { + if (now > 1) this.getPlayer().attack(ev); + } else { + this.getPlayer().attack(ev); + } + + this.foodTickTimer = 0; + } + } + if (this.getPlayer().hasEffect(Effect.HUNGER)) { + this.updateFoodExpLevel(0.025); + } } } diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java index 9a12c51abf..af8b134427 100644 --- a/src/main/java/cn/nukkit/Server.java +++ b/src/main/java/cn/nukkit/Server.java @@ -11,13 +11,15 @@ import cn.nukkit.entity.mob.*; import cn.nukkit.entity.passive.*; import cn.nukkit.entity.projectile.EntityArrow; +import cn.nukkit.entity.projectile.EntityEgg; import cn.nukkit.entity.projectile.EntityEnderPearl; import cn.nukkit.entity.projectile.EntitySnowball; import cn.nukkit.event.HandlerList; import cn.nukkit.event.level.LevelInitEvent; import cn.nukkit.event.level.LevelLoadEvent; import cn.nukkit.event.server.QueryRegenerateEvent; -import cn.nukkit.inventory.*; +import cn.nukkit.inventory.CraftingManager; +import cn.nukkit.inventory.Recipe; import cn.nukkit.item.Item; import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.lang.BaseLang; @@ -48,7 +50,10 @@ import cn.nukkit.network.Network; import cn.nukkit.network.RakNetInterface; import cn.nukkit.network.SourceInterface; -import cn.nukkit.network.protocol.*; +import cn.nukkit.network.protocol.BatchPacket; +import cn.nukkit.network.protocol.DataPacket; +import cn.nukkit.network.protocol.PlayerListPacket; +import cn.nukkit.network.protocol.ProtocolInfo; import cn.nukkit.network.query.QueryHandler; import cn.nukkit.network.rcon.RCON; import cn.nukkit.permission.BanEntry; @@ -259,6 +264,7 @@ public class Server { this.properties = new Config(this.dataPath + "server.properties", Config.PROPERTIES, new ConfigSection() { { put("motd", "Nukkit Server For Minecraft: PE"); + put("sub-motd", "Powered by Nukkit"); put("server-port", 19132); put("server-ip", "0.0.0.0"); put("view-distance", 10); @@ -353,6 +359,7 @@ public class Server { this.network = new Network(this); this.network.setName(this.getMotd()); + this.network.setSubName(this.getSubMotd()); this.logger.info(this.getLanguage().translateString("nukkit.server.info", this.getName(), TextFormat.YELLOW + this.getNukkitVersion() + TextFormat.WHITE, TextFormat.AQUA + this.getCodename() + TextFormat.WHITE, this.getApiVersion())); this.logger.info(this.getLanguage().translateString("nukkit.server.license", this.getName())); @@ -426,7 +433,7 @@ public class Server { if (this.getDefaultLevel() == null) { String defaultName = this.getPropertyString("level-name", "world"); - if (defaultName == null || "".equals(defaultName.trim())) { + if (defaultName == null || defaultName.trim().isEmpty()) { this.getLogger().warning("level-name cannot be null, using default"); defaultName = "world"; this.setPropertyString("level-name", defaultName); @@ -639,7 +646,7 @@ public boolean dispatchCommand(CommandSender sender, String commandLine) throws return true; } - sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.notFound")); + sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.unknown", commandLine)); return false; } @@ -810,6 +817,10 @@ public void tickProcessor() { } } + public void onPlayerCompleteLoginSequence(Player player) { + this.sendFullPlayerListData(player); + } + public void onPlayerLogin(Player player) { if (this.sendUsageTicker > 0) { this.uniquePlayers.add(player.getUniqueId()); @@ -823,7 +834,7 @@ public void addPlayer(String identifier, Player player) { public void addOnlinePlayer(Player player) { this.playerList.put(player.getUniqueId(), player); - this.updatePlayerListData(player.getUniqueId(), player.getId(), player.getDisplayName(), player.getSkin()); + this.updatePlayerListData(player.getUniqueId(), player.getId(), player.getDisplayName(), player.getSkin(), player.getLoginChainData().getXUID()); } public void removeOnlinePlayer(Player player) { @@ -839,18 +850,26 @@ public void removeOnlinePlayer(Player player) { } public void updatePlayerListData(UUID uuid, long entityId, String name, Skin skin) { - this.updatePlayerListData(uuid, entityId, name, skin, this.playerList.values()); + this.updatePlayerListData(uuid, entityId, name, skin, "", this.playerList.values()); + } + + public void updatePlayerListData(UUID uuid, long entityId, String name, Skin skin, String xboxUserId) { + this.updatePlayerListData(uuid, entityId, name, skin, xboxUserId, this.playerList.values()); } public void updatePlayerListData(UUID uuid, long entityId, String name, Skin skin, Player[] players) { + this.updatePlayerListData(uuid, entityId, name, skin, "", players); + } + + public void updatePlayerListData(UUID uuid, long entityId, String name, Skin skin, String xboxUserId, Player[] players) { PlayerListPacket pk = new PlayerListPacket(); pk.type = PlayerListPacket.TYPE_ADD; - pk.entries = new PlayerListPacket.Entry[]{new PlayerListPacket.Entry(uuid, entityId, name, skin)}; + pk.entries = new PlayerListPacket.Entry[]{new PlayerListPacket.Entry(uuid, entityId, name, skin, xboxUserId)}; Server.broadcastPacket(players, pk); } - public void updatePlayerListData(UUID uuid, long entityId, String name, Skin skin, Collection players) { - this.updatePlayerListData(uuid, entityId, name, skin, + public void updatePlayerListData(UUID uuid, long entityId, String name, Skin skin, String xboxUserId, Collection players) { + this.updatePlayerListData(uuid, entityId, name, skin, xboxUserId, players.stream() .filter(p -> !p.getUniqueId().equals(uuid)) .toArray(Player[]::new)); @@ -872,39 +891,22 @@ public void removePlayerListData(UUID uuid, Collection players) { } public void sendFullPlayerListData(Player player) { - final UUID uuid = player.getUniqueId(); PlayerListPacket pk = new PlayerListPacket(); pk.type = PlayerListPacket.TYPE_ADD; - pk.entries = this.playerList.values() - .stream() - .filter(p -> !p.getUniqueId().equals(uuid)) + pk.entries = this.playerList.values().stream() .map(p -> new PlayerListPacket.Entry( p.getUniqueId(), p.getId(), p.getDisplayName(), - p.getSkin())) + p.getSkin(), + p.getLoginChainData().getXUID())) .toArray(PlayerListPacket.Entry[]::new); player.dataPacket(pk); } public void sendRecipeList(Player player) { - CraftingDataPacket pk = new CraftingDataPacket(); - pk.cleanRecipes = true; - - for (Recipe recipe : this.getCraftingManager().getRecipes().values()) { - if (recipe instanceof ShapedRecipe) { - pk.addShapedRecipe((ShapedRecipe) recipe); - } else if (recipe instanceof ShapelessRecipe) { - pk.addShapelessRecipe((ShapelessRecipe) recipe); - } - } - - for (FurnaceRecipe recipe : this.getCraftingManager().getFurnaceRecipes().values()) { - pk.addFurnaceRecipe(recipe); - } - - player.dataPacket(pk); + player.dataPacket(CraftingManager.packet); } private void checkTickUpdates(int currentTick, long tickTime) { @@ -1012,6 +1014,7 @@ private boolean tick() { if ((this.tickCounter & 0b1111) == 0) { this.titleTick(); + this.network.resetStatistics(); this.maxTick = 20; this.maxUse = 0; @@ -1080,7 +1083,7 @@ private boolean tick() { // TODO: Fix title tick public void titleTick() { - if (true || !Nukkit.ANSI) { + if (!Nukkit.ANSI) { return; } @@ -1100,8 +1103,6 @@ public void titleTick() { " | Load " + this.getTickUsage() + "%" + (char) 0x07; System.out.print(title); - - this.network.resetStatistics(); } public QueryRegenerateEvent getQueryInformation() { @@ -1192,15 +1193,19 @@ public boolean getForceGamemode() { } public static String getGamemodeString(int mode) { + return getGamemodeString(mode, false); + } + + public static String getGamemodeString(int mode, boolean direct) { switch (mode) { case Player.SURVIVAL: - return "%gameMode.survival"; + return direct ? "Survival" : "%gameMode.survival"; case Player.CREATIVE: - return "%gameMode.creative"; + return direct ? "Creative" : "%gameMode.creative"; case Player.ADVENTURE: - return "%gameMode.adventure"; + return direct ? "Adventure" : "%gameMode.adventure"; case Player.SPECTATOR: - return "%gameMode.spectator"; + return direct ? "Spectator" : "%gameMode.spectator"; } return "UNKNOWN"; } @@ -1288,6 +1293,10 @@ public String getMotd() { return this.getPropertyString("motd", "Nukkit Server For Minecraft: PE"); } + public String getSubMotd() { + return this.getPropertyString("sub-motd", "Powered by Nukkit"); + } + public boolean getForceResources() { return this.getPropertyBoolean("force-resources", false); } @@ -1902,8 +1911,9 @@ public boolean shouldSavePlayerData() { /** * Checks the current thread against the expected primary thread for the server. - * + *

* Note: this method should not be used to indicate the current synchronized state of the runtime. A current thread matching the main thread indicates that it is synchronized, but a mismatch does not preclude the same assumption. + * * @return true if the current thread matches the expected primary thread, false otherwise */ public boolean isPrimaryThread() { @@ -1960,6 +1970,7 @@ private void registerEntities() { Entity.registerEntity("ThrownExpBottle", EntityExpBottle.class); Entity.registerEntity("XpOrb", EntityXPOrb.class); Entity.registerEntity("ThrownPotion", EntityPotion.class); + Entity.registerEntity("Egg", EntityEgg.class); Entity.registerEntity("Human", EntityHuman.class, true); @@ -1986,6 +1997,7 @@ private void registerBlockEntities() { BlockEntity.registerBlockEntity(BlockEntity.COMPARATOR, BlockEntityComparator.class); BlockEntity.registerBlockEntity(BlockEntity.HOPPER, BlockEntityHopper.class); BlockEntity.registerBlockEntity(BlockEntity.BED, BlockEntityBed.class); + BlockEntity.registerBlockEntity(BlockEntity.JUKEBOX, BlockEntityJukebox.class); } public static Server getInstance() { diff --git a/src/main/java/cn/nukkit/api/API.java b/src/main/java/cn/nukkit/api/API.java index e906d2b0b0..70f7369ecc 100644 --- a/src/main/java/cn/nukkit/api/API.java +++ b/src/main/java/cn/nukkit/api/API.java @@ -11,9 +11,9 @@ /** * Describes an API element. * + * @author Lin Mulan, Nukkit Project * @see Usage * @see Definition - * @author Lin Mulan, Nukkit Project */ @Retention(RetentionPolicy.SOURCE) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE}) @@ -21,110 +21,110 @@ @SuppressWarnings("unused") public @interface API { - /** - * Indicates the level of stability of an API element. - * The stability also describes when to use this API element. - * - * @return The stability - * @see Usage - */ - Usage usage(); - - /** - * Indicates definition or the platforms this API element supports. - * - * @return The definition - * @see Definition - */ - Definition definition(); - - /** - * Enum constant for API usage. Indicates when to use this API element. - * - * @see #DEPRECATED - * @see #INCUBATING - * @see #BLEEDING - * @see #EXPERIMENTAL - * @see #MAINTAINED - * @see #STABLE - */ - enum Usage{ - - /** - * Should no longer be used, might disappear in the next minor release. - */ - DEPRECATED, - /** - * Intended for features in drafts. Should only be used for tests. + * Indicates the level of stability of an API element. + * The stability also describes when to use this API element. * - *

Might contains notable new features, but will be moved to a new package before remarking to {@link #BLEEDING}. - * Could be unsafe, might be removed without prior notice. Warnings will be send if used. + * @return The stability + * @see Usage */ - INCUBATING, + Usage usage(); /** - * Intended for features in early development. Should only be used for tests. + * Indicates definition or the platforms this API element supports. * - *

Might be unwrapped, unsafe or have unchecked parameters. - * Further contribution was demanded to enhance, strengthen or simplify before remarking to {@link #EXPERIMENTAL}. - * Might be removed or modified without prior notice. - */ - BLEEDING, - - /** - * Intended for new, experimental features where we are looking for feedback. - * At least stable for development. - * - *

Use with caution, might be remarked to {@link #MAINTAINED} or {@link #STABLE} in the future, - * but also might be removed without prior notice. - */ - EXPERIMENTAL, - - /** - * Intended for features that was tested, documented and at least stable for production use. - * - *

These features will not be modified in a backwards-incompatible way for at least next minor release - * of the current major version. Will be remarked to {@link #DEPRECATED} first if scheduled for removal. - */ - MAINTAINED, - - /** - * Intended for features that was tested, documented and is preferred in production use. - * - *

Will not be changed in a backwards-incompatible way in the current version. - */ - STABLE - } - - /** - * Enum constant for API definition. Indicates which client platform this API element supports. - * - * @see #INTERNAL - * @see #PLATFORM_NATIVE - * @see #UNIVERSAL - */ - enum Definition{ - - /** - * Intended for features should only be used by Nukkit itself. - * Should not be used in production. + * @return The definition + * @see Definition */ - INTERNAL, + Definition definition(); /** - * Intended for features only available on one or several client platforms. + * Enum constant for API usage. Indicates when to use this API element. * - *

By using {@code PLATFORM_NATIVE} features, program will lose some cross-platform features provided. - * Might not available in some client platforms. Read the documents carefully before using this API element. + * @see #DEPRECATED + * @see #INCUBATING + * @see #BLEEDING + * @see #EXPERIMENTAL + * @see #MAINTAINED + * @see #STABLE */ - PLATFORM_NATIVE, + enum Usage { + + /** + * Should no longer be used, might disappear in the next minor release. + */ + DEPRECATED, + + /** + * Intended for features in drafts. Should only be used for tests. + *

+ *

Might contains notable new features, but will be moved to a new package before remarking to {@link #BLEEDING}. + * Could be unsafe, might be removed without prior notice. Warnings will be send if used. + */ + INCUBATING, + + /** + * Intended for features in early development. Should only be used for tests. + *

+ *

Might be unwrapped, unsafe or have unchecked parameters. + * Further contribution was demanded to enhance, strengthen or simplify before remarking to {@link #EXPERIMENTAL}. + * Might be removed or modified without prior notice. + */ + BLEEDING, + + /** + * Intended for new, experimental features where we are looking for feedback. + * At least stable for development. + *

+ *

Use with caution, might be remarked to {@link #MAINTAINED} or {@link #STABLE} in the future, + * but also might be removed without prior notice. + */ + EXPERIMENTAL, + + /** + * Intended for features that was tested, documented and at least stable for production use. + *

+ *

These features will not be modified in a backwards-incompatible way for at least next minor release + * of the current major version. Will be remarked to {@link #DEPRECATED} first if scheduled for removal. + */ + MAINTAINED, + + /** + * Intended for features that was tested, documented and is preferred in production use. + *

+ *

Will not be changed in a backwards-incompatible way in the current version. + */ + STABLE + } /** - * Intended for features implemented in all client platforms. + * Enum constant for API definition. Indicates which client platform this API element supports. * - *

Preferred to use for production use, but sometimes be lack of platform-native features. + * @see #INTERNAL + * @see #PLATFORM_NATIVE + * @see #UNIVERSAL */ - UNIVERSAL - } + enum Definition { + + /** + * Intended for features should only be used by Nukkit itself. + * Should not be used in production. + */ + INTERNAL, + + /** + * Intended for features only available on one or several client platforms. + *

+ *

By using {@code PLATFORM_NATIVE} features, program will lose some cross-platform features provided. + * Might not available in some client platforms. Read the documents carefully before using this API element. + */ + PLATFORM_NATIVE, + + /** + * Intended for features implemented in all client platforms. + *

+ *

Preferred to use for production use, but sometimes be lack of platform-native features. + */ + UNIVERSAL + } } diff --git a/src/main/java/cn/nukkit/block/Block.java b/src/main/java/cn/nukkit/block/Block.java index be476fac00..de6230759d 100644 --- a/src/main/java/cn/nukkit/block/Block.java +++ b/src/main/java/cn/nukkit/block/Block.java @@ -141,7 +141,7 @@ public abstract class Block extends Position implements Metadatable, Cloneable { public static final int CLAY_BLOCK = 82; public static final int REEDS = 83; public static final int SUGARCANE_BLOCK = 83; - + public static final int JUKEBOX = 84; public static final int FENCE = 85; public static final int PUMPKIN = 86; public static final int NETHERRACK = 87; @@ -295,6 +295,11 @@ public abstract class Block extends Position implements Metadatable, Cloneable { public static final int END_ROD = 208; public static final int END_GATEWAY = 209; + public static final int MAGMA = 213; + public static final int BLOCK_NETHER_WART_BLOCK = 214; + public static final int RED_NETHER_BRICK = 215; + public static final int BONE_BLOCK = 216; + public static final int SHULKER_BOX = 218; public static final int PURPLE_GLAZED_TERRACOTTA = 219; public static final int WHITE_GLAZED_TERRACOTTA = 220; @@ -410,7 +415,7 @@ public static void init() { list[REDSTONE_WIRE] = BlockRedstoneWire.class; //55 list[DIAMOND_ORE] = BlockOreDiamond.class; //56 list[DIAMOND_BLOCK] = BlockDiamond.class; //57 - list[WORKBENCH] = BlockWorkbench.class; //58 + list[WORKBENCH] = BlockCraftingTable.class; //58 list[WHEAT_BLOCK] = BlockWheat.class; //59 list[FARMLAND] = BlockFarmland.class; //60 list[FURNACE] = BlockFurnace.class; //61 @@ -436,7 +441,7 @@ public static void init() { list[CACTUS] = BlockCactus.class; //81 list[CLAY_BLOCK] = BlockClay.class; //82 list[SUGARCANE_BLOCK] = BlockSugarcane.class; //83 - + list[JUKEBOX] = BlockJukebox.class; //84 list[FENCE] = BlockFence.class; //85 list[PUMPKIN] = BlockPumpkin.class; //86 list[NETHERRACK] = BlockNetherrack.class; //87 @@ -512,7 +517,7 @@ public static void init() { list[DOUBLE_WOOD_SLAB] = BlockDoubleSlabWood.class; //157 list[WOOD_SLAB] = BlockSlabWood.class; //158 list[STAINED_TERRACOTTA] = BlockTerracottaStained.class; //159 - //TODO: list[STAINED_GLASS_PANE] = BlockGlassPaneStained.class; //160 + list[STAINED_GLASS_PANE] = BlockGlassPaneStained.class; //160 list[LEAVES2] = BlockLeaves2.class; //161 list[WOOD2] = BlockWood2.class; //162 @@ -558,6 +563,8 @@ public static void init() { list[END_ROD] = BlockEndRod.class; //208 list[END_GATEWAY] = BlockEndGateway.class; //209 + list[BONE_BLOCK] = BlockBone.class; //216 + //TODO: list[SHULKER_BOX] = BlockShulkerBox.class; //218 list[PURPLE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedPurple.class; //219 list[WHITE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedWhite.class; //220 @@ -580,6 +587,7 @@ public static void init() { list[CONCRETE_POWDER] = BlockConcretePowder.class; //237 //TODO: list[CHORUS_PLANT] = BlockChorusPlant.class; //240 + list[STAINED_GLASS] = BlockGlassStained.class; //241 list[PODZOL] = BlockPodzol.class; //243 list[BEETROOT_BLOCK] = BlockBeetroot.class; //244 list[GLOWING_OBSIDIAN] = BlockObsidianGlowing.class; //246 @@ -822,21 +830,27 @@ 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; - if(toolType == ItemTool.TYPE_NONE) return 1.0; + if (toolType == ItemTool.TYPE_SWORD) return isCobweb ? 15.0 : 1.0; + if (toolType == ItemTool.TYPE_SHEARS) return isWoolBlock ? 5.0 : 15.0; + if (toolType == ItemTool.TYPE_NONE) return 1.0; switch (toolTier) { - case ItemTool.TIER_WOODEN: return 2.0; - case ItemTool.TIER_STONE: return 4.0; - case ItemTool.TIER_IRON: return 6.0; - case ItemTool.TIER_DIAMOND: return 8.0; - case ItemTool.TIER_GOLD: return 12.0; - default: return 1.0; + case ItemTool.TIER_WOODEN: + return 2.0; + case ItemTool.TIER_STONE: + return 4.0; + case ItemTool.TIER_IRON: + return 6.0; + case ItemTool.TIER_DIAMOND: + return 8.0; + case ItemTool.TIER_GOLD: + return 12.0; + default: + return 1.0; } } private static double speedBonusByEfficiencyLore0(int efficiencyLoreLevel) { - if(efficiencyLoreLevel == 0) return 0; + if (efficiencyLoreLevel == 0) return 0; return efficiencyLoreLevel * efficiencyLoreLevel + 1; } @@ -845,20 +859,20 @@ private static double speedRateByHasteLore0(int hasteLoreLevel) { } private static int toolType0(Item item) { - if(item.isSword()) return ItemTool.TYPE_SWORD ; - if(item.isShovel()) return ItemTool.TYPE_SHOVEL ; - if(item.isPickaxe()) return ItemTool.TYPE_PICKAXE ; - if(item.isAxe()) return ItemTool.TYPE_AXE ; - if(item.isShears()) return ItemTool.TYPE_SHEARS ; + if (item.isSword()) return ItemTool.TYPE_SWORD; + if (item.isShovel()) return ItemTool.TYPE_SHOVEL; + if (item.isPickaxe()) return ItemTool.TYPE_PICKAXE; + if (item.isAxe()) return ItemTool.TYPE_AXE; + if (item.isShears()) return ItemTool.TYPE_SHEARS; return ItemTool.TYPE_NONE; } private static boolean correctTool0(int blockToolType, Item item) { - return (blockToolType == ItemTool.TYPE_SWORD && item.isSword() ) || - (blockToolType == ItemTool.TYPE_SHOVEL && item.isShovel() ) || - (blockToolType == ItemTool.TYPE_PICKAXE && item.isPickaxe() ) || - (blockToolType == ItemTool.TYPE_AXE && item.isAxe() ) || - (blockToolType == ItemTool.TYPE_SHEARS && item.isShears() ) || + return (blockToolType == ItemTool.TYPE_SWORD && item.isSword()) || + (blockToolType == ItemTool.TYPE_SHOVEL && item.isShovel()) || + (blockToolType == ItemTool.TYPE_PICKAXE && item.isPickaxe()) || + (blockToolType == ItemTool.TYPE_AXE && item.isAxe()) || + (blockToolType == ItemTool.TYPE_SHEARS && item.isShears()) || blockToolType == ItemTool.TYPE_NONE; } @@ -869,11 +883,11 @@ private static double breakTime0(double blockHardness, boolean correctTool, bool 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, isWoolBlock, isCobweb); speed += speedBonusByEfficiencyLore0(efficiencyLoreLevel); speed *= speedRateByHasteLore0(hasteEffectLevel); - if(insideOfWaterWithoutAquaAffinity) speed *= 0.2; - if(outOfWaterButNotOnGround) speed *= 0.2; + if (insideOfWaterWithoutAquaAffinity) speed *= 0.2; + if (outOfWaterButNotOnGround) speed *= 0.2; return 1.0 / speed; } diff --git a/src/main/java/cn/nukkit/block/BlockAnvil.java b/src/main/java/cn/nukkit/block/BlockAnvil.java index 7759da99e6..d8a60a5d5a 100644 --- a/src/main/java/cn/nukkit/block/BlockAnvil.java +++ b/src/main/java/cn/nukkit/block/BlockAnvil.java @@ -76,9 +76,9 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl int[] faces = {1, 2, 3, 0}; this.meta = faces[player != null ? player.getDirection().getHorizontalIndex() : 0]; if (damage >= 4 && damage <= 7) { - this.meta |= 0x04; + this.meta |= 0x04; } else if (damage >= 8 && damage <= 11) { - this.meta |= 0x08; + this.meta |= 0x08; } this.getLevel().setBlock(block, this, true); return true; @@ -89,7 +89,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl @Override public boolean onActivate(Item item, Player player) { if (player != null) { - player.addWindow(new AnvilInventory(this)); + player.addWindow(new AnvilInventory(this), Player.ANVIL_WINDOW_ID); } return true; } diff --git a/src/main/java/cn/nukkit/block/BlockBed.java b/src/main/java/cn/nukkit/block/BlockBed.java index 72ffe14a2a..6f84f2bf02 100644 --- a/src/main/java/cn/nukkit/block/BlockBed.java +++ b/src/main/java/cn/nukkit/block/BlockBed.java @@ -5,6 +5,7 @@ import cn.nukkit.blockentity.BlockEntityBed; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBed; +import cn.nukkit.lang.TranslationContainer; import cn.nukkit.level.Level; import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.BlockFace; @@ -77,7 +78,7 @@ public boolean onActivate(Item item, Player player) { boolean isNight = (time >= Level.TIME_NIGHT && time < Level.TIME_SUNRISE); if (player != null && !isNight) { - player.sendMessage(TextFormat.GRAY + "You can only sleep at night"); + player.sendMessage(new TranslationContainer("tile.bed.noSleep")); return true; } @@ -100,7 +101,7 @@ public boolean onActivate(Item item, Player player) { b = blockWest; } else { if (player != null) { - player.sendMessage(TextFormat.GRAY + "This bed is incomplete"); + player.sendMessage(new TranslationContainer("tile.bed.notValid")); } return true; @@ -108,7 +109,7 @@ public boolean onActivate(Item item, Player player) { } if (player != null && !player.sleepOn(b)) { - player.sendMessage(TextFormat.GRAY + "This bed is occupied"); + player.sendMessage(new TranslationContainer("tile.bed.occupied")); } diff --git a/src/main/java/cn/nukkit/block/BlockBone.java b/src/main/java/cn/nukkit/block/BlockBone.java new file mode 100644 index 0000000000..4e28539b40 --- /dev/null +++ b/src/main/java/cn/nukkit/block/BlockBone.java @@ -0,0 +1,53 @@ +package cn.nukkit.block; + +import cn.nukkit.item.Item; +import cn.nukkit.item.ItemBlock; +import cn.nukkit.item.ItemTool; + +/** + * @author CreeperFace + */ +public class BlockBone extends BlockSolid { + + public BlockBone() { + this(0); + } + + public BlockBone(int meta) { + super(meta); + } + + @Override + public int getId() { + return BONE_BLOCK; + } + + @Override + public String getName() { + return "Bone Block"; + } + + @Override + public double getHardness() { + return 2; + } + + @Override + public double getResistance() { + return 10; + } + + @Override + public int getToolType() { + return ItemTool.TYPE_PICKAXE; + } + + @Override + public Item[] getDrops(Item item) { + if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) { + return new Item[]{new ItemBlock(this)}; + } + + return new Item[0]; + } +} diff --git a/src/main/java/cn/nukkit/block/BlockWorkbench.java b/src/main/java/cn/nukkit/block/BlockCraftingTable.java similarity index 81% rename from src/main/java/cn/nukkit/block/BlockWorkbench.java rename to src/main/java/cn/nukkit/block/BlockCraftingTable.java index b5129f79f4..c94c41ca39 100644 --- a/src/main/java/cn/nukkit/block/BlockWorkbench.java +++ b/src/main/java/cn/nukkit/block/BlockCraftingTable.java @@ -1,6 +1,7 @@ package cn.nukkit.block; import cn.nukkit.Player; +import cn.nukkit.inventory.BigCraftingGrid; import cn.nukkit.item.Item; import cn.nukkit.item.ItemTool; import cn.nukkit.utils.BlockColor; @@ -9,12 +10,12 @@ * Created on 2015/12/5 by xtypr. * Package cn.nukkit.block in project Nukkit . */ -public class BlockWorkbench extends BlockSolid { - public BlockWorkbench() { +public class BlockCraftingTable extends BlockSolid { + public BlockCraftingTable() { this(0); } - public BlockWorkbench(int meta) { + public BlockCraftingTable(int meta) { super(meta); } @@ -51,6 +52,7 @@ public int getToolType() { @Override public boolean onActivate(Item item, Player player) { if (player != null) { + player.setCraftingGrid(new BigCraftingGrid(player)); player.craftingType = Player.CRAFTING_BIG; } return true; diff --git a/src/main/java/cn/nukkit/block/BlockEnchantingTable.java b/src/main/java/cn/nukkit/block/BlockEnchantingTable.java index 2619966fe5..d29d132b8a 100644 --- a/src/main/java/cn/nukkit/block/BlockEnchantingTable.java +++ b/src/main/java/cn/nukkit/block/BlockEnchantingTable.java @@ -122,7 +122,7 @@ public boolean onActivate(Item item, Player player) { } } - player.addWindow(new EnchantInventory(this.getLocation())); + player.addWindow(new EnchantInventory(this.getLocation()), Player.ENCHANT_WINDOW_ID); } return true; diff --git a/src/main/java/cn/nukkit/block/BlockEndGateway.java b/src/main/java/cn/nukkit/block/BlockEndGateway.java index 87038f41d7..c87a0ade38 100644 --- a/src/main/java/cn/nukkit/block/BlockEndGateway.java +++ b/src/main/java/cn/nukkit/block/BlockEndGateway.java @@ -4,7 +4,6 @@ import cn.nukkit.utils.BlockColor; /** - * * @author PikyCZ */ public class BlockEndGateway extends BlockSolid { diff --git a/src/main/java/cn/nukkit/block/BlockEndStone.java b/src/main/java/cn/nukkit/block/BlockEndStone.java index d8701c1ed5..ab497aee2e 100644 --- a/src/main/java/cn/nukkit/block/BlockEndStone.java +++ b/src/main/java/cn/nukkit/block/BlockEndStone.java @@ -44,7 +44,7 @@ public int getToolType() { @Override public Item[] getDrops(Item item) { - if (item.isPickaxe() && item.getTier() > ItemTool.TIER_WOODEN) { + if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) { return new Item[]{ toItem() }; diff --git a/src/main/java/cn/nukkit/block/BlockFlowerPot.java b/src/main/java/cn/nukkit/block/BlockFlowerPot.java index 4c8a6c4fbb..1e7648d085 100644 --- a/src/main/java/cn/nukkit/block/BlockFlowerPot.java +++ b/src/main/java/cn/nukkit/block/BlockFlowerPot.java @@ -157,4 +157,9 @@ protected AxisAlignedBB recalculateBoundingBox() { public boolean canPassThrough() { return false; } + + @Override + public Item toItem() { + return new ItemFlowerPot(); + } } diff --git a/src/main/java/cn/nukkit/block/BlockGlassPane.java b/src/main/java/cn/nukkit/block/BlockGlassPane.java index 4da68af165..b2fa9fcd21 100644 --- a/src/main/java/cn/nukkit/block/BlockGlassPane.java +++ b/src/main/java/cn/nukkit/block/BlockGlassPane.java @@ -14,10 +14,9 @@ public BlockGlassPane() { } public BlockGlassPane(int meta) { - super(0); + super(meta); } - @Override public String getName() { return "Glass Pane"; diff --git a/src/main/java/cn/nukkit/block/BlockGlassPaneStained.java b/src/main/java/cn/nukkit/block/BlockGlassPaneStained.java new file mode 100644 index 0000000000..bf3caeba14 --- /dev/null +++ b/src/main/java/cn/nukkit/block/BlockGlassPaneStained.java @@ -0,0 +1,37 @@ +package cn.nukkit.block; + +import cn.nukkit.utils.BlockColor; +import cn.nukkit.utils.DyeColor; + +/** + * Created by CreeperFace on 7.8.2017. + */ +public class BlockGlassPaneStained extends BlockGlassPane { + + public BlockGlassPaneStained() { + this(0); + } + + public BlockGlassPaneStained(int meta) { + super(meta); + } + + @Override + public int getId() { + return STAINED_GLASS_PANE; + } + + @Override + public String getName() { + return getDyeColor().getName() + " stained glass pane"; + } + + @Override + public BlockColor getColor() { + return getDyeColor().getColor(); + } + + public DyeColor getDyeColor() { + return DyeColor.getByWoolData(meta); + } +} diff --git a/src/main/java/cn/nukkit/block/BlockGlassStained.java b/src/main/java/cn/nukkit/block/BlockGlassStained.java new file mode 100644 index 0000000000..a00e545cd1 --- /dev/null +++ b/src/main/java/cn/nukkit/block/BlockGlassStained.java @@ -0,0 +1,37 @@ +package cn.nukkit.block; + +import cn.nukkit.utils.BlockColor; +import cn.nukkit.utils.DyeColor; + +/** + * Created by CreeperFace on 7.8.2017. + */ +public class BlockGlassStained extends BlockGlass { + + public BlockGlassStained() { + this(0); + } + + public BlockGlassStained(int meta) { + super(meta); + } + + @Override + public int getId() { + return STAINED_GLASS; + } + + @Override + public String getName() { + return getDyeColor().getName() + " Stained Glass"; + } + + @Override + public BlockColor getColor() { + return DyeColor.getByWoolData(meta).getColor(); + } + + public DyeColor getDyeColor() { + return DyeColor.getByWoolData(meta); + } +} diff --git a/src/main/java/cn/nukkit/block/BlockGlowstone.java b/src/main/java/cn/nukkit/block/BlockGlowstone.java index b0b623553f..b19c80069a 100644 --- a/src/main/java/cn/nukkit/block/BlockGlowstone.java +++ b/src/main/java/cn/nukkit/block/BlockGlowstone.java @@ -2,8 +2,12 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemGlowstoneDust; +import cn.nukkit.item.enchantment.Enchantment; +import cn.nukkit.math.MathHelper; import cn.nukkit.utils.BlockColor; +import java.util.Random; + /** * Created on 2015/12/6 by xtypr. * Package cn.nukkit.block in project Nukkit . @@ -44,8 +48,16 @@ public int getLightLevel() { @Override public Item[] getDrops(Item item) { + Random random = new Random(); + int count = 2 + random.nextInt(3); + + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + count += random.nextInt(fortune.getLevel() + 1); + } + return new Item[]{ - new ItemGlowstoneDust(0, ((int) (2d * Math.random()) + 2)) + new ItemGlowstoneDust(0, MathHelper.clamp(count, 1, 4)) }; } diff --git a/src/main/java/cn/nukkit/block/BlockJukebox.java b/src/main/java/cn/nukkit/block/BlockJukebox.java new file mode 100644 index 0000000000..419d11d1c7 --- /dev/null +++ b/src/main/java/cn/nukkit/block/BlockJukebox.java @@ -0,0 +1,78 @@ +package cn.nukkit.block; + +import cn.nukkit.Player; +import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityJukebox; +import cn.nukkit.item.Item; +import cn.nukkit.item.ItemRecord; +import cn.nukkit.math.BlockFace; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.ListTag; + +/** + * Created by CreeperFace on 7.8.2017. + */ +public class BlockJukebox extends BlockSolid { + + public BlockJukebox() { + this(0); + } + + public BlockJukebox(int meta) { + super(0); + } + + @Override + public String getName() { + return "Jukebox"; + } + + @Override + public int getId() { + return JUKEBOX; + } + + @Override + public boolean canBeActivated() { + return true; + } + + @Override + public boolean onActivate(Item item, Player player) { + BlockEntity blockEntity = this.getLevel().getBlockEntity(this); + if (blockEntity == null || !(blockEntity instanceof BlockEntityJukebox)) { + blockEntity = this.createBlockEntity(); + } + + BlockEntityJukebox jukebox = (BlockEntityJukebox) blockEntity; + if (jukebox.getRecordItem().getId() != 0) { + jukebox.dropItem(); + } else if (item instanceof ItemRecord) { + jukebox.setRecordItem(item); + jukebox.play(); + } + + return false; + } + + @Override + public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { + if (super.place(item, block, target, face, fx, fy, fz, player)) { + createBlockEntity(); + return true; + } + + return false; + } + + private BlockEntity createBlockEntity() { + CompoundTag nbt = new CompoundTag() + .putList(new ListTag<>("Items")) + .putString("id", BlockEntity.JUKEBOX) + .putInt("x", getFloorX()) + .putInt("y", getFloorY()) + .putInt("z", getFloorZ()); + + return BlockEntity.createBlockEntity(BlockEntity.JUKEBOX, this.level.getChunk(getFloorX() >> 4, getFloorZ() >> 4), nbt); + } +} diff --git a/src/main/java/cn/nukkit/block/BlockLever.java b/src/main/java/cn/nukkit/block/BlockLever.java index e1ab5b3ac7..498ed4c695 100644 --- a/src/main/java/cn/nukkit/block/BlockLever.java +++ b/src/main/java/cn/nukkit/block/BlockLever.java @@ -83,7 +83,7 @@ public int onUpdate(int type) { @Override public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) { - if (!target.isTransparent() && target.isSolid()) { + if (target.isNormalBlock()) { this.meta = LeverOrientation.forFacings(face, player.getHorizontalFacing()).getMetadata(); this.getLevel().setBlock(block, this, true, true); return true; diff --git a/src/main/java/cn/nukkit/block/BlockMelon.java b/src/main/java/cn/nukkit/block/BlockMelon.java index 0f8e41c513..8b227cdb9f 100644 --- a/src/main/java/cn/nukkit/block/BlockMelon.java +++ b/src/main/java/cn/nukkit/block/BlockMelon.java @@ -3,6 +3,7 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemMelon; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.utils.BlockColor; import java.util.Random; @@ -42,8 +43,16 @@ public double getResistance() { @Override public Item[] getDrops(Item item) { + Random random = new Random(); + int count = 3 + random.nextInt(5); + + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + count += random.nextInt(fortune.getLevel() + 1); + } + return new Item[]{ - new ItemMelon(0, new Random().nextInt(4) + 3) + new ItemMelon(0, Math.min(9, count)) }; } diff --git a/src/main/java/cn/nukkit/block/BlockOreCoal.java b/src/main/java/cn/nukkit/block/BlockOreCoal.java index c03f69ba62..56012c2d25 100644 --- a/src/main/java/cn/nukkit/block/BlockOreCoal.java +++ b/src/main/java/cn/nukkit/block/BlockOreCoal.java @@ -3,8 +3,11 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemCoal; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.math.NukkitRandom; +import java.util.concurrent.ThreadLocalRandom; + /** * author: MagicDroidX * Nukkit Project @@ -47,8 +50,20 @@ public String getName() { @Override public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) { + int count = 1; + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + int i = ThreadLocalRandom.current().nextInt(fortune.getLevel() + 2) - 1; + + if (i < 0) { + i = 0; + } + + count = i + 1; + } + return new Item[]{ - new ItemCoal() + new ItemCoal(0, count) }; } else { return new Item[0]; diff --git a/src/main/java/cn/nukkit/block/BlockOreDiamond.java b/src/main/java/cn/nukkit/block/BlockOreDiamond.java index ec06031fef..31dcaf22b4 100644 --- a/src/main/java/cn/nukkit/block/BlockOreDiamond.java +++ b/src/main/java/cn/nukkit/block/BlockOreDiamond.java @@ -3,8 +3,11 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemDiamond; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.math.NukkitRandom; +import java.util.concurrent.ThreadLocalRandom; + /** * author: MagicDroidX * Nukkit Project @@ -48,8 +51,20 @@ public String getName() { @Override public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) { + int count = 1; + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + int i = ThreadLocalRandom.current().nextInt(fortune.getLevel() + 2) - 1; + + if (i < 0) { + i = 0; + } + + count = i + 1; + } + return new Item[]{ - new ItemDiamond() + new ItemDiamond(0, count) }; } else { return new Item[0]; diff --git a/src/main/java/cn/nukkit/block/BlockOreEmerald.java b/src/main/java/cn/nukkit/block/BlockOreEmerald.java index c0afbd0d9e..1e81d7d144 100644 --- a/src/main/java/cn/nukkit/block/BlockOreEmerald.java +++ b/src/main/java/cn/nukkit/block/BlockOreEmerald.java @@ -3,8 +3,11 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemEmerald; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.math.NukkitRandom; +import java.util.concurrent.ThreadLocalRandom; + /** * Created on 2015/12/1 by xtypr. * Package cn.nukkit.block in project Nukkit . @@ -47,8 +50,20 @@ public double getResistance() { @Override public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) { + int count = 1; + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + int i = ThreadLocalRandom.current().nextInt(fortune.getLevel() + 2) - 1; + + if (i < 0) { + i = 0; + } + + count = i + 1; + } + return new Item[]{ - new ItemEmerald() + new ItemEmerald(0, count) }; } else { return new Item[0]; diff --git a/src/main/java/cn/nukkit/block/BlockOreGold.java b/src/main/java/cn/nukkit/block/BlockOreGold.java index e46c15d5ea..c3d1d47a8a 100644 --- a/src/main/java/cn/nukkit/block/BlockOreGold.java +++ b/src/main/java/cn/nukkit/block/BlockOreGold.java @@ -46,7 +46,7 @@ public String getName() { public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) { return new Item[]{ - toItem() + Item.get(GOLD_ORE) }; } else { return new Item[0]; diff --git a/src/main/java/cn/nukkit/block/BlockOreIron.java b/src/main/java/cn/nukkit/block/BlockOreIron.java index 057c632e51..87e62af814 100644 --- a/src/main/java/cn/nukkit/block/BlockOreIron.java +++ b/src/main/java/cn/nukkit/block/BlockOreIron.java @@ -47,7 +47,7 @@ public String getName() { public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_STONE) { return new Item[]{ - toItem() + Item.get(IRON_ORE) }; } else { return new Item[0]; diff --git a/src/main/java/cn/nukkit/block/BlockOreLapis.java b/src/main/java/cn/nukkit/block/BlockOreLapis.java index 6fc943a0de..23b6957d47 100644 --- a/src/main/java/cn/nukkit/block/BlockOreLapis.java +++ b/src/main/java/cn/nukkit/block/BlockOreLapis.java @@ -3,9 +3,11 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemDye; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.math.NukkitRandom; import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; /** * author: MagicDroidX @@ -50,6 +52,18 @@ public String getName() { @Override public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_STONE) { + int count = 4 + ThreadLocalRandom.current().nextInt(5); + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + int i = ThreadLocalRandom.current().nextInt(fortune.getLevel() + 2) - 1; + + if (i < 0) { + i = 0; + } + + count *= (i + 1); + } + return new Item[]{ new ItemDye(4, new Random().nextInt(4) + 4) }; diff --git a/src/main/java/cn/nukkit/block/BlockOreQuartz.java b/src/main/java/cn/nukkit/block/BlockOreQuartz.java index 2fede06996..135eab5fa1 100644 --- a/src/main/java/cn/nukkit/block/BlockOreQuartz.java +++ b/src/main/java/cn/nukkit/block/BlockOreQuartz.java @@ -3,8 +3,11 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemQuartz; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.math.NukkitRandom; +import java.util.concurrent.ThreadLocalRandom; + /** * Created on 2015/12/26 by xtypr. * Package cn.nukkit.block in project Nukkit . @@ -47,8 +50,20 @@ public int getToolType() { @Override public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) { + int count = 1; + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + int i = ThreadLocalRandom.current().nextInt(fortune.getLevel() + 2) - 1; + + if (i < 0) { + i = 0; + } + + count = i + 1; + } + return new Item[]{ - new ItemQuartz() + new ItemQuartz(0, count) }; } else { return new Item[0]; diff --git a/src/main/java/cn/nukkit/block/BlockOreRedstone.java b/src/main/java/cn/nukkit/block/BlockOreRedstone.java index 0c3cd1cbe7..0357dfd6dd 100644 --- a/src/main/java/cn/nukkit/block/BlockOreRedstone.java +++ b/src/main/java/cn/nukkit/block/BlockOreRedstone.java @@ -3,6 +3,7 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemRedstone; import cn.nukkit.item.ItemTool; +import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.level.Level; import cn.nukkit.math.NukkitRandom; @@ -50,8 +51,15 @@ public String getName() { @Override public Item[] getDrops(Item item) { if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) { + int count = new Random().nextInt(2) + 4; + + Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING); + if (fortune != null && fortune.getLevel() >= 1) { + count += new Random().nextInt(fortune.getLevel() + 1); + } + return new Item[]{ - new ItemRedstone(0, new Random().nextInt(1) + 4) + new ItemRedstone(0, count) }; } else { return new Item[0]; diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java index 06de9688b5..b1c0d16383 100644 --- a/src/main/java/cn/nukkit/block/BlockPistonBase.java +++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java @@ -1,12 +1,16 @@ package cn.nukkit.block; import cn.nukkit.Player; +import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.blockentity.BlockEntityPistonArm; +import cn.nukkit.event.block.BlockPistonChangeEvent; import cn.nukkit.item.Item; import cn.nukkit.level.Level; import cn.nukkit.level.sound.PistonInSound; import cn.nukkit.level.sound.PistonOutSound; import cn.nukkit.math.BlockFace; import cn.nukkit.math.Vector3; +import cn.nukkit.nbt.tag.CompoundTag; import java.util.ArrayList; import java.util.List; @@ -53,14 +57,14 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl } this.level.setBlock(block, this, true, false); - /*CompoundTag nbt = new CompoundTag("") + CompoundTag nbt = new CompoundTag("") .putString("id", BlockEntity.PISTON_ARM) .putInt("x", (int) this.x) .putInt("y", (int) this.y) .putInt("z", (int) this.z) - .putBoolean("Sticky", this.sticky);*/ + .putBoolean("Sticky", this.sticky); - //BlockEntityPistonArm be = new BlockEntityPistonArm(this.level.getChunk((int) this.x >> 4, (int) this.z >> 4), nbt); bug? + BlockEntityPistonArm be = new BlockEntityPistonArm(this.level.getChunk((int) this.x >> 4, (int) this.z >> 4), nbt); //this.checkState(); return true; @@ -86,12 +90,24 @@ public boolean isExtended() { @Override public int onUpdate(int type) { - if (type == Level.BLOCK_UPDATE_REDSTONE || type == Level.BLOCK_UPDATE_NORMAL) { - //checkState(); + if (type != 6 && type != 1) { + 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(); + } + } + } + return type; } - - return 0; } private void checkState() { diff --git a/src/main/java/cn/nukkit/block/BlockPlanks.java b/src/main/java/cn/nukkit/block/BlockPlanks.java index 7d5d79c8a0..be8eda1a9e 100644 --- a/src/main/java/cn/nukkit/block/BlockPlanks.java +++ b/src/main/java/cn/nukkit/block/BlockPlanks.java @@ -21,7 +21,7 @@ public BlockPlanks() { } public BlockPlanks(int meta) { - super(meta); + super(meta % 6); } @Override diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java b/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java index 6c6a2c9443..c19e296fbe 100644 --- a/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java +++ b/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java @@ -1,7 +1,12 @@ package cn.nukkit.block; +import cn.nukkit.Player; import cn.nukkit.entity.Entity; +import cn.nukkit.event.Event; import cn.nukkit.event.block.BlockRedstoneEvent; +import cn.nukkit.event.entity.EntityInteractEvent; +import cn.nukkit.event.player.PlayerInteractEvent; +import cn.nukkit.event.player.PlayerInteractEvent.Action; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; import cn.nukkit.level.Level; @@ -92,7 +97,19 @@ public void onEntityCollide(Entity entity) { int power = getRedstonePower(); if (power == 0) { - updateState(power); + Event ev; + + if (entity instanceof Player) { + ev = new PlayerInteractEvent((Player) entity, null, this, null, Action.PHYSICAL); + } else { + ev = new EntityInteractEvent(entity, this); + } + + this.level.getServer().getPluginManager().callEvent(ev); + + if (!ev.isCancelled()) { + updateState(power); + } } } diff --git a/src/main/java/cn/nukkit/block/BlockRail.java b/src/main/java/cn/nukkit/block/BlockRail.java index 9fa3336685..54119ae2e6 100644 --- a/src/main/java/cn/nukkit/block/BlockRail.java +++ b/src/main/java/cn/nukkit/block/BlockRail.java @@ -4,12 +4,18 @@ import cn.nukkit.item.Item; import cn.nukkit.item.ItemTool; import cn.nukkit.level.Level; +import cn.nukkit.math.AxisAlignedBB; import cn.nukkit.math.BlockFace; -import cn.nukkit.math.Vector3; import cn.nukkit.utils.BlockColor; +import cn.nukkit.utils.Rail; +import cn.nukkit.utils.Rail.Orientation; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static cn.nukkit.math.BlockFace.*; +import static cn.nukkit.utils.Rail.Orientation.*; /** * Created by Snake1999 on 2016/1/11. @@ -17,6 +23,11 @@ */ public class BlockRail extends BlockFlowable { + // 0x8: Set the block active + // 0x7: Reset the block to normal + // If the rail can be powered. So its a complex rail! + protected boolean canBePowered = false; + public BlockRail() { this(0); } @@ -58,7 +69,8 @@ public int getToolType() { @Override public int onUpdate(int type) { if (type == Level.BLOCK_UPDATE_NORMAL) { - if (this.down().isTransparent()) { + Optional ascendingDirection = this.getOrientation().ascendingDirection(); + if (this.down().isTransparent() || (ascendingDirection.isPresent() && this.getSide(ascendingDirection.get()).isTransparent())) { this.getLevel().useBreakOn(this); return Level.BLOCK_UPDATE_NORMAL; } @@ -66,161 +78,182 @@ public int onUpdate(int type) { return 0; } + @Override + public AxisAlignedBB recalculateBoundingBox() { + return new AxisAlignedBB( + this.x, + this.y, + this.z, + this.x + 1, + this.y + 0.125D, + this.z + 1); + } + @Override public BlockColor getColor() { return BlockColor.AIR_BLOCK_COLOR; } + //Information from http://minecraft.gamepedia.com/Rail @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) return false; - if (down.isTransparent()) return false; - int[][] arrayXZ = new int[][]{{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; - int[] arrayY = new int[]{0, 1, -1}; - List connected = new ArrayList<>(); - for (int[] xz : arrayXZ) { - int x = xz[0]; - int z = xz[1]; - for (int y : arrayY) { - Vector3 v3 = new Vector3(x, y, z).add(this); - Block v3block = this.getLevel().getBlock(v3); - if (v3block == null) continue; - if (!isRailBlock(v3block.getId()) || !isValidRailMeta(v3block.getDamage())) continue; - if (!(v3block instanceof BlockRail)) continue; - this.connectRail(v3block); - connected.add(v3block); - } - if (connected.size() >= 2) break; + if (down == null || down.isTransparent()) { + return false; } - - if (connected.size() == 1) { - Vector3 v3 = connected.get(0).subtract(this); - this.meta = (v3.y != 1) ? (v3.x == 0 ? 0 : 1) : (int) (v3.z == 0 ? (v3.x / -2) + 2.5 : (v3.z / 2) + 4.5); - } else if (connected.size() == 2) { - Vector3[] subtract = new Vector3[2]; - for (int i = 0; i < connected.size(); i++) { - subtract[i] = connected.get(i).subtract(this); + Map railsAround = this.checkRailsAroundAffected(); + List rails = new ArrayList<>(railsAround.keySet()); + List faces = new ArrayList<>(railsAround.values()); + if (railsAround.size() == 1) { + BlockRail other = rails.get(0); + this.meta = this.connect(other, railsAround.get(other)).metadata(); + } else if (railsAround.size() == 4) { + if (this.isAbstract()) { + this.meta = this.connect(rails.get(faces.indexOf(SOUTH)), SOUTH, rails.get(faces.indexOf(EAST)), EAST).metadata(); + } else { + this.meta = this.connect(rails.get(faces.indexOf(EAST)), EAST, rails.get(faces.indexOf(WEST)), WEST).metadata(); } - if (Math.abs(subtract[0].x) == Math.abs(subtract[1].z) && Math.abs(subtract[1].x) == Math.abs(subtract[0].z)) { - Vector3 v3 = connected.get(0).subtract(this).add(connected.get(1).subtract(this)); - this.meta = v3.x == 1 ? (v3.z == 1 ? 6 : 9) : (v3.z == 1 ? 7 : 8); - } else if (subtract[0].y == 1 || subtract[1].y == 1) { - Vector3 v3 = subtract[0].y == 1 ? subtract[0] : subtract[1]; - this.meta = v3.x == 0 ? (v3.z == -1 ? 4 : 5) : (v3.x == 1 ? 2 : 3); + } else if (!railsAround.isEmpty()) { + if (this.isAbstract()) { + if (railsAround.size() == 2) { + BlockRail rail1 = rails.get(0); + BlockRail rail2 = rails.get(1); + this.meta = this.connect(rail1, railsAround.get(rail1), rail2, railsAround.get(rail2)).metadata(); + } else { + List cd = Stream.of(CURVED_SOUTH_EAST, CURVED_NORTH_EAST, CURVED_SOUTH_WEST) + .filter(o -> o.connectingDirections().stream().allMatch(faces::contains)) + .findFirst().get().connectingDirections(); + BlockFace f1 = cd.get(0); + BlockFace f2 = cd.get(1); + this.meta = this.connect(rails.get(faces.indexOf(f1)), f1, rails.get(faces.indexOf(f2)), f2).metadata(); + } } else { - this.meta = subtract[0].x == 0 ? 0 : 1; + BlockFace f = faces.stream() + .sorted((f1, f2) -> (f1.getIndex() < f2.getIndex()) ? 1 : ((x == y) ? 0 : -1)) + .findFirst().get(); + BlockFace fo = f.getOpposite(); + if (faces.contains(fo)) { //Opposite connectable + this.meta = this.connect(rails.get(faces.indexOf(f)), f, rails.get(faces.indexOf(fo)), fo).metadata(); + } else { + this.meta = this.connect(rails.get(faces.indexOf(f)), f).metadata(); + } } } - this.getLevel().setBlock(this, Block.get(this.getId(), this.getDamage()), true, true); + this.level.setBlock(this, this, true, true); + if (!isAbstract()) { + level.scheduleUpdate(this, this, 0); + } return true; } - /************ Rail Connecting Part ***********/ - /**** - * todo: too complex, need to simplify - ****/ + private Orientation connect(BlockRail rail1, BlockFace face1, BlockRail rail2, BlockFace face2) { + this.connect(rail1, face1); + this.connect(rail2, face2); - protected Vector3[] canConnectRail(Block block) { - if (!(block instanceof BlockRail)) return null; - if (this.distanceSquared(block) > 2) return null; - Vector3[] result = checkRail(this); - if (result.length == 2) return null; - return result; - } + if (face1.getOpposite() == face2) { + int delta1 = (int) (this.y - rail1.y); + int delta2 = (int) (this.y - rail2.y); - protected void connectRail(Block rail) { - Vector3[] connected = canConnectRail(rail); - if (connected == null) return; - if (connected.length == 1) { - Vector3 v3 = connected[0].subtract(this); - this.meta = (v3.y != 1) ? (v3.x == 0 ? 0 : 1) : (int) (v3.z == 0 ? (v3.x / -2) + 2.5 : (v3.z / 2) + 4.5); - } else if (connected.length == 2) { - Vector3[] subtract = new Vector3[2]; - for (int i = 0; i < connected.length; i++) { - subtract[i] = connected[i].subtract(this); - } - if (Math.abs(subtract[0].x) == Math.abs(subtract[1].z) && Math.abs(subtract[1].x) == Math.abs(subtract[0].z)) { - Vector3 v3 = connected[0].subtract(this).add(connected[1].subtract(this)); - this.meta = v3.x == 1 ? (v3.z == 1 ? 6 : 9) : (v3.z == 1 ? 7 : 8); - } else if (subtract[0].y == 1 || subtract[1].y == 1) { - Vector3 v3 = subtract[0].y == 1 ? subtract[0] : subtract[1]; - this.meta = v3.x == 0 ? (v3.z == -1 ? 4 : 5) : (v3.x == 1 ? 2 : 3); - } else { - this.meta = subtract[0].x == 0 ? 0 : 1; + if (delta1 == -1) { + return Orientation.ascending(face1); + } else if (delta2 == -1) { + return Orientation.ascending(face2); } } - this.getLevel().setBlock(this, Block.get(this.getId(), this.getDamage()), true, true); - } - - protected static Vector3[] checkRail(Block rail) { - if (!(rail instanceof BlockRail)) return null; - int damage = rail.getDamage(); - if (damage < 0 || damage > 10) return null; - int[][][] delta = new int[][][]{ - {{0, 1}, {0, -1}}, - {{1, 0}, {-1, 0}}, - {{1, 0}, {-1, 0}}, - {{1, 0}, {-1, 0}}, - {{0, 1}, {0, -1}}, - {{0, 1}, {0, -1}}, - {{1, 0}, {0, 1}}, - {{0, 1}, {-1, 0}}, - {{-1, 0}, {0, -1}}, - {{0, -1}, {1, 0}} - }; - int[] deltaY = new int[]{0, 1, -1}; - int[][] blocks = delta[damage]; - List connected = new ArrayList<>(); - for (int y : deltaY) { - Vector3 v3 = new Vector3( - rail.getFloorX() + blocks[0][0], - rail.getFloorY() + y, - rail.getFloorZ() + blocks[0][1] - ); - int idToConnect = rail.getLevel().getBlockIdAt(v3.getFloorX(), v3.getFloorY(), v3.getFloorZ()); - int metaToConnect = rail.getLevel().getBlockDataAt(v3.getFloorX(), v3.getFloorY(), v3.getFloorZ()); - if (!isRailBlock(idToConnect) || !isValidRailMeta(metaToConnect)) continue; - int xDiff = damage - v3.getFloorX(); - int zDiff = damage - v3.getFloorZ(); - for (int[] xz : blocks) { - if (xz[0] != xDiff || xz[1] != zDiff) continue; - connected.add(v3); + return straightOrCurved(face1, face2); + } + + private Orientation connect(BlockRail other, BlockFace face) { + int delta = (int) (this.y - other.y); + Map rails = other.checkRailsConnected(); + if (rails.isEmpty()) { //Only one + other.setOrientation(delta == 1 ? ascending(face.getOpposite()) : straight(face)); + return delta == -1 ? ascending(face) : straight(face); + } else if (rails.size() == 1) { //Already connected + BlockFace faceConnected = rails.values().iterator().next(); + + if (other.isAbstract() && faceConnected != face) { //Curve! + other.setOrientation(curved(face.getOpposite(), faceConnected)); + return delta == -1 ? ascending(face) : straight(face); + } else if (faceConnected == face) { //Turn! + if (!other.getOrientation().isAscending()) { + other.setOrientation(delta == 1 ? ascending(face.getOpposite()) : straight(face)); + } + return delta == -1 ? ascending(face) : straight(face); + } else if (other.getOrientation().hasConnectingDirections(NORTH, SOUTH)) { //North-south + other.setOrientation(delta == 1 ? ascending(face.getOpposite()) : straight(face)); + return delta == -1 ? ascending(face) : straight(face); } } - for (int y : deltaY) { - Vector3 v3 = new Vector3( - rail.getFloorX() + blocks[1][0], - rail.getFloorY() + y, - rail.getFloorZ() + blocks[1][1] - ); - int idToConnect = rail.getLevel().getBlockIdAt(v3.getFloorX(), v3.getFloorY(), v3.getFloorZ()); - int metaToConnect = rail.getLevel().getBlockDataAt(v3.getFloorX(), v3.getFloorY(), v3.getFloorZ()); - if (!isRailBlock(idToConnect) || !isValidRailMeta(metaToConnect)) continue; - int xDiff = damage - v3.getFloorX(); - int zDiff = damage - v3.getFloorZ(); - for (int[] xz : blocks) { - if (xz[0] != xDiff || xz[1] != zDiff) continue; - connected.add(v3); - } + return STRAIGHT_NORTH_SOUTH; + } + + private Map checkRailsAroundAffected() { + Map railsAround = this.checkRailsAround(Arrays.asList(SOUTH, EAST, WEST, NORTH)); + return railsAround.keySet().stream() + .filter(r -> r.checkRailsConnected().size() != 2) + .collect(Collectors.toMap(r -> r, railsAround::get)); + } + + private Map checkRailsAround(Collection faces) { + Map result = new HashMap<>(); + faces.forEach(f -> { + Block b = this.getSide(f); + Stream.of(b, b.up(), b.down()) + .filter(Rail::isRailBlock) + .forEach(block -> result.put((BlockRail) block, f)); + }); + return result; + } + + protected Map checkRailsConnected() { + Map railsAround = this.checkRailsAround(this.getOrientation().connectingDirections()); + return railsAround.keySet().stream() + .filter(r -> r.getOrientation().hasConnectingDirections(railsAround.get(r).getOpposite())) + .collect(Collectors.toMap(r -> r, railsAround::get)); + } + + public boolean isAbstract() { + return this.getId() == RAIL; + } + + public boolean canPowered() { + return this.canBePowered; + } + + public Orientation getOrientation() { + return byMetadata(this.getRealMeta()); + } + + public void setOrientation(Orientation o) { + if (o.metadata() != this.getRealMeta()) { + this.setDamage(o.metadata()); + this.level.setBlock(this, this, true, true); } - return connected.toArray(new Vector3[connected.size()]); - } - - protected static boolean isRailBlock(int id) { - switch (id) { - case RAIL: - case POWERED_RAIL: - case ACTIVATOR_RAIL: - case DETECTOR_RAIL: - return true; - default: - return false; + } + + public int getRealMeta() { + // Check if this can be powered + // Avoid modifying the value from meta (The rail orientation may be false) + // Reason: When the rail is curved, the meta will return STRAIGHT_NORTH_SOUTH. + // OR Null Pointer Exception + if (!isAbstract()) { + return getDamage() & 0x7; } + // Return the default: This meta + return getDamage(); } - protected static boolean isValidRailMeta(int meta) { - return !(meta < 0 || meta > 10); + public boolean isActive() { + return (getDamage() & 0x8) != 0; } -} \ No newline at end of file + public void setActive(boolean active) { + if (active) { + setDamage(getDamage() | 0x8); + } else { + setDamage(getDamage() & 0x7); + } + level.setBlock(this, this, true, true); + } +} diff --git a/src/main/java/cn/nukkit/block/BlockRailActivator.java b/src/main/java/cn/nukkit/block/BlockRailActivator.java index 020f84b704..e0e91bf35f 100644 --- a/src/main/java/cn/nukkit/block/BlockRailActivator.java +++ b/src/main/java/cn/nukkit/block/BlockRailActivator.java @@ -1,5 +1,9 @@ package cn.nukkit.block; +import cn.nukkit.level.Level; +import cn.nukkit.math.Vector3; +import cn.nukkit.utils.Rail; + /** * @author Nukkit Project Team */ @@ -11,6 +15,7 @@ public BlockRailActivator(int meta) { public BlockRailActivator() { this(0); + canBePowered = true; } @Override @@ -22,4 +27,142 @@ public String getName() { public int getId() { return ACTIVATOR_RAIL; } + + @Override + public int onUpdate(int type) { + if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE || type == Level.BLOCK_UPDATE_SCHEDULED) { + super.onUpdate(type); + boolean wasPowered = isActive(); + boolean isPowered = level.isBlockPowered(this) + || checkSurrounding(this, true, 0) + || checkSurrounding(this, false, 0); + boolean hasUpdate = false; + + if (wasPowered != isPowered) { + setActive(isPowered); + hasUpdate = true; + } + + if (hasUpdate) { + level.updateAround(down()); + if (getOrientation().isAscending()) { + level.updateAround(up()); + } + } + return type; + } + return 0; + } + + /** + * Check the surrounding of the rail + * + * @param pos The rail position + * @param relative The relative of the rail that will be checked + * @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) { + if (power >= 8) { + return false; + } + int dx = pos.getFloorX(); + int dy = pos.getFloorY(); + int dz = pos.getFloorZ(); + + BlockRail block; + Block block2 = level.getBlock(new Vector3(dx, dy, dz)); + + if (Rail.isRailBlock(block2)) { + block = (BlockRail) block2; + } else { + return false; + } + + Rail.Orientation base = null; + boolean onStraight = true; + + switch (block.getOrientation()) { + case STRAIGHT_NORTH_SOUTH: + if (relative) { + dz++; + } else { + dz--; + } + break; + case STRAIGHT_EAST_WEST: + if (relative) { + dx--; + } else { + dx++; + } + break; + case ASCENDING_EAST: + if (relative) { + dx--; + } else { + dx++; + dy++; + onStraight = false; + } + base = Rail.Orientation.STRAIGHT_EAST_WEST; + break; + case ASCENDING_WEST: + if (relative) { + dx--; + dy++; + onStraight = false; + } else { + dx++; + } + base = Rail.Orientation.STRAIGHT_EAST_WEST; + break; + case ASCENDING_NORTH: + if (relative) { + dz++; + } else { + dz--; + dy++; + onStraight = false; + } + base = Rail.Orientation.STRAIGHT_NORTH_SOUTH; + break; + case ASCENDING_SOUTH: + if (relative) { + dz++; + dy++; + onStraight = false; + } else { + dz--; + } + base = Rail.Orientation.STRAIGHT_NORTH_SOUTH; + break; + default: + return false; + } + + return canPowered(new Vector3(dx, dy, dz), base, power, relative) + || onStraight && canPowered(new Vector3(dx, dy - 1, dz), base, power, relative); + } + + protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boolean relative) { + Block block = level.getBlock(pos); + + if (!(block instanceof BlockRailActivator)) { + return false; + } + + Rail.Orientation base = ((BlockRailActivator) block).getOrientation(); + + return (state != Rail.Orientation.STRAIGHT_EAST_WEST + || base != Rail.Orientation.STRAIGHT_NORTH_SOUTH + && base != Rail.Orientation.ASCENDING_NORTH + && base != Rail.Orientation.ASCENDING_SOUTH) + && (state != Rail.Orientation.STRAIGHT_NORTH_SOUTH + || base != Rail.Orientation.STRAIGHT_EAST_WEST + && base != Rail.Orientation.ASCENDING_EAST + && base != Rail.Orientation.ASCENDING_WEST) + && (level.isBlockPowered(pos) || checkSurrounding(pos, relative, power + 1)); + } + } diff --git a/src/main/java/cn/nukkit/block/BlockRailDetector.java b/src/main/java/cn/nukkit/block/BlockRailDetector.java index 929acc68c7..83818621d2 100644 --- a/src/main/java/cn/nukkit/block/BlockRailDetector.java +++ b/src/main/java/cn/nukkit/block/BlockRailDetector.java @@ -1,12 +1,24 @@ package cn.nukkit.block; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityMinecartAbstract; +import cn.nukkit.level.Level; +import cn.nukkit.math.AxisAlignedBB; +import cn.nukkit.math.BlockFace; + /** * Created on 2015/11/22 by CreeperFace. - * Package cn.nukkit.block in project Nukkit . + * Contributed by: larryTheCoder on 2017/7/8. + *

+ * Nukkit Project, + * Minecart and Riding Project, + * Package cn.nukkit.block in project Nukkit. */ public class BlockRailDetector extends BlockRail { + public BlockRailDetector() { this(0); + canBePowered = true; } public BlockRailDetector(int meta) { @@ -22,4 +34,64 @@ public int getId() { public String getName() { return "Detector Rail"; } + + @Override + public boolean isPowerSource() { + return true; + } + + @Override + public int getWeakPower(BlockFace side) { + return isActive() ? 15 : 0; + } + + @Override + public int getStrongPower(BlockFace side) { + return isActive() ? 0 : (side == BlockFace.UP ? 15 : 0); + } + + @Override + public int onUpdate(int type) { + if (type == Level.BLOCK_UPDATE_SCHEDULED) { + updateState(); + return type; + } + return super.onUpdate(type); + } + + @Override + public void onEntityCollide(Entity entity) { + updateState(); + } + + protected void updateState() { + boolean wasPowered = isActive(); + boolean isPowered = false; + + for (Entity entity : level.getNearbyEntities(new AxisAlignedBB( + getFloorX() + 0.125D, + getFloorY(), + getFloorZ() + 0.125D, + getFloorX() + 0.875D, + getFloorY() + 0.525D, + getFloorZ() + 0.875D))) { + if (entity instanceof EntityMinecartAbstract) { + isPowered = true; + } + } + + if (isPowered && !wasPowered) { + setActive(true); + level.scheduleUpdate(this, this, 0); + level.scheduleUpdate(this, this.down(), 0); + } + + if (!isPowered && wasPowered) { + setActive(false); + level.scheduleUpdate(this, this, 0); + level.scheduleUpdate(this, this.down(), 0); + } + + level.updateComparatorOutputLevel(this); + } } diff --git a/src/main/java/cn/nukkit/block/BlockRailPowered.java b/src/main/java/cn/nukkit/block/BlockRailPowered.java index dab96d3bde..3350d60728 100644 --- a/src/main/java/cn/nukkit/block/BlockRailPowered.java +++ b/src/main/java/cn/nukkit/block/BlockRailPowered.java @@ -1,12 +1,22 @@ package cn.nukkit.block; +import cn.nukkit.level.Level; +import cn.nukkit.math.Vector3; +import cn.nukkit.utils.Rail; + /** * Created by Snake1999 on 2016/1/11. - * Package cn.nukkit.block in project nukkit + * Contributed by: larryTheCoder on 2017/7/18. + *

+ * Nukkit Project, + * Minecart and Riding Project, + * Package cn.nukkit.block in project Nukkit. */ public class BlockRailPowered extends BlockRail { + public BlockRailPowered() { this(0); + canBePowered = true; } public BlockRailPowered(int meta) { @@ -22,4 +32,151 @@ public int getId() { public String getName() { return "Powered Rail"; } + + @Override + public int onUpdate(int type) { + // Warning: I din't recommended this on slow networks server or slow client + // Network below 86Kb/s. This will became unresponsive to clients + // When updating the block state. Espicially on the world with many rails. + // Trust me, I tested this on my server. + if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE || type == Level.BLOCK_UPDATE_SCHEDULED) { + super.onUpdate(type); + boolean wasPowered = isActive(); + boolean isPowered = level.isBlockPowered(this) + || checkSurrounding(this, true, 0) + || checkSurrounding(this, false, 0); + + // Avoid Block minstake + if (wasPowered != isPowered) { + setActive(isPowered); + level.updateAround(down()); + if (getOrientation().isAscending()) { + level.updateAround(up()); + } + } + return type; + } + return 0; + } + + /** + * Check the surrounding of the rail + * + * @param pos The rail position + * @param relative The relative of the rail that will be checked + * @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) { + // The powered rail can power up to 8 blocks only + if (power >= 8) { + return false; + } + // The position of the floor numbers + int dx = pos.getFloorX(); + int dy = pos.getFloorY(); + int dz = pos.getFloorZ(); + // First: get the base block + BlockRail block; + Block block2 = level.getBlock(new Vector3(dx, dy, dz)); + + // Second: check if the rail is Powered rail + if (Rail.isRailBlock(block2)) { + block = (BlockRail) block2; + } else { + return false; + } + + // Used to check if the next ascending rail should be what + Rail.Orientation base = null; + boolean onStraight = true; + // Third: Recalculate the base position + switch (block.getOrientation()) { + case STRAIGHT_NORTH_SOUTH: + if (relative) { + dz++; + } else { + dz--; + } + break; + case STRAIGHT_EAST_WEST: + if (relative) { + dx--; + } else { + dx++; + } + break; + case ASCENDING_EAST: + if (relative) { + dx--; + } else { + dx++; + dy++; + onStraight = false; + } + base = Rail.Orientation.STRAIGHT_EAST_WEST; + break; + case ASCENDING_WEST: + if (relative) { + dx--; + dy++; + onStraight = false; + } else { + dx++; + } + base = Rail.Orientation.STRAIGHT_EAST_WEST; + break; + case ASCENDING_NORTH: + if (relative) { + dz++; + } else { + dz--; + dy++; + onStraight = false; + } + base = Rail.Orientation.STRAIGHT_NORTH_SOUTH; + break; + case ASCENDING_SOUTH: + if (relative) { + dz++; + dy++; + onStraight = false; + } else { + dz--; + } + base = Rail.Orientation.STRAIGHT_NORTH_SOUTH; + break; + default: + // Unable to determinate the rail orientation + // Wrong rail? + return false; + } + // Next check the if rail is on power state + return canPowered(new Vector3(dx, dy, dz), base, power, relative) + || onStraight && canPowered(new Vector3(dx, dy - 1, dz), base, power, relative); + } + + protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boolean relative) { + Block block = level.getBlock(pos); + // What! My block is air??!! Impossible! XD + if (!(block instanceof BlockRailPowered)) { + return false; + } + + // Sometimes the rails are diffrent orientation + Rail.Orientation base = ((BlockRailPowered) block).getOrientation(); + + // Possible way how to know when the rail is activated is rail were directly powered + // OR recheck the surrounding... Which will returns here =w= + return (state != Rail.Orientation.STRAIGHT_EAST_WEST + || base != Rail.Orientation.STRAIGHT_NORTH_SOUTH + && base != Rail.Orientation.ASCENDING_NORTH + && base != Rail.Orientation.ASCENDING_SOUTH) + && (state != Rail.Orientation.STRAIGHT_NORTH_SOUTH + || base != Rail.Orientation.STRAIGHT_EAST_WEST + && base != Rail.Orientation.ASCENDING_EAST + && base != Rail.Orientation.ASCENDING_WEST) + && (level.isBlockPowered(pos) || checkSurrounding(pos, relative, power + 1)); + } + } diff --git a/src/main/java/cn/nukkit/block/BlockStairs.java b/src/main/java/cn/nukkit/block/BlockStairs.java index 90be6d2cdd..304b91f616 100644 --- a/src/main/java/cn/nukkit/block/BlockStairs.java +++ b/src/main/java/cn/nukkit/block/BlockStairs.java @@ -73,4 +73,80 @@ public Item toItem() { item.setDamage(0); return item; } + + @Override + public boolean collidesWithBB(AxisAlignedBB bb) { + int damage = this.getDamage(); + int side = damage & 0x03; + double f = 0; + double f1 = 0.5; + double f2 = 0.5; + double f3 = 1; + if ((damage & 0x04) > 0) { + f = 0.5; + f1 = 1; + f2 = 0; + f3 = 0.5; + } + + if (bb.intersectsWith(new AxisAlignedBB( + this.x, + this.y + f, + this.z, + this.x + 1, + this.y + f1, + this.z + 1 + ))) { + return true; + } + + + if (side == 0) { + if (bb.intersectsWith(new AxisAlignedBB( + this.x + 0.5, + this.y + f2, + this.z, + this.x + 1, + this.y + f3, + this.z + 1 + ))) { + return true; + } + } else if (side == 1) { + if (bb.intersectsWith(new AxisAlignedBB( + this.x, + this.y + f2, + this.z, + this.x + 0.5, + this.y + f3, + this.z + 1 + ))) { + return true; + } + } else if (side == 2) { + if (bb.intersectsWith(new AxisAlignedBB( + this.x, + this.y + f2, + this.z + 0.5, + this.x + 1, + this.y + f3, + this.z + 1 + ))) { + return true; + } + } else if (side == 3) { + if (bb.intersectsWith(new AxisAlignedBB( + this.x, + this.y + f2, + this.z, + this.x + 1, + this.y + f3, + this.z + 0.5 + ))) { + return true; + } + } + + return false; + } } diff --git a/src/main/java/cn/nukkit/block/BlockTripWireHook.java b/src/main/java/cn/nukkit/block/BlockTripWireHook.java index aed58677d2..1ed0edc6bc 100644 --- a/src/main/java/cn/nukkit/block/BlockTripWireHook.java +++ b/src/main/java/cn/nukkit/block/BlockTripWireHook.java @@ -177,15 +177,15 @@ public void calculateState(boolean onBreak, boolean updateAround, int pos, Block private void addSound(Vector3 pos, boolean canConnect, boolean nextPowered, boolean attached, boolean powered) { if (nextPowered && !powered) { - this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_POWER_ON, 1, -1, pos, false, false); + this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_POWER_ON, 1, -1, pos); this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 0, 15)); } else if (!nextPowered && powered) { - this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_POWER_OFF, 1, -1, pos, false, false); + this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_POWER_OFF, 1, -1, pos); this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 15, 0)); } else if (canConnect && !attached) { - this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_ATTACH, 1, -1, pos, false, false); + this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_ATTACH, 1, -1, pos); } else if (!canConnect && attached) { - this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_DETACH, 1, -1, pos, false, false); + this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_DETACH, 1, -1, pos); } } diff --git a/src/main/java/cn/nukkit/block/BlockWaterStill.java b/src/main/java/cn/nukkit/block/BlockWaterStill.java index 2f58d1dc59..1775eef3dc 100644 --- a/src/main/java/cn/nukkit/block/BlockWaterStill.java +++ b/src/main/java/cn/nukkit/block/BlockWaterStill.java @@ -1,7 +1,5 @@ package cn.nukkit.block; -import cn.nukkit.level.Level; - /** * author: Angelic47 * Nukkit Project diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java index 58c72dbf91..f0571ec255 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java @@ -38,6 +38,7 @@ public abstract class BlockEntity extends Position { public static final String COMPARATOR = "Comparator"; public static final String HOPPER = "Hopper"; public static final String BED = "Bed"; + public static final String JUKEBOX = "Jukebox"; public static long count = 1; @@ -49,6 +50,8 @@ public abstract class BlockEntity extends Position { public String name; public long id; + public boolean movable = true; + public boolean closed = false; public CompoundTag namedTag; protected long lastUpdate; @@ -71,6 +74,7 @@ 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"); this.chunk.addBlockEntity(this); this.getLevel().addBlockEntity(this); @@ -141,21 +145,22 @@ public void saveNBT() { this.namedTag.putInt("x", (int) this.getX()); this.namedTag.putInt("y", (int) this.getY()); this.namedTag.putInt("z", (int) this.getZ()); + this.namedTag.putBoolean("isMovable", this.movable); } - public CompoundTag getCleanedNBT(){ + public CompoundTag getCleanedNBT() { this.saveNBT(); CompoundTag tag = this.namedTag.clone(); tag.remove("x").remove("y").remove("z").remove("id"); - if(tag.getTags().size() > 0){ + if (tag.getTags().size() > 0) { return tag; - }else{ + } else { return null; } } public Block getBlock() { - return this.level.getBlock(this); + return this.getLevelBlock(); } public abstract boolean isBlockEntityValid(); @@ -186,6 +191,10 @@ public String getName() { return name; } + public boolean isMovable() { + return movable; + } + public static CompoundTag getDefaultCompound(Vector3 pos, String id) { return new CompoundTag("") .putString("id", id) diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java index ecf277b113..ec033ad95e 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java @@ -1,10 +1,12 @@ package cn.nukkit.blockentity; - import cn.nukkit.Player; import cn.nukkit.Server; import cn.nukkit.block.Block; import cn.nukkit.block.BlockAir; +import cn.nukkit.block.BlockBrewingStand; +import cn.nukkit.event.inventory.BrewEvent; +import cn.nukkit.event.inventory.StartBrewEvent; import cn.nukkit.inventory.BrewingInventory; import cn.nukkit.inventory.BrewingRecipe; import cn.nukkit.inventory.InventoryHolder; @@ -28,6 +30,8 @@ public class BlockEntityBrewingStand extends BlockEntitySpawnable implements Inv public static final int MAX_BREW_TIME = 400; public int brewTime = MAX_BREW_TIME; + public int fuelTotal; + public int fuelAmount; public static final List ingredients = new ArrayList() { { @@ -53,6 +57,9 @@ public BlockEntityBrewingStand(FullChunk chunk, CompoundTag nbt) { this.brewTime = namedTag.getShort("CookTime"); } + this.fuelAmount = namedTag.getShort("FuelAmount"); + this.fuelTotal = namedTag.getShort("FuelTotal"); + if (brewTime < MAX_BREW_TIME) { this.scheduleUpdate(); } @@ -96,6 +103,8 @@ public void saveNBT() { } namedTag.putShort("CookTime", brewTime); + namedTag.putShort("FuelAmount", this.fuelAmount); + namedTag.putShort("FuelTotal", this.fuelTotal); } @Override @@ -167,46 +176,65 @@ public boolean onUpdate() { Item ingredient = this.inventory.getIngredient(); boolean canBrew = false; - for (int i = 1; i <= 3; i++) { - if (this.inventory.getItem(i).getId() == Item.POTION) { - canBrew = true; - } + Item fuel = this.getInventory().getFuel(); + if (this.fuelAmount <= 0 && fuel.getId() == Item.BLAZE_POWDER && fuel.getCount() > 0) { + fuel.count--; + this.fuelAmount = 20; + this.fuelTotal = 20; + + this.inventory.setFuel(fuel); + this.sendFuel(); } - if (this.brewTime <= MAX_BREW_TIME && canBrew && ingredient.getCount() > 0) { - if (!this.checkIngredient(ingredient)) { + if (this.fuelAmount > 0) { + for (int i = 1; i <= 3; i++) { + if (this.inventory.getItem(i).getId() == Item.POTION) { + canBrew = true; + } + } + + if (this.brewTime <= MAX_BREW_TIME && canBrew && ingredient.getCount() > 0) { + if (!this.checkIngredient(ingredient)) { + canBrew = false; + } + } else { canBrew = false; } - } else { - canBrew = false; } if (canBrew) { - this.brewTime--; + if (this.brewTime == MAX_BREW_TIME) { + this.sendBrewTime(); + StartBrewEvent e = new StartBrewEvent(this); + this.server.getPluginManager().callEvent(e); - for (Player player : this.inventory.getViewers()) { - int windowId = player.getWindowId(this.inventory); - if (windowId > 0) { - ContainerSetDataPacket pk = new ContainerSetDataPacket(); - pk.windowid = (byte) windowId; - pk.property = 0; - pk.value = this.brewTime; - player.dataPacket(pk); + if (e.isCancelled()) { + return false; } } + this.brewTime--; + if (this.brewTime <= 0) { //20 seconds - for (int i = 1; i <= 3; i++) { - Item potion = this.inventory.getItem(i); - BrewingRecipe recipe = Server.getInstance().getCraftingManager().matchBrewingRecipe(ingredient, potion); + BrewEvent e = new BrewEvent(this); + this.server.getPluginManager().callEvent(e); - if (recipe != null) { - this.inventory.setItem(i, recipe.getResult()); + if (!e.isCancelled()) { + for (int i = 1; i <= 3; i++) { + Item potion = this.inventory.getItem(i); + BrewingRecipe recipe = Server.getInstance().getCraftingManager().matchBrewingRecipe(ingredient, potion); + + if (recipe != null) { + this.inventory.setItem(i, recipe.getResult()); + } } - } - ingredient.count--; - this.inventory.setIngredient(ingredient); + ingredient.count--; + this.inventory.setIngredient(ingredient); + + this.fuelAmount--; + this.sendFuel(); + } this.brewTime = MAX_BREW_TIME; } @@ -216,11 +244,75 @@ public boolean onUpdate() { this.brewTime = MAX_BREW_TIME; } + //this.sendBrewTime(); lastUpdate = System.currentTimeMillis(); return ret; } + protected void sendFuel() { + ContainerSetDataPacket pk = new ContainerSetDataPacket(); + + for (Player p : this.inventory.getViewers()) { + int windowId = p.getWindowId(this.inventory); + if (windowId > 0) { + pk.windowId = windowId; + + pk.property = ContainerSetDataPacket.PROPERTY_BREWING_STAND_FUEL_AMOUNT; + pk.value = this.fuelAmount; + p.dataPacket(pk); + + pk.property = ContainerSetDataPacket.PROPERTY_BREWING_STAND_FUEL_TOTAL; + pk.value = this.fuelTotal; + p.dataPacket(pk); + } + } + } + + protected void sendBrewTime() { + ContainerSetDataPacket pk = new ContainerSetDataPacket(); + pk.property = ContainerSetDataPacket.PROPERTY_BREWING_STAND_BREW_TIME; + pk.value = this.brewTime; + + for (Player p : this.inventory.getViewers()) { + int windowId = p.getWindowId(this.inventory); + if (windowId > 0) { + pk.windowId = windowId; + + p.dataPacket(pk); + } + } + } + + public void updateBlock() { + Block block = this.getLevelBlock(); + + if (!(block instanceof BlockBrewingStand)) { + return; + } + + int meta = 0; + + for (int i = 1; i <= 3; ++i) { + Item potion = this.inventory.getItem(i); + + if (potion.getId() == Item.POTION && potion.getCount() > 0) { + meta |= 1 << i; + } + } + + block.setDamage(meta); + this.level.setBlock(block, block, false, false); + } + + public int getFuel() { + return fuelAmount; + } + + public void setFuel(int fuel) { + this.fuelAmount = fuel; + } + @Override public CompoundTag getSpawnCompound() { CompoundTag nbt = new CompoundTag() @@ -228,7 +320,12 @@ public CompoundTag getSpawnCompound() { .putInt("x", (int) this.x) .putInt("y", (int) this.y) .putInt("z", (int) this.z) - .putShort("CookTime", 0); + .putShort("FuelTotal", this.fuelTotal) + .putShort("FuelAmount", this.fuelAmount); + + if (this.brewTime < MAX_BREW_TIME) { + nbt.putShort("CookTime", this.brewTime); + } if (this.hasName()) { nbt.put("CustomName", namedTag.get("CustomName")); diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java b/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java index c4f2df249a..f4cdeab250 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java @@ -38,7 +38,7 @@ public BlockEntityChest(FullChunk chunk, CompoundTag nbt) { /* for (int i = 0; i < this.getSize(); i++) { this.inventory.setItem(i, this.getItem(i)); } */ - + ListTag list = (ListTag) this.namedTag.getList("Items"); for (CompoundTag compound : list.getAll()) { Item item = NBTIO.getItemHelper(compound); @@ -104,7 +104,7 @@ public Item getItem(int index) { @Override public void setItem(int index, Item item) { int i = this.getSlotIndex(index); - + CompoundTag d = NBTIO.putItemHelper(item, index); // If item is air or count less than 0, remove the item from the "Items" list @@ -116,14 +116,14 @@ public void setItem(int index, Item item) { // If it is less than i, then it is a new item, so we are going to add it at the end of the list (this.namedTag.getList("Items", CompoundTag.class)).add(d); } else { - // If it is more than i, then it is an update on a slot, so we are going to overwrite the item in the list + // If it is more than i, then it is an update on a inventorySlot, so we are going to overwrite the item in the list (this.namedTag.getList("Items", CompoundTag.class)).add(i, d); } } @Override public BaseInventory getInventory() { - if (this.isPaired() && this.doubleInventory == null) { + if (this.doubleInventory == null && this.isPaired()) { this.checkPairing(); } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java b/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java index 4cac086701..6e3265c627 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java @@ -262,14 +262,15 @@ public boolean onUpdate() { int windowId = player.getWindowId(this.getInventory()); if (windowId > 0) { ContainerSetDataPacket pk = new ContainerSetDataPacket(); - pk.windowid = (byte) windowId; - pk.property = 0; + pk.windowId = windowId; + pk.property = ContainerSetDataPacket.PROPERTY_FURNACE_TICK_COUNT; + ; pk.value = cookTime; player.dataPacket(pk); pk = new ContainerSetDataPacket(); - pk.windowid = (byte) windowId; - pk.property = 1; + pk.windowId = windowId; + pk.property = ContainerSetDataPacket.PROPERTY_FURNACE_LIT_TIME; pk.value = burnDuration; player.dataPacket(pk); } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java index 9bdc5fd129..000a378045 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java @@ -146,7 +146,7 @@ public boolean onUpdate() { if (this.closed) { return false; } - + this.transferCooldown--; if (!this.isOnTransferCooldown()) { diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java b/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java index ef5aad0f82..222a1b0490 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java @@ -96,8 +96,8 @@ public CompoundTag getSpawnCompound() { .putInt("z", (int) this.z) .putCompound("Item", item ? NBTIO.putItemHelper(new ItemBlock(new BlockAir())) : NBTItem) .putByte("ItemRotation", item ? 0 : this.getItemRotation()); - // TODO: This crashes the client, why? - // .putFloat("ItemDropChance", this.getItemDropChance()); + // TODO: This crashes the client, why? + // .putFloat("ItemDropChance", this.getItemDropChance()); } public int getAnalogOutput() { diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityJukebox.java b/src/main/java/cn/nukkit/blockentity/BlockEntityJukebox.java new file mode 100644 index 0000000000..6ae882863d --- /dev/null +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityJukebox.java @@ -0,0 +1,70 @@ +package cn.nukkit.blockentity; + +import cn.nukkit.Server; +import cn.nukkit.block.Block; +import cn.nukkit.item.Item; +import cn.nukkit.item.ItemRecord; +import cn.nukkit.level.format.FullChunk; +import cn.nukkit.nbt.NBTIO; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +import java.util.Objects; + +/** + * @author CreeperFace + */ +public class BlockEntityJukebox extends BlockEntitySpawnable { + + private Item recordItem; + + public BlockEntityJukebox(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + + this.recordItem = NBTIO.getItemHelper(nbt.getCompound("RecordItem")); + } + + @Override + public boolean isBlockEntityValid() { + return this.getLevel().getBlockIdAt(getFloorX(), getFloorY(), getFloorZ()) == Block.JUKEBOX; + } + + public void setRecordItem(Item recordItem) { + Objects.requireNonNull(recordItem, "Record item cannot be null"); + this.recordItem = recordItem; + } + + public Item getRecordItem() { + return recordItem; + } + + public void play() { + if (this.recordItem instanceof ItemRecord) { + LevelSoundEventPacket pk = new LevelSoundEventPacket(); + pk.sound = ((ItemRecord) this.recordItem).getSoundId(); + pk.pitch = 1; + pk.extraData = -1; + pk.x = (float) this.x; + pk.y = (float) this.y; + pk.z = (float) this.z; + + Server.broadcastPacket(this.level.getPlayers().values(), pk); + } + } + + public void dropItem() { + this.level.dropItem(this.up(), this.recordItem); + } + + @Override + public void saveNBT() { + super.saveNBT(); + this.namedTag.putCompound("RecordItem", NBTIO.putItemHelper(this.recordItem)); + } + + @Override + public CompoundTag getSpawnCompound() { + return getDefaultCompound(this, JUKEBOX) + .putCompound("RecordItem", NBTIO.putItemHelper(this.recordItem)); + } +} diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java index f97de378da..23793af123 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java @@ -1,6 +1,5 @@ package cn.nukkit.blockentity; -import cn.nukkit.Player; import cn.nukkit.entity.Entity; import cn.nukkit.level.format.FullChunk; import cn.nukkit.math.AxisAlignedBB; @@ -13,30 +12,27 @@ /** * @author CreeperFace */ -public class BlockEntityPistonArm extends BlockEntitySpawnable { +public class BlockEntityPistonArm extends BlockEntity { - public float progress = 1f; - public float lastProgress = 1f; + public float progress = 1.0F; + public float lastProgress = 1.0F; public BlockFace facing; public boolean extending = false; public boolean sticky = false; - public byte state = 1; public byte newState = 1; - public Vector3 attachedBlock = null; - public boolean isMovable = true; + public boolean powered = false; public BlockEntityPistonArm(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); - if (nbt.contains("Progress")) { this.progress = nbt.getFloat("Progress"); } if (nbt.contains("LastProgress")) { - this.lastProgress = nbt.getInt("LastProgress"); + this.lastProgress = (float) nbt.getInt("LastProgress"); } if (nbt.contains("Sticky")) { @@ -47,68 +43,42 @@ public BlockEntityPistonArm(FullChunk chunk, CompoundTag nbt) { this.extending = nbt.getBoolean("Extending"); } + if (nbt.contains("powered")) { + this.powered = nbt.getBoolean("powered"); + } + if (nbt.contains("AttachedBlocks")) { - ListTag blocks = nbt.getList("AttachedBlocks", IntTag.class); + ListTag blocks = nbt.getList("AttachedBlocks", IntTag.class); if (blocks != null && blocks.size() > 0) { - this.attachedBlock = new Vector3(blocks.get(0).getData(), blocks.get(1).getData(), blocks.get(2).getData()); + this.attachedBlock = new Vector3((double) ((IntTag) blocks.get(0)).getData().intValue(), (double) ((IntTag) blocks.get(1)).getData().intValue(), (double) ((IntTag) blocks.get(2)).getData().intValue()); } } else { - nbt.putList(new ListTag("AttachedBlocks")); + nbt.putList(new ListTag("AttachedBlocks")); } - } - - /*@Override - public boolean onUpdate() { - this.lastProgress = this.progress; - - if (this.lastProgress >= 1) { - if(this.newState == 2) { - this.spawnToAll(); - this.state = 2; - return true; - } - - this.newState = 2; - this.spawnToAll(); - this.pushEntities(); - } else { - this.progress += 0.5; - - if (this.progress >= 1) { - this.progress = 1; - } - - this.pushEntities(); - this.spawnToAll(); - } - - return this.state != 2; - }*/ + } private void pushEntities() { float lastProgress = this.getExtendedProgress(this.lastProgress); - double x = lastProgress * (float) this.facing.getXOffset(); - double y = lastProgress * (float) this.facing.getYOffset(); - double z = lastProgress * (float) this.facing.getZOffset(); - AxisAlignedBB bb = new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1); + 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 AxisAlignedBB(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D); Entity[] entities = this.level.getCollidingEntities(bb); - if (entities.length != 0) { - //TODO: + ; } + } private float getExtendedProgress(float progress) { return this.extending ? progress - 1.0F : 1.0F - progress; } - @Override public boolean isBlockEntityValid() { return true; } - @Override public void saveNBT() { super.saveNBT(); this.namedTag.putBoolean("isMovable", this.isMovable); @@ -116,35 +86,10 @@ public void saveNBT() { this.namedTag.putByte("NewState", this.newState); this.namedTag.putFloat("Progress", this.progress); this.namedTag.putFloat("LastProgress", this.lastProgress); + this.namedTag.putBoolean("powered", this.powered); } - @Override - public void spawnTo(Player player) { - super.spawnTo(player); - - //System.out.println("spawn X: "+this.x+" Y: "+this.y+" Z: "+this.z); - } - - @Override public CompoundTag getSpawnCompound() { - /*return new CompoundTag() - .putString("id", BlockEntity.PISTON_ARM) - .putInt("x", (int) this.x) - .putInt("y", (int) this.y) - .putInt("z", (int) this.z) - .putFloat("Progress", this.progress) - .putFloat("LastProgress", this.lastProgress) - .putBoolean("isMovable", this.isMovable) - .putList(new ListTag<>("AttachedBlocks")) - .putList(new ListTag<>("BreakBlocks")) - .putBoolean("Sticky", this.sticky) - .putByte("State", this.state) - .putByte("NewState", this.newState);*/ - - return new CompoundTag() - .putString("id", BlockEntity.PISTON_ARM) - .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); } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java index 23f73a1991..2a3e212fd5 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java @@ -1,8 +1,15 @@ package cn.nukkit.blockentity; +import cn.nukkit.Player; import cn.nukkit.block.Block; +import cn.nukkit.event.block.SignChangeEvent; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.utils.TextFormat; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; /** * author: MagicDroidX @@ -12,19 +19,24 @@ public class BlockEntitySign extends BlockEntitySpawnable { public BlockEntitySign(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); - if (!nbt.contains("Text1")) { - nbt.putString("Text1", ""); - } - if (!nbt.contains("Text2")) { - nbt.putString("Text2", ""); - } - if (!nbt.contains("Text3")) { - nbt.putString("Text3", ""); - } - if (!nbt.contains("Text4")) { - nbt.putString("Text4", ""); + + if (!nbt.contains("Text")) { + List lines = new ArrayList<>(); + + for (int i = 1; i <= 4; i++) { + String key = "Text" + i; + + if (nbt.contains(key)) { + String line = nbt.getString(key); + + lines.add(line); + + nbt.remove(key); + } + } + + nbt.putString("Text", String.join("\n", lines)); } - this.namedTag = nbt; } @Override @@ -39,27 +51,8 @@ public boolean isBlockEntityValid() { return blockID == Block.SIGN_POST || blockID == Block.WALL_SIGN; } - public boolean setText() { - return this.setText(""); - } - - public boolean setText(String line1) { - return this.setText(line1, ""); - } - - public boolean setText(String line1, String line2) { - return this.setText(line1, line2, ""); - } - - public boolean setText(String line1, String line2, String line3) { - return this.setText(line1, line2, line3, ""); - } - - public boolean setText(String line1, String line2, String line3, String line4) { - this.namedTag.putString("Text1", line1); - this.namedTag.putString("Text2", line2); - this.namedTag.putString("Text3", line3); - this.namedTag.putString("Text4", line4); + public boolean setText(String... lines) { + this.namedTag.putString("Text", String.join("\n", lines)); this.spawnToAll(); if (this.chunk != null) { @@ -71,26 +64,46 @@ public boolean setText(String line1, String line2, String line3, String line4) { } public String[] getText() { - return new String[]{ - this.namedTag.getString("Text1"), - this.namedTag.getString("Text2"), - this.namedTag.getString("Text3"), - this.namedTag.getString("Text4") - }; + return this.namedTag.getString("Text").split("\n"); + } + + @Override + public boolean updateCompoundTag(CompoundTag nbt, Player player) { + if (!nbt.getString("id").equals(BlockEntity.SIGN)) { + return false; + } + String[] text = nbt.getString("Text").split("\n", 4); + + SignChangeEvent signChangeEvent = new SignChangeEvent(this.getBlock(), player, text); + + if (!this.namedTag.contains("Creator") || !Objects.equals(player.getUniqueId().toString(), this.namedTag.getString("Creator"))) { + signChangeEvent.setCancelled(); + } + + if (player.getRemoveFormat()) { + for (int i = 0; i < text.length; i++) { + text[i] = TextFormat.clean(text[i]); + } + } + + this.server.getPluginManager().callEvent(signChangeEvent); + + if (!signChangeEvent.isCancelled()) { + this.setText(signChangeEvent.getLines()); + return true; + } + + return false; } @Override public CompoundTag getSpawnCompound() { return new CompoundTag() .putString("id", BlockEntity.SIGN) - .putString("Text1", this.namedTag.getString("Text1")) - .putString("Text2", this.namedTag.getString("Text2")) - .putString("Text3", this.namedTag.getString("Text3")) - .putString("Text4", this.namedTag.getString("Text4")) + .putString("Text", this.namedTag.getString("Text")) .putInt("x", (int) this.x) .putInt("y", (int) this.y) .putInt("z", (int) this.z); } - } diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java index 5371b9655d..1f9b7ee3ad 100644 --- a/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java +++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java @@ -50,4 +50,14 @@ public void spawnToAll() { } } } + + /** + * Called when a player updates a block entity's NBT data + * for example when writing on a sign. + * + * @return bool indication of success, will respawn the tile to the player if false. + */ + public boolean updateCompoundTag(CompoundTag nbt, Player player) { + return false; + } } diff --git a/src/main/java/cn/nukkit/command/Command.java b/src/main/java/cn/nukkit/command/Command.java index 76471bced4..d94283a2d7 100644 --- a/src/main/java/cn/nukkit/command/Command.java +++ b/src/main/java/cn/nukkit/command/Command.java @@ -151,7 +151,7 @@ public boolean testPermission(CommandSender target) { } if (this.permissionMessage == null) { - target.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.permission")); + target.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.unknown", this.name)); } else if (!this.permissionMessage.equals("")) { target.sendMessage(this.permissionMessage.replace("", this.permission)); } @@ -207,7 +207,7 @@ public boolean unregister(CommandMap commandMap) { } public boolean allowChangesFrom(CommandMap commandMap) { - return commandMap == null || commandMap.equals(this.commandMap); + return commandMap != null && !commandMap.equals(this.commandMap); } public boolean isRegistered() { diff --git a/src/main/java/cn/nukkit/command/CommandReader.java b/src/main/java/cn/nukkit/command/CommandReader.java index 037750dbee..4586c6ce5c 100644 --- a/src/main/java/cn/nukkit/command/CommandReader.java +++ b/src/main/java/cn/nukkit/command/CommandReader.java @@ -37,7 +37,7 @@ public CommandReader() { this.reader = new ConsoleReader(); reader.setPrompt("> "); instance = this; - + reader.addCompleter(new PlayersCompleter()); // Add player TAB completer reader.addCompleter(new CommandsCompleter()); // Add command TAB completer } catch (IOException e) { @@ -54,11 +54,11 @@ public String readLine() { } return null; } - + public void run() { Long lastLine = System.currentTimeMillis(); String line; - + try { while ((line = reader.readLine()) != null) { if (Server.getInstance().getConsoleSender() == null || Server.getInstance().getPluginManager() == null) { diff --git a/src/main/java/cn/nukkit/command/data/CommandParameter.java b/src/main/java/cn/nukkit/command/data/CommandParameter.java index 281115bab6..a3fe91c979 100644 --- a/src/main/java/cn/nukkit/command/data/CommandParameter.java +++ b/src/main/java/cn/nukkit/command/data/CommandParameter.java @@ -41,25 +41,25 @@ public CommandParameter(String name) { this(name, false); } - public CommandParameter(String name, boolean optional, String enumType){ + public CommandParameter(String name, boolean optional, String enumType) { this.name = name; this.type = ARG_TYPE_STRING_ENUM; this.optional = optional; this.enum_type = enumType; } - public CommandParameter(String name, boolean optional, String[] enumValues){ + public CommandParameter(String name, boolean optional, String[] enumValues) { this.name = name; this.type = ARG_TYPE_STRING_ENUM; this.optional = optional; this.enum_values = enumValues; } - public CommandParameter(String name, String enumType){ + public CommandParameter(String name, String enumType) { this(name, false, enumType); } - public CommandParameter(String name, String[] enumValues){ + public CommandParameter(String name, String[] enumValues) { this(name, false, enumValues); } diff --git a/src/main/java/cn/nukkit/command/defaults/DeopCommand.java b/src/main/java/cn/nukkit/command/defaults/DeopCommand.java index 4bc71c98fa..d87544af85 100644 --- a/src/main/java/cn/nukkit/command/defaults/DeopCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/DeopCommand.java @@ -14,7 +14,7 @@ */ public class DeopCommand extends VanillaCommand { public DeopCommand(String name) { - super(name, "%nukkit.command.deop.description", "%commands.deop.usage"); + super(name, "%nukkit.command.deop.description", "%commands.deop.description"); this.setPermission("nukkit.command.op.take"); this.commandParameters.put("default", new CommandParameter[]{ new CommandParameter("player", CommandParameter.ARG_TYPE_TARGET, false) @@ -38,7 +38,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) player.setOp(false); if (player instanceof Player) { - ((Player) player).sendMessage(TextFormat.GRAY + "You are no longer op!"); + ((Player) player).sendMessage(new TranslationContainer(TextFormat.GRAY + "%commands.deop.message")); } Command.broadcastCommandMessage(sender, new TranslationContainer("commands.deop.success", new String[]{player.getName()})); diff --git a/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java b/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java index d2c75f1d6d..40d6800748 100644 --- a/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java @@ -24,7 +24,7 @@ public DifficultyCommand(String name) { }); this.commandParameters.put("byString", new CommandParameter[]{ new CommandParameter("difficulty", new String[]{"peaceful", "p", "easy", "e", - "normal", "n", "hard", "h"}) + "normal", "n", "hard", "h"}) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java b/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java index 0ae775b8de..868f23c44c 100644 --- a/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java @@ -71,7 +71,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) } public int getIdByName(String value) throws NumberFormatException { - switch (value){ + switch (value) { case "protection": return 0; case "fire_protection": diff --git a/src/main/java/cn/nukkit/command/defaults/OpCommand.java b/src/main/java/cn/nukkit/command/defaults/OpCommand.java index df0dc5c256..8e74af6316 100644 --- a/src/main/java/cn/nukkit/command/defaults/OpCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/OpCommand.java @@ -15,7 +15,7 @@ public class OpCommand extends VanillaCommand { public OpCommand(String name) { - super(name, "%nukkit.command.op.description", "%commands.op.usage"); + super(name, "%nukkit.command.op.description", "%commands.op.description"); this.setPermission("nukkit.command.op.give"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ @@ -38,7 +38,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) Command.broadcastCommandMessage(sender, new TranslationContainer("commands.op.success", player.getName())); if (player instanceof Player) { - ((Player) player).sendMessage(TextFormat.GRAY + "You are now op!"); + ((Player) player).sendMessage(new TranslationContainer(TextFormat.GRAY + "%commands.op.message")); } player.setOp(true); diff --git a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java index ccb687bba0..0fea580f32 100644 --- a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java @@ -39,6 +39,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) if (Pattern.matches("^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$", value)) { sender.getServer().getIPBans().remove(value); + sender.getServer().getNetwork().unblockAddress(value); Command.broadcastCommandMessage(sender, new TranslationContainer("commands.unbanip.success", value)); } else { diff --git a/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java b/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java index ade4f51260..879e38371d 100644 --- a/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java @@ -20,7 +20,7 @@ public SetWorldSpawnCommand(String name) { this.setPermission("nukkit.command.setworldspawn"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("pos", CommandParameter.ARG_TYPE_BLOCK_POS, true) + new CommandParameter("blockPos", CommandParameter.ARG_TYPE_BLOCK_POS, true) }); } diff --git a/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java b/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java index 04fdaaab6c..ca97b11d6c 100644 --- a/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java @@ -21,7 +21,7 @@ public SpawnpointCommand(String name) { this.setPermission("nukkit.command.spawnpoint"); this.commandParameters.clear(); this.commandParameters.put("default", new CommandParameter[]{ - new CommandParameter("pos", CommandParameter.ARG_TYPE_BLOCK_POS, true), + new CommandParameter("blockPos", CommandParameter.ARG_TYPE_BLOCK_POS, true), }); } diff --git a/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java b/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java index 4bbcb30d84..5fba764991 100644 --- a/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java @@ -28,10 +28,10 @@ public TeleportCommand(String name) { }); this.commandParameters.put("Player->Pos", new CommandParameter[]{ new CommandParameter("player", CommandParameter.ARG_TYPE_TARGET, false), - new CommandParameter("pos", CommandParameter.ARG_TYPE_BLOCK_POS, false), + new CommandParameter("blockPos", CommandParameter.ARG_TYPE_BLOCK_POS, false), }); this.commandParameters.put("->Pos", new CommandParameter[]{ - new CommandParameter("pos", CommandParameter.ARG_TYPE_BLOCK_POS, false), + new CommandParameter("blockPos", CommandParameter.ARG_TYPE_BLOCK_POS, false), }); } diff --git a/src/main/java/cn/nukkit/command/defaults/TitleCommand.java b/src/main/java/cn/nukkit/command/defaults/TitleCommand.java index 732fb77129..ab09ce5d13 100644 --- a/src/main/java/cn/nukkit/command/defaults/TitleCommand.java +++ b/src/main/java/cn/nukkit/command/defaults/TitleCommand.java @@ -78,20 +78,20 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage)); return false; } - } else if (args.length == 3){ + } else if (args.length == 3) { switch (args[1].toLowerCase()) { case "title": player.sendTitle(args[2]); sender.sendMessage(new TranslationContainer("nukkit.command.title.title", new String[]{TextFormat.clean(args[2]), player.getName()})); break; - case "subtitle": + /*case "subtitle": player.setSubtitle(args[2]); sender.sendMessage(new TranslationContainer("nukkit.command.title.subtitle", new String[]{TextFormat.clean(args[2]), player.getName()})); break; case "actionbar": player.sendActionBarTitle(args[2]); sender.sendMessage(new TranslationContainer("nukkit.command.title.actionbar", new String[]{TextFormat.clean(args[2]), player.getName()})); - break; + break;*/ default: sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage)); return false; @@ -99,9 +99,9 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args) } else if (args.length == 5) { if (args[1].toLowerCase().equals("times")) { try { - player.setTitleAnimationTimes(Integer.valueOf(args[2]), //fadeIn + /*player.setTitleAnimationTimes(Integer.valueOf(args[2]), //fadeIn Integer.valueOf(args[3]), //stay - Integer.valueOf(args[4])); //fadeOut + Integer.valueOf(args[4])); //fadeOut*/ sender.sendMessage(new TranslationContainer("nukkit.command.title.times.success", new String[]{ args[2], args[3], args[4], player.getName()})); } catch (NumberFormatException exception) { diff --git a/src/main/java/cn/nukkit/entity/Entity.java b/src/main/java/cn/nukkit/entity/Entity.java index 9a99a82fb8..ac6a907f99 100644 --- a/src/main/java/cn/nukkit/entity/Entity.java +++ b/src/main/java/cn/nukkit/entity/Entity.java @@ -7,6 +7,8 @@ import cn.nukkit.block.BlockFire; import cn.nukkit.block.BlockWater; import cn.nukkit.entity.data.*; +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Event; import cn.nukkit.event.entity.*; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; import cn.nukkit.event.entity.EntityPortalEnterEvent.PortalType; @@ -69,7 +71,7 @@ public abstract class Entity extends Location implements Metadatable { public static final int DATA_AIR = 7; //short public static final int DATA_POTION_COLOR = 8; //int (ARGB!) public static final int DATA_POTION_AMBIENT = 9; //byte - /* 10 (byte) */ + public static final int DATA_JUMP_DURATION = 10; //long public static final int DATA_HURT_TIME = 11; //int (minecart/boat) public static final int DATA_HURT_DIRECTION = 12; //int (minecart/boat) public static final int DATA_PADDLE_TIME_LEFT = 13; //float @@ -82,9 +84,9 @@ public abstract class Entity extends Location implements Metadatable { public static final int DATA_ENDERMAN_HELD_ITEM_ID = 23; //short public static final int DATA_ENDERMAN_HELD_ITEM_DAMAGE = 24; //short public static final int DATA_ENTITY_AGE = 25; //short - /* 27 (byte) player-specific flags - * 28 (int) player "index"? - * 29 (block coords) bed position */ + public static final int DATA_PLAYER_FLAGS = 29; //byte + /* 28 (int) player "index"? */ + public static final int DATA_PLAYER_BED_POSITION = 29; //block coords public static final int DATA_FIREBALL_POWER_X = 30; //float public static final int DATA_FIREBALL_POWER_Y = 31; public static final int DATA_FIREBALL_POWER_Z = 32; @@ -103,6 +105,7 @@ public abstract class Entity extends Location implements Metadatable { /* 45 (byte) container stuff * 46 (int) container stuff * 47 (int) container stuff */ + public static final int DATA_FLAG_FIRE_IMMUNE = 47; public static final int DATA_BLOCK_TARGET = 48; //block coords (ender crystal) public static final int DATA_WITHER_INVULNERABLE_TICKS = 49; //int public static final int DATA_WITHER_TARGET_1 = 50; //long @@ -144,7 +147,7 @@ public abstract class Entity extends Location implements Metadatable { public static final int DATA_FLAG_SADDLED = 8; public static final int DATA_FLAG_POWERED = 9; public static final int DATA_FLAG_IGNITED = 10; - public static final int DATA_FLAG_BABY = 11; + public static final int DATA_FLAG_BABY = 11; //disable head scaling public static final int DATA_FLAG_CONVERTING = 12; public static final int DATA_FLAG_CRITICAL = 13; public static final int DATA_FLAG_CAN_SHOW_NAMETAG = 14; @@ -175,7 +178,10 @@ public abstract class Entity extends Location implements Metadatable { public static final int DATA_FLAG_IDLING = 39; public static final int DATA_FLAG_EVOKER_SPELL = 40; public static final int DATA_FLAG_CHARGE_ATTACK = 41; + public static final int DATA_FLAG_WASD_CONTROLLED = 43; + public static final int DATA_FLAG_CAN_POWER_JUMP = 44; public static final int DATA_FLAG_LINGER = 45; + public static final int DATA_FLAG_GRAVITY = 46; public static long entityCount = 1; @@ -204,8 +210,8 @@ public abstract class Entity extends Location implements Metadatable { protected EntityDamageEvent lastDamageCause = null; - protected List blocksAround = new ArrayList<>(); - protected List collisionBlocks = new ArrayList<>(); + public List blocksAround = new ArrayList<>(); + public List collisionBlocks = new ArrayList<>(); public double lastX; public double lastY; @@ -225,6 +231,10 @@ public abstract class Entity extends Location implements Metadatable { public double lastYaw; public double lastPitch; + public double PitchDelta; + public double YawDelta; + + public double entityCollisionReduction = 0; // Higher than 0.9 will result a fast collisions public AxisAlignedBB boundingBox; public boolean onGround; public boolean inBlock = false; @@ -236,6 +246,8 @@ public abstract class Entity extends Location implements Metadatable { protected float health = 20; private int maxHealth = 20; + protected float absorption = 0; + protected float ySize = 0; public boolean keepMovement = false; @@ -587,9 +599,13 @@ public void addEffect(Effect effect) { Effect oldEffect = this.effects.getOrDefault(effect.getId(), null); if (oldEffect != null) { - if (Math.abs(effect.getAmplifier()) < Math.abs(oldEffect.getAmplifier())) return; + if (Math.abs(effect.getAmplifier()) < Math.abs(oldEffect.getAmplifier())) { + return; + } if (Math.abs(effect.getAmplifier()) == Math.abs(oldEffect.getAmplifier()) - && effect.getDuration() < oldEffect.getDuration()) return; + && effect.getDuration() < oldEffect.getDuration()) { + return; + } effect.add(this, true); } else { effect.add(this, false); @@ -845,6 +861,11 @@ public boolean attack(EntityDamageEvent source) { if (source.isCancelled()) { return false; } + if (this.absorption > 0) { //Damage Absorption + float absorptionHealth = this.absorption - source.getFinalDamage() > 0 ? source.getFinalDamage() : this.absorption; + this.setAbsorption(this.absorption - absorptionHealth); + source.setDamage(-absorptionHealth, EntityDamageEvent.DamageModifier.ABSORPTION); + } setLastDamageCause(source); setHealth(getHealth() - source.getFinalDamage()); return true; @@ -1027,6 +1048,9 @@ public boolean entityBaseTick(int tickDiff) { Timings.entityBaseTickTimer.stopTiming(); return false; } + if (riding != null && !riding.isAlive() && riding instanceof EntityRideable) { + ((EntityRideable) riding).mountEntity(this); + } if (!this.effects.isEmpty()) { for (Effect effect : this.effects.values()) { @@ -1077,12 +1101,11 @@ public boolean entityBaseTick(int tickDiff) { } } - if (this.inPortalTicks > 80) { + if (this.inPortalTicks == 80) { EntityPortalEnterEvent ev = new EntityPortalEnterEvent(this, PortalType.NETHER); getServer().getPluginManager().callEvent(ev); //TODO: teleport - this.inPortalTicks = 0; } this.age += tickDiff; @@ -1178,6 +1201,53 @@ public boolean onUpdate(int currentTick) { return hasUpdate; } + protected boolean updateRidden() { + if (this.linkedEntity != null) { + if (!linkedEntity.isAlive()) { + return false; + } + this.motionX = 0.0D; + this.motionY = 0.0D; + this.motionZ = 0.0D; + onUpdate(lastUpdate); + if (this.linkedEntity != null) { + this.YawDelta += this.linkedEntity.yaw - this.linkedEntity.lastYaw; + for (this.PitchDelta += this.linkedEntity.pitch - this.linkedEntity.lastPitch; this.YawDelta >= 180.0D; this.YawDelta -= 360.0D) { + } + while (this.YawDelta < -180.0D) { + this.YawDelta += 360.0D; + } + while (this.PitchDelta >= 180.0D) { + this.PitchDelta -= 360.0D; + } + while (this.PitchDelta < -180.0D) { + this.PitchDelta += 360.0D; + } + double var1 = this.YawDelta * 0.5D; + double var3 = this.PitchDelta * 0.5D; + float var5 = 10.0F; + var1 = NukkitMath.clamp(var1, -var5, var5); + var3 = NukkitMath.clamp(var3, -var5, var5); + this.YawDelta -= var1; + this.PitchDelta -= var3; + } + return true; + } + return false; + } + + protected void updateRiderPosition(float offset) { + // Messy unknown variables + if (updateRidden()) { + linkedEntity.setDataProperty(new Vector3fEntityData(DATA_RIDER_SEAT_POSITION, + new Vector3f(0, offset, 0))); + } + } + + public float getMountedYOffset() { + return getHeight() * 0.75F; + } + public final void scheduleUpdate() { this.level.updateEntities.put(this.id, this); } @@ -1193,6 +1263,17 @@ public void setOnFire(int seconds) { } } + public float getAbsorption() { + return absorption; + } + + public void setAbsorption(float absorption) { + if (absorption != this.absorption) { + this.absorption = absorption; + if (this instanceof Player) ((Player) this).setAttribute(Attribute.getAttribute(Attribute.ABSORPTION).setValue(absorption)); + } + } + public BlockFace getDirection() { double rotation = this.yaw % 360; if (rotation < 0) { @@ -1251,13 +1332,17 @@ public void fall(float fallDistance) { Block down = this.level.getBlock(this.floor().down()); if (down.getId() == Item.FARMLAND) { + Event ev; + if (this instanceof Player) { - Player p = (Player) this; - PlayerInteractEvent ev = new PlayerInteractEvent(p, p.getInventory().getItemInHand(), down, null, Action.PHYSICAL); - this.server.getPluginManager().callEvent(ev); - if (ev.isCancelled()) { - return; - } + ev = new PlayerInteractEvent((Player) this, null, down, null, Action.PHYSICAL); + } else { + ev = new EntityInteractEvent(this, down); + } + + this.server.getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + return; } this.level.setBlock(down, new BlockDirt(), true, true); } @@ -1268,14 +1353,58 @@ public void handleLavaMovement() { //todo } - public void moveFlying() { - //todo + public void moveFlying(float strafe, float forward, float friction) { + // This is special for Nukkit! :) + float speed = strafe * strafe + forward * forward; + if (speed >= 1.0E-4F) { + speed = MathHelper.sqrt(speed); + if (speed < 1.0F) { + speed = 1.0F; + } + speed = friction / speed; + strafe *= speed; + forward *= speed; + float nest = MathHelper.sin((float) (this.yaw * 3.1415927F / 180.0F)); + float place = MathHelper.cos((float) (this.yaw * 3.1415927F / 180.0F)); + this.motionX += strafe * place - forward * nest; + this.motionZ += forward * place + strafe * nest; + } } public void onCollideWithPlayer(EntityHuman entityPlayer) { } + public void applyEntityCollision(Entity entity) { + if (entity.riding != this && entity.linkedEntity != this) { + double dx = entity.x - this.x; + double dy = entity.z - this.z; + double dz = NukkitMath.getDirection(dx, dy); + + if (dz >= 0.009999999776482582D) { + dz = MathHelper.sqrt((float) dz); + dx /= dz; + dy /= dz; + double d3 = 1.0D / dz; + + if (d3 > 1.0D) { + d3 = 1.0D; + } + + dx *= d3; + dy *= d3; + dx *= 0.05000000074505806D; + dy *= 0.05000000074505806D; + dx *= 1.0F + entityCollisionReduction; + dz *= 1.0F + entityCollisionReduction; + if (this.riding == null) { + motionX -= dx; + motionZ -= dy; + } + } + } + } + public void onStruckByLightning(Entity entity) { this.attack(new EntityDamageByEntityEvent(entity, this, DamageCause.LIGHTNING, 5)); @@ -1391,7 +1520,6 @@ public boolean fastMove(double dx, double dy, double dz) { } public boolean move(double dx, double dy, double dz) { - if (dx == 0 && dz == 0 && dy == 0) { return true; } @@ -1435,7 +1563,6 @@ public boolean move(double dx, double dy, double dz) { this.boundingBox.offset(0, 0, dz); - if (this.getStepHeight() > 0 && fallingFlag && this.ySize < 0.05 && (movX != dx || movZ != dz)) { double cx = dx; double cy = dy; @@ -1502,7 +1629,6 @@ public boolean move(double dx, double dy, double dz) { this.motionZ = 0; } - //TODO: vehicle collision events (first we need to spawn them!) Timings.entityMoveTimer.stopTiming(); return true; @@ -1570,6 +1696,8 @@ protected void checkBlockCollision() { if (portal) { inPortalTicks++; + } else { + this.inPortalTicks = 0; } if (vector.lengthSquared() > 0) { @@ -1600,7 +1728,7 @@ public void setRotation(double yaw, double pitch) { * used for bat only */ public boolean doesTriggerPressurePlate() { - return false; + return true; } protected void checkChunks() { @@ -1650,8 +1778,8 @@ public boolean setPosition(Vector3 pos) { double radius = this.getWidth() / 2d; - this.boundingBox.setBounds(pos.x - radius, pos.y, pos.z - radius, pos.x + radius, pos.y + (this.getHeight() * this.scale), pos.z + - radius); + this.boundingBox.setBounds(pos.x - radius, pos.y, pos.z - radius, pos.x + radius, pos.y + (this.getHeight() * this.scale), pos.z + + radius); this.checkChunks(); @@ -1793,8 +1921,9 @@ public boolean setDataProperty(EntityData data) { public boolean setDataProperty(EntityData data, boolean send) { if (!Objects.equals(data, this.getDataProperties().get(data.getId()))) { this.getDataProperties().put(data); - if (send) + if (send) { this.sendData(this.hasSpawned.values().stream().toArray(Player[]::new), new EntityMetadata().put(this.dataProperties.get(data.getId()))); + } 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 ed50f2ff5b..0fef9b4728 100644 --- a/src/main/java/cn/nukkit/entity/EntityHuman.java +++ b/src/main/java/cn/nukkit/entity/EntityHuman.java @@ -22,7 +22,8 @@ public class EntityHuman extends EntityHumanType { public static final int DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK public static final int DATA_PLAYER_FLAGS = 27; - public static final int DATA_PLAYER_BED_POSITION = 17; + + public static final int DATA_PLAYER_BED_POSITION = 29; public static final int DATA_PLAYER_BUTTON_TEXT = 40; protected UUID uuid; @@ -83,6 +84,7 @@ public void setSkin(Skin skin) { @Override protected void initEntity() { this.setDataFlag(DATA_PLAYER_FLAGS, DATA_PLAYER_FLAG_SLEEP, false); + this.setDataFlag(DATA_FLAGS, DATA_FLAG_GRAVITY); this.setDataProperty(new IntPositionEntityData(DATA_PLAYER_BED_POSITION, 0, 0, 0), false); @@ -103,10 +105,6 @@ protected void initEntity() { } super.initEntity(); - - if (this instanceof Player) { - ((Player) this).addWindow(this.inventory, 0); - } } @Override @@ -135,7 +133,10 @@ public void spawnTo(Player player) { throw new IllegalStateException(this.getClass().getSimpleName() + " must have a valid skin set"); } - this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getName(), this.skin, new Player[]{player}); + if (this instanceof Player) + this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getName(), this.skin, ((Player) this).getLoginChainData().getXUID(), new Player[]{player}); + else + this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getName(), this.skin, new Player[]{player}); AddPlayerPacket pk = new AddPlayerPacket(); pk.uuid = this.getUniqueId(); diff --git a/src/main/java/cn/nukkit/entity/EntityHumanType.java b/src/main/java/cn/nukkit/entity/EntityHumanType.java index 215a7d5e04..a85d2cc764 100644 --- a/src/main/java/cn/nukkit/entity/EntityHumanType.java +++ b/src/main/java/cn/nukkit/entity/EntityHumanType.java @@ -45,8 +45,9 @@ protected void initEntity() { ListTag inventoryList = this.namedTag.getList("Inventory", CompoundTag.class); for (CompoundTag item : inventoryList.getAll()) { int slot = item.getByte("Slot"); - if (slot >= 0 && slot < 9) { - this.inventory.setHotbarSlotIndex(slot, item.contains("TrueSlot") ? item.getByte("TrueSlot") : -1); + if (slot >= 0 && slot < 9) { //hotbar + //Old hotbar saving stuff, remove it (useless now) + inventoryList.remove(item); } else if (slot >= 100 && slot < 104) { this.inventory.setItem(this.inventory.getSize() + slot - 100, NBTIO.getItemHelper(item)); } else { @@ -74,14 +75,6 @@ public void saveNBT() { this.namedTag.putList(new ListTag("Inventory")); if (this.inventory != null) { for (int slot = 0; slot < 9; ++slot) { - int hotbarSlot = this.inventory.getHotbarSlotIndex(slot); - if (hotbarSlot != -1) { - Item item = this.inventory.getItem(hotbarSlot); - if (item.getId() != 0 && item.getCount() > 0) { - this.namedTag.getList("Inventory", CompoundTag.class).add(NBTIO.putItemHelper(item, slot).putByte("TrueSlot", hotbarSlot)); - continue; - } - } this.namedTag.getList("Inventory", CompoundTag.class).add(new CompoundTag() .putByte("Count", 0) .putShort("Damage", 0) diff --git a/src/main/java/cn/nukkit/entity/EntityInteractable.java b/src/main/java/cn/nukkit/entity/EntityInteractable.java new file mode 100644 index 0000000000..49c9515a1a --- /dev/null +++ b/src/main/java/cn/nukkit/entity/EntityInteractable.java @@ -0,0 +1,13 @@ +package cn.nukkit.entity; + +/** + * @author Adam Matthew + */ +public interface EntityInteractable { + + // Todo: Passive entity?? i18n and boat leaving text + String getInteractButtonText(); + + boolean canDoInteraction(); + +} diff --git a/src/main/java/cn/nukkit/entity/EntityLiving.java b/src/main/java/cn/nukkit/entity/EntityLiving.java index 4550b1d155..5f30e58155 100644 --- a/src/main/java/cn/nukkit/entity/EntityLiving.java +++ b/src/main/java/cn/nukkit/entity/EntityLiving.java @@ -4,6 +4,7 @@ import cn.nukkit.Server; import cn.nukkit.block.Block; import cn.nukkit.entity.data.ShortEntityData; +import cn.nukkit.entity.item.EntityVehicle; import cn.nukkit.entity.passive.EntityWaterAnimal; import cn.nukkit.event.entity.*; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; @@ -27,6 +28,7 @@ * Nukkit Project */ public abstract class EntityLiving extends Entity implements EntityDamageable { + public EntityLiving(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); } @@ -86,6 +88,10 @@ public boolean hasLineOfSight(Entity entity) { return true; } + public void collidingWith(Entity ent) { // can override (IronGolem|Bats) + ent.applyEntityCollision(this); + } + @Override public void heal(EntityRegainHealthEvent source) { super.heal(source); @@ -231,6 +237,14 @@ public boolean entityBaseTick(int tickDiff) { if (this.attackTime > 0) { this.attackTime -= tickDiff; } + if (this.riding == null) { + for (Entity entity : level.getNearbyEntities(this.boundingBox.grow(0.20000000298023224D, 0.0D, 0.20000000298023224D), this)) { + if (entity instanceof EntityRideable) { + this.collidingWith(entity); + } + } + } + Timings.livingEntityBaseTickTimer.stopTiming(); return hasUpdate; @@ -326,9 +340,4 @@ public void setMovementSpeed(float speed) { public float getMovementSpeed() { return this.movementSpeed; } - - @Override - public boolean doesTriggerPressurePlate() { - return true; - } } diff --git a/src/main/java/cn/nukkit/entity/EntityRideable.java b/src/main/java/cn/nukkit/entity/EntityRideable.java index 70b5b8f20d..91441f8dea 100644 --- a/src/main/java/cn/nukkit/entity/EntityRideable.java +++ b/src/main/java/cn/nukkit/entity/EntityRideable.java @@ -5,4 +5,13 @@ * Nukkit Project */ public interface EntityRideable { + + /** + * Mount or Dismounts an Entity from a rideable entity + * + * @param entity The target Entity + * @return {@code true} if the mounting successful + */ + boolean mountEntity(Entity entity); + } diff --git a/src/main/java/cn/nukkit/entity/data/Skin.java b/src/main/java/cn/nukkit/entity/data/Skin.java index c2ba8dfd0f..d6e4b1772d 100644 --- a/src/main/java/cn/nukkit/entity/data/Skin.java +++ b/src/main/java/cn/nukkit/entity/data/Skin.java @@ -25,6 +25,7 @@ public class Skin { private byte[] data = new byte[SINGLE_SKIN_SIZE]; private String model; + private Cape cape = new Cape(new byte[0]); //default no cape public Skin(byte[] data) { this(data, MODEL_STEVE); @@ -150,7 +151,96 @@ public void setModel(String model) { this.model = model; } + public Cape getCape() { + return cape; + } + + public void setCape(Cape cape) { + this.cape = cape; + } + public boolean isValid() { return this.data.length == SINGLE_SKIN_SIZE || this.data.length == DOUBLE_SKIN_SIZE; } + + public class Cape { + + public byte[] data; + + public Cape(byte[] data) { + this.setData(data); + } + + public Cape(InputStream inputStream) { + BufferedImage image; + try { + image = ImageIO.read(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + + this.parseBufferedImage(image); + } + + public Cape(ImageInputStream inputStream) { + BufferedImage image; + try { + image = ImageIO.read(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.parseBufferedImage(image); + } + + public Cape(File file, String model) { + BufferedImage image; + try { + image = ImageIO.read(file); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.parseBufferedImage(image); + } + + public Cape(URL url) { + BufferedImage image; + try { + image = ImageIO.read(url); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.parseBufferedImage(image); + } + + public Cape(BufferedImage image) { + this.parseBufferedImage(image); + } + + public Cape(String base64) { + this(Base64.getDecoder().decode(base64)); + } + + public void setData(byte[] data) { + this.data = data; + } + + public byte[] getData() { + return data; + } + + public void parseBufferedImage(BufferedImage image) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + Color color = new Color(image.getRGB(x, y), true); + outputStream.write(color.getRed()); + outputStream.write(color.getGreen()); + outputStream.write(color.getBlue()); + outputStream.write(color.getAlpha()); + } + } + image.flush(); + this.setData(outputStream.toByteArray()); + } + } } diff --git a/src/main/java/cn/nukkit/entity/item/EntityBoat.java b/src/main/java/cn/nukkit/entity/item/EntityBoat.java index ea2e3491ff..8c940db809 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityBoat.java +++ b/src/main/java/cn/nukkit/entity/item/EntityBoat.java @@ -1,19 +1,21 @@ package cn.nukkit.entity.item; import cn.nukkit.Player; -import cn.nukkit.Server; import cn.nukkit.entity.Entity; import cn.nukkit.event.entity.EntityDamageByEntityEvent; import cn.nukkit.event.entity.EntityDamageEvent; +import cn.nukkit.event.vehicle.VehicleDamageEvent; +import cn.nukkit.event.vehicle.VehicleDestroyEvent; +import cn.nukkit.event.vehicle.VehicleMoveEvent; +import cn.nukkit.event.vehicle.VehicleUpdateEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBoat; +import cn.nukkit.level.Location; import cn.nukkit.level.format.FullChunk; import cn.nukkit.level.particle.SmokeParticle; import cn.nukkit.math.Vector3; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.AddEntityPacket; -import cn.nukkit.network.protocol.EntityEventPacket; -import cn.nukkit.network.protocol.SetEntityLinkPacket; /** * Created by yescallop on 2016/2/13. @@ -58,6 +60,11 @@ protected float getGravity() { return 0.1f; } + @Override + public float getBaseOffset() { + return 0.35F; + } + @Override public int getNetworkId() { return NETWORK_ID; @@ -85,31 +92,49 @@ public void spawnTo(Player player) { @Override public boolean attack(EntityDamageEvent source) { - if (super.attack(source)) { + if (invulnerable) { + return false; + } else { + // Event start + VehicleDamageEvent event = new VehicleDamageEvent(this, source.getEntity(), source.getFinalDamage()); + getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // Event stop + performHurtAnimation((int) event.getDamage()); + + boolean instantKill = false; + if (source instanceof EntityDamageByEntityEvent) { Entity damager = ((EntityDamageByEntityEvent) source).getDamager(); - if (damager instanceof Player) { - if (((Player) damager).isCreative()) { - this.kill(); - } - if (this.getHealth() <= 0) { - if (((Player) damager).isSurvival() && this.level.getGameRules().getBoolean("doEntityDrops")) { - this.level.dropItem(this, new ItemBoat()); - } - this.close(); - } - } + instantKill = damager instanceof Player && ((Player) damager).isCreative(); } - EntityEventPacket pk = new EntityEventPacket(); - pk.eid = this.getId(); - pk.event = this.getHealth() <= 0 ? EntityEventPacket.DEATH_ANIMATION : EntityEventPacket.HURT_ANIMATION; - Server.broadcastPacket(this.hasSpawned.values(), pk); + if (instantKill || getDamage() > 40) { + // Event start + VehicleDestroyEvent event2 = new VehicleDestroyEvent(this, source.getEntity()); + getServer().getPluginManager().callEvent(event2); + if (event2.isCancelled()) { + return false; + } + // Event stop + if (linkedEntity != null) { + mountEntity(linkedEntity); + } - return true; - } else { - return false; + if (instantKill && (!hasCustomName())) { + kill(); + } else { + if (level.getGameRules().getBoolean("doEntityDrops")) { + this.level.dropItem(this, new ItemBoat()); + } + close(); + } + } } + + return true; } @Override @@ -141,6 +166,7 @@ public boolean onUpdate(int currentTick) { boolean hasUpdate = this.entityBaseTick(tickDiff); if (this.isAlive()) { + super.onUpdate(currentTick); this.motionY = (this.level.getBlock(new Vector3(this.x, this.y, this.z)).getBoundingBox() != null || this.isInsideOfWater()) ? getGravity() : -0.08; @@ -164,6 +190,15 @@ public boolean onUpdate(int currentTick) { this.motionY *= -0.5; } + Location from = new Location(lastX, lastY, lastZ, lastYaw, lastPitch, level); + Location to = new Location(this.x, this.y, this.z, this.yaw, this.pitch, level); + + this.getServer().getPluginManager().callEvent(new VehicleUpdateEvent(this)); + + if (!from.equals(to)) { + this.getServer().getPluginManager().callEvent(new VehicleMoveEvent(this, from, to)); + } + this.updateMovement(); } @@ -176,24 +211,7 @@ public boolean onInteract(Player player, Item item) { return false; } - SetEntityLinkPacket pk; - - pk = new SetEntityLinkPacket(); - pk.rider = this.getId(); //WTF - pk.riding = player.getId(); - pk.type = 2; - Server.broadcastPacket(this.hasSpawned.values(), pk); - - pk = new SetEntityLinkPacket(); - pk.rider = this.getId(); - pk.riding = 0; - pk.type = 2; - player.dataPacket(pk); - - player.riding = this; - this.linkedEntity = player; - - player.setDataFlag(DATA_FLAGS, DATA_FLAG_RIDING, true); + super.mountEntity(player); return true; } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/entity/item/EntityItem.java b/src/main/java/cn/nukkit/entity/item/EntityItem.java index 9baf3460c7..4fbca75fa9 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityItem.java +++ b/src/main/java/cn/nukkit/entity/item/EntityItem.java @@ -29,12 +29,12 @@ public int getNetworkId() { return NETWORK_ID; } - protected String owner = null; - protected String thrower = null; + protected String owner; + protected String thrower; protected Item item; - protected int pickupDelay = 0; + protected int pickupDelay; @Override public float getWidth() { @@ -100,6 +100,7 @@ protected void initEntity() { } this.item = NBTIO.getItemHelper(this.namedTag.getCompound("Item")); + this.setDataFlag(DATA_FLAGS, DATA_FLAG_IMMOBILE, true); this.server.getPluginManager().callEvent(new ItemSpawnEvent(this)); } @@ -133,12 +134,19 @@ public boolean onUpdate(int currentTick) { boolean hasUpdate = this.entityBaseTick(tickDiff); if (this.isAlive()) { - if (this.pickupDelay > 0 && this.pickupDelay < 32767) { this.pickupDelay -= tickDiff; if (this.pickupDelay < 0) { this.pickupDelay = 0; } + } else { + for (Entity entity : this.level.getNearbyEntities(this.boundingBox.grow(1, 0.5, 1), this)) { + if (entity instanceof Player) { + if (((Player) entity).pickupEntity(this, true)) { + return true; + } + } + } } this.motionY -= this.getGravity(); @@ -255,4 +263,9 @@ public void spawnTo(Player player) { super.spawnTo(player); } + + @Override + public boolean doesTriggerPressurePlate() { + return true; + } } diff --git a/src/main/java/cn/nukkit/entity/item/EntityMinecartAbstract.java b/src/main/java/cn/nukkit/entity/item/EntityMinecartAbstract.java new file mode 100644 index 0000000000..93221589a6 --- /dev/null +++ b/src/main/java/cn/nukkit/entity/item/EntityMinecartAbstract.java @@ -0,0 +1,825 @@ +package cn.nukkit.entity.item; + +import cn.nukkit.Player; +import cn.nukkit.api.API; +import cn.nukkit.api.API.Definition; +import cn.nukkit.api.API.Usage; +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockRail; +import cn.nukkit.block.BlockRailActivator; +import cn.nukkit.block.BlockRailPowered; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.EntityHuman; +import cn.nukkit.entity.EntityLiving; +import cn.nukkit.entity.data.ByteEntityData; +import cn.nukkit.entity.data.IntEntityData; +import cn.nukkit.event.entity.EntityDamageByEntityEvent; +import cn.nukkit.event.entity.EntityDamageEvent; +import cn.nukkit.event.vehicle.VehicleMoveEvent; +import cn.nukkit.event.vehicle.VehicleUpdateEvent; +import cn.nukkit.item.Item; +import cn.nukkit.item.ItemMinecart; +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; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.network.protocol.AddEntityPacket; +import cn.nukkit.utils.MinecartType; +import cn.nukkit.utils.Rail; +import cn.nukkit.utils.Rail.Orientation; + +import java.util.Objects; + +/** + * Created by: larryTheCoder on 2017/6/26. + *

+ * Nukkit Project, + * Minecart and Riding Project, + * Package cn.nukkit.entity.item in project Nukkit. + */ +public abstract class EntityMinecartAbstract extends EntityVehicle { + + private String entityName; + private static final int[][][] matrix = new int[][][]{ + {{0, 0, -1}, {0, 0, 1}}, + {{-1, 0, 0}, {1, 0, 0}}, + {{-1, -1, 0}, {1, 0, 0}}, + {{-1, 0, 0}, {1, -1, 0}}, + {{0, 0, -1}, {0, -1, 1}}, + {{0, -1, -1}, {0, 0, 1}}, + {{0, 0, 1}, {1, 0, 0}}, + {{0, 0, 1}, {-1, 0, 0}}, + {{0, 0, -1}, {-1, 0, 0}}, + {{0, 0, -1}, {1, 0, 0}} + }; + private double currentSpeed = 0; + private Block blockInside; + // Plugins modifiers + private boolean slowWhenEmpty = true; + private double derailedX = 0.5; + private double derailedY = 0.5; + private double derailedZ = 0.5; + private double flyingX = 0.95; + private double flyingY = 0.95; + private double flyingZ = 0.95; + private double maxSpeed = 0.4D; + private final boolean devs = false; // Avoid maintained features into production + + public abstract MinecartType getType(); + + public EntityMinecartAbstract(FullChunk chunk, CompoundTag nbt) { + super(chunk, nbt); + } + + @Override + public float getHeight() { + return 0.7F; + } + + @Override + public float getWidth() { + return 0.98F; + } + + @Override + protected float getDrag() { + return 0.1F; + } + + public void setName(String name) { + entityName = name; + } + + @Override + public String getName() { + return entityName; + } + + @Override + public float getBaseOffset() { + return 0.35F; + } + + @Override + public boolean hasCustomName() { + return entityName != null; + } + + @Override + public boolean canDoInteraction() { + return linkedEntity == null && this.getDisplayBlock() == null; + } + + @Override + public float getMountedYOffset() { + return 0.45F; // Real minecart offset + } + + @Override + public void initEntity() { + super.initEntity(); + + prepareDataProperty(); + } + + @Override + public boolean onUpdate(int currentTick) { + if (this.closed) { + return false; + } + + if (!this.isAlive()) { + ++this.deadTicks; + if (this.deadTicks >= 10) { + this.despawnFromAll(); + this.close(); + } + return this.deadTicks < 10; + } + + int tickDiff = currentTick - this.lastUpdate; + + if (tickDiff <= 0) { + return false; + } + + this.lastUpdate = currentTick; + + if (isAlive()) { + super.onUpdate(currentTick); + + // Entity variables + lastX = x; + lastY = y; + lastZ = z; + motionY -= 0.03999999910593033D; + int dx = MathHelper.floor(x); + int dy = MathHelper.floor(y); + int dz = MathHelper.floor(z); + + // Some hack to check rails + if (Rail.isRailBlock(level.getBlockIdAt(dx, dy - 1, dz))) { + --dy; + } + + Block block = level.getBlock(new Vector3(dx, dy, dz)); + + // Ensure that the block is a rail + if (Rail.isRailBlock(block)) { + processMovement(dx, dy, dz, (BlockRail) block); + if (block instanceof BlockRailActivator) { + // Activate the minecart/TNT + activate(dx, dy, dz, (block.getDamage() & 0x8) != 0); + } + } else { + setFalling(); + } + checkBlockCollision(); + + // Minecart head + pitch = 0; + double diffX = this.lastX - this.x; + double diffZ = this.lastZ - this.z; + double yawToChange = yaw; + if (diffX * diffX + diffZ * diffZ > 0.001D) { + yawToChange = (Math.atan2(diffZ, diffX) * 180 / 3.141592653589793D); + } + + // Reverse yaw if yaw is below 0 + if (yawToChange < 0) { + // -90-(-90)-(-90) = 90 + yawToChange -= yawToChange - yawToChange; + } + + setRotation(yawToChange, pitch); + + Location from = new Location(lastX, lastY, lastZ, lastYaw, lastPitch, level); + Location to = new Location(this.x, this.y, this.z, this.yaw, this.pitch, level); + + this.getServer().getPluginManager().callEvent(new VehicleUpdateEvent(this)); + + if (!from.equals(to)) { + this.getServer().getPluginManager().callEvent(new VehicleMoveEvent(this, from, to)); + } + + // Collisions + for (Entity entity : level.getNearbyEntities(boundingBox.grow(0.2D, 0, 0.2D), this)) { + if (entity != linkedEntity && entity instanceof EntityMinecartAbstract) { + entity.applyEntityCollision(this); + } + } + + // Easier + if ((linkedEntity != null) && (!linkedEntity.isAlive())) { + if (linkedEntity.riding == this) { + linkedEntity.riding = null; + } + linkedEntity = null; + } + // No need to onGround or Motion diff! This always have an update + return true; + } + return false; + } + + @Override + public boolean attack(EntityDamageEvent source) { + if (invulnerable) { + return false; + } else { + Entity damager = ((EntityDamageByEntityEvent) source).getDamager(); + boolean instantKill = damager instanceof Player && ((Player) damager).isCreative(); + if (!instantKill) performHurtAnimation((int) source.getFinalDamage()); + + if (instantKill || getDamage() > 40) { + if (linkedEntity != null) { + mountEntity(linkedEntity); + } + + if (instantKill && (!hasCustomName())) { + kill(); + } else { + if (level.getGameRules().getBoolean("doEntityDrops")) { + dropItem(); + } + close(); + } + } + } + + return true; + } + + public void dropItem() { + level.dropItem(this, new ItemMinecart()); + } + + @Override + public void close() { + super.close(); + + if (linkedEntity instanceof Player) { + linkedEntity.riding = null; + linkedEntity = null; + } + + SmokeParticle particle = new SmokeParticle(this); + level.addParticle(particle); + } + + @Override + public boolean onInteract(Player p, Item item) { + if (linkedEntity != null) { + return false; + } + + if (blockInside == null) { + mountEntity(p); // Simple + } + return true; + } + + @Override + public void spawnTo(Player player) { + AddEntityPacket pk = new AddEntityPacket(); + pk.entityUniqueId = getId(); + pk.entityRuntimeId = getId(); + pk.type = getNetworkId(); + pk.x = (float) x; + pk.y = (float) y; + pk.z = (float) z; + pk.speedX = 0; + pk.speedY = 0; + pk.speedZ = 0; + pk.yaw = 0; + pk.pitch = 0; + pk.metadata = dataProperties; + player.dataPacket(pk); + + super.spawnTo(player); + } + + @Override + public void applyEntityCollision(Entity entity) { + if (entity != riding) { + if (entity instanceof EntityLiving + && !(entity instanceof EntityHuman) + && motionX * motionX + motionZ * motionZ > 0.01D + && linkedEntity == null + && entity.riding == null + && blockInside == null) { + if (riding == null && devs) { + mountEntity(entity);// TODO: rewrite (weird riding) + } + } + + double motiveX = entity.x - x; + double motiveZ = entity.z - z; + double square = motiveX * motiveX + motiveZ * motiveZ; + + if (square >= 9.999999747378752E-5D) { + square = Math.sqrt(square); + motiveX /= square; + motiveZ /= square; + double next = 1 / square; + + if (next > 1) { + next = 1; + } + + motiveX *= next; + motiveZ *= next; + motiveX *= 0.10000000149011612D; + motiveZ *= 0.10000000149011612D; + motiveX *= 1 + entityCollisionReduction; + motiveZ *= 1 + entityCollisionReduction; + motiveX *= 0.5D; + motiveZ *= 0.5D; + if (entity instanceof EntityMinecartAbstract) { + EntityMinecartAbstract mine = (EntityMinecartAbstract) entity; + double desinityX = mine.x - x; + double desinityZ = mine.z - z; + Vector3 vector = new Vector3(desinityX, 0, desinityZ).normalize(); + Vector3 vec = new Vector3((double) MathHelper.cos((float) yaw * 0.017453292F), 0, (double) MathHelper.sin((float) yaw * 0.017453292F)).normalize(); + double desinityXZ = Math.abs(vector.dot(vec)); + + if (desinityXZ < 0.800000011920929D) { + return; + } + + double motX = mine.motionX + motionX; + double motZ = mine.motionZ + motionZ; + + if (mine.getType().getId() == 2 && getType().getId() != 2) { + motionX *= 0.20000000298023224D; + motionZ *= 0.20000000298023224D; + motionX += mine.motionX - motiveX; + motionZ += mine.motionZ - motiveZ; + mine.motionX *= 0.949999988079071D; + mine.motionZ *= 0.949999988079071D; + } else if (mine.getType().getId() != 2 && getType().getId() == 2) { + mine.motionX *= 0.20000000298023224D; + mine.motionZ *= 0.20000000298023224D; + motionX += mine.motionX + motiveX; + motionZ += mine.motionZ + motiveZ; + motionX *= 0.949999988079071D; + motionZ *= 0.949999988079071D; + } else { + motX /= 2; + motZ /= 2; + motionX *= 0.20000000298023224D; + motionZ *= 0.20000000298023224D; + motionX += motX - motiveX; + motionZ += motZ - motiveZ; + mine.motionX *= 0.20000000298023224D; + mine.motionZ *= 0.20000000298023224D; + mine.motionX += motX + motiveX; + mine.motionZ += motZ + motiveZ; + } + } else { + motionX -= motiveX; + motionZ -= motiveZ; + } + } + } + } + + @Override + public void saveNBT() { + super.saveNBT(); + + saveEntityData(); + } + + public double getMaxSpeed() { + return maxSpeed; + } + + protected void activate(int x, int y, int z, boolean flag) { + } + + private boolean hasUpdated = false; + + private void setFalling() { + motionX = NukkitMath.clamp(motionX, -getMaxSpeed(), getMaxSpeed()); + motionZ = NukkitMath.clamp(motionZ, -getMaxSpeed(), getMaxSpeed()); + + if (linkedEntity != null && !hasUpdated) { + updateRiderPosition(getMountedYOffset() + 0.35F); + hasUpdated = true; + } else { + hasUpdated = false; + } + + if (onGround) { + motionX *= derailedX; + motionY *= derailedY; + motionZ *= derailedZ; + } + + move(motionX, motionY, motionZ); + if (!onGround) { + motionX *= flyingX; + motionY *= flyingY; + motionZ *= flyingZ; + } + } + + private void processMovement(int dx, int dy, int dz, BlockRail block) { + fallDistance = 0.0F; + Vector3 vector = getNextRail(x, y, z); + + y = (double) dy; + boolean isPowered = false; + boolean isSlowed = false; + + if (block instanceof BlockRailPowered) { + isPowered = block.isActive(); + isSlowed = !block.isActive(); + } + + switch (Orientation.byMetadata(block.getRealMeta())) { + case ASCENDING_NORTH: + motionX -= 0.0078125D; + y += 1; + break; + case ASCENDING_SOUTH: + motionX += 0.0078125D; + y += 1; + break; + case ASCENDING_EAST: + motionZ += 0.0078125D; + y += 1; + break; + case ASCENDING_WEST: + motionZ -= 0.0078125D; + y += 1; + break; + } + + int[][] facing = matrix[block.getRealMeta()]; + double facing1 = (double) (facing[1][0] - facing[0][0]); + double facing2 = (double) (facing[1][2] - facing[0][2]); + double speedOnTurns = Math.sqrt(facing1 * facing1 + facing2 * facing2); + double realFacing = motionX * facing1 + motionZ * facing2; + + if (realFacing < 0) { + facing1 = -facing1; + facing2 = -facing2; + } + + double squareOfFame = Math.sqrt(motionX * motionX + motionZ * motionZ); + + if (squareOfFame > 2) { + squareOfFame = 2; + } + + motionX = squareOfFame * facing1 / speedOnTurns; + motionZ = squareOfFame * facing2 / speedOnTurns; + double expectedSpeed; + double playerYawNeg; // PlayerYawNegative + double playerYawPos; // PlayerYawPositive + double motion; + + if (linkedEntity != null && linkedEntity instanceof EntityLiving) { + expectedSpeed = currentSpeed; + if (expectedSpeed > 0) { + // This is a trajectory (Angle of elevation) + playerYawNeg = -Math.sin(linkedEntity.yaw * 3.1415927F / 180.0F); + playerYawPos = Math.cos(linkedEntity.yaw * 3.1415927F / 180.0F); + motion = motionX * motionX + motionZ * motionZ; + if (motion < 0.01D) { + motionX += playerYawNeg * 0.1D; + motionZ += playerYawPos * 0.1D; + + isSlowed = false; + } + } + } + + //http://minecraft.gamepedia.com/Powered_Rail#Rail + if (isSlowed) { + expectedSpeed = Math.sqrt(motionX * motionX + motionZ * motionZ); + if (expectedSpeed < 0.03D) { + motionX *= 0; + motionY *= 0; + motionZ *= 0; + } else { + motionX *= 0.5D; + motionY *= 0; + motionZ *= 0.5D; + } + } + + playerYawNeg = (double) dx + 0.5D + (double) facing[0][0] * 0.5D; + playerYawPos = (double) dz + 0.5D + (double) facing[0][2] * 0.5D; + motion = (double) dx + 0.5D + (double) facing[1][0] * 0.5D; + double wallOfFame = (double) dz + 0.5D + (double) facing[1][2] * 0.5D; + + facing1 = motion - playerYawNeg; + facing2 = wallOfFame - playerYawPos; + double motX; + double motZ; + + if (facing1 == 0) { + x = (double) dx + 0.5D; + expectedSpeed = z - (double) dz; + } else if (facing2 == 0) { + z = (double) dz + 0.5D; + expectedSpeed = x - (double) dx; + } else { + motX = x - playerYawNeg; + motZ = z - playerYawPos; + expectedSpeed = (motX * facing1 + motZ * facing2) * 2; + } + + x = playerYawNeg + facing1 * expectedSpeed; + z = playerYawPos + facing2 * expectedSpeed; + setPosition(new Vector3(x, y, z)); // Hehe, my minstake :3 + + motX = motionX; + motZ = motionZ; + if (linkedEntity != null) { + motX *= 0.75D; + motZ *= 0.75D; + } + motX = NukkitMath.clamp(motX, -getMaxSpeed(), getMaxSpeed()); + motZ = NukkitMath.clamp(motZ, -getMaxSpeed(), getMaxSpeed()); + + move(motX, 0, motZ); + if (facing[0][1] != 0 && MathHelper.floor(x) - dx == facing[0][0] && MathHelper.floor(z) - dz == facing[0][2]) { + setPosition(new Vector3(x, y + (double) facing[0][1], z)); + } else if (facing[1][1] != 0 && MathHelper.floor(x) - dx == facing[1][0] && MathHelper.floor(z) - dz == facing[1][2]) { + setPosition(new Vector3(x, y + (double) facing[1][1], z)); + } + + applyDrag(); + Vector3 vector1 = getNextRail(x, y, z); + + if (vector1 != null && vector != null) { + double d14 = (vector.y - vector1.y) * 0.05D; + + squareOfFame = Math.sqrt(motionX * motionX + motionZ * motionZ); + if (squareOfFame > 0) { + motionX = motionX / squareOfFame * (squareOfFame + d14); + motionZ = motionZ / squareOfFame * (squareOfFame + d14); + } + + setPosition(new Vector3(x, vector1.y, z)); + } + + int floorX = MathHelper.floor(x); + int floorZ = MathHelper.floor(z); + + if (floorX != dx || floorZ != dz) { + squareOfFame = Math.sqrt(motionX * motionX + motionZ * motionZ); + motionX = squareOfFame * (double) (floorX - dx); + motionZ = squareOfFame * (double) (floorZ - dz); + } + + if (isPowered) { + double newMovie = Math.sqrt(motionX * motionX + motionZ * motionZ); + + if (newMovie > 0.01D) { + double nextMovie = 0.06D; + + motionX += motionX / newMovie * nextMovie; + motionZ += motionZ / newMovie * nextMovie; + } else if (block.getOrientation() == Orientation.STRAIGHT_NORTH_SOUTH) { + if (level.getBlock(new Vector3(dx - 1, dy, dz)).isNormalBlock()) { + motionX = 0.02D; + } else if (level.getBlock(new Vector3(dx + 1, dy, dz)).isNormalBlock()) { + motionX = -0.02D; + } + } else if (block.getOrientation() == Orientation.STRAIGHT_EAST_WEST) { + if (level.getBlock(new Vector3(dx, dy, dz - 1)).isNormalBlock()) { + motionZ = 0.02D; + } else if (level.getBlock(new Vector3(dx, dy, dz + 1)).isNormalBlock()) { + motionZ = -0.02D; + } + } + } + + } + + private void applyDrag() { + if (linkedEntity != null || !slowWhenEmpty) { + motionX *= 0.996999979019165D; + motionY *= 0.0D; + motionZ *= 0.996999979019165D; + } else { + motionX *= 0.9599999785423279D; + motionY *= 0.0D; + motionZ *= 0.9599999785423279D; + } + } + + private Vector3 getNextRail(double dx, double dy, double dz) { + int checkX = MathHelper.floor(dx); + int checkY = MathHelper.floor(dy); + int checkZ = MathHelper.floor(dz); + + if (Rail.isRailBlock(level.getBlockIdAt(checkX, checkY - 1, checkZ))) { + --checkY; + } + + Block block = level.getBlock(new Vector3(checkX, checkY, checkZ)); + + if (Rail.isRailBlock(block)) { + int[][] facing = matrix[((BlockRail) block).getRealMeta()]; + double rail; + // Genisys mistake (Doesn't check surrounding more exactly) + double nextOne = (double) checkX + 0.5D + (double) facing[0][0] * 0.5D; + double nextTwo = (double) checkY + 0.5D + (double) facing[0][1] * 0.5D; + double nextThree = (double) checkZ + 0.5D + (double) facing[0][2] * 0.5D; + double nextFour = (double) checkX + 0.5D + (double) facing[1][0] * 0.5D; + double nextFive = (double) checkY + 0.5D + (double) facing[1][1] * 0.5D; + double nextSix = (double) checkZ + 0.5D + (double) facing[1][2] * 0.5D; + double nextSeven = nextFour - nextOne; + double nextEight = (nextFive - nextTwo) * 2; + double nextMax = nextSix - nextThree; + + if (nextSeven == 0) { + rail = dz - (double) checkZ; + } else if (nextMax == 0) { + rail = dx - (double) checkX; + } else { + double whatOne = dx - nextOne; + double whatTwo = dz - nextThree; + + rail = (whatOne * nextSeven + whatTwo * nextMax) * 2; + } + + dx = nextOne + nextSeven * rail; + dy = nextTwo + nextEight * rail; + dz = nextThree + nextMax * rail; + if (nextEight < 0) { + ++dy; + } + + if (nextEight > 0) { + dy += 0.5D; + } + + return new Vector3(dx, dy, dz); + } else { + return null; + } + } + + /** + * Used to multiply the minecart current speed + * + * @param speed The speed of the minecart that will be calculated + */ + public void setCurrentSpeed(double speed) { + currentSpeed = speed; + } + + private void prepareDataProperty() { + setRollingAmplitude(0); + setRollingDirection(1); + setDamage(0); + if (namedTag.contains("CustomDisplayTile")) { + if (namedTag.getBoolean("CustomDisplayTile")) { + int display = namedTag.getInt("DisplayTile"); + int offSet = namedTag.getInt("DisplayOffset"); + setDataProperty(new ByteEntityData(DATA_MINECART_HAS_DISPLAY, 1)); + setDataProperty(new IntEntityData(DATA_MINECART_DISPLAY_BLOCK, display)); + setDataProperty(new IntEntityData(DATA_MINECART_DISPLAY_OFFSET, offSet)); + } + } else { + int display = blockInside == null ? 0 + : blockInside.getId() + | blockInside.getDamage() << 16; + if (display == 0) { + setDataProperty(new ByteEntityData(DATA_MINECART_HAS_DISPLAY, 0)); + return; + } + setDataProperty(new ByteEntityData(DATA_MINECART_HAS_DISPLAY, 1)); + setDataProperty(new IntEntityData(DATA_MINECART_DISPLAY_BLOCK, display)); + setDataProperty(new IntEntityData(DATA_MINECART_DISPLAY_OFFSET, 6)); + } + } + + private void saveEntityData() { + boolean hasDisplay = super.getDataPropertyByte(DATA_MINECART_HAS_DISPLAY) == 1 + || blockInside != null; + int display; + int offSet; + namedTag.putBoolean("CustomDisplayTile", hasDisplay); + if (hasDisplay) { + display = blockInside.getId() + | blockInside.getDamage() << 16; + offSet = getDataPropertyInt(DATA_MINECART_DISPLAY_OFFSET); + namedTag.putInt("DisplayTile", display); + namedTag.putInt("DisplayOffset", offSet); + } + } + + /** + * Set the minecart display block! + * + * @param block The block that will changed. Set {@code null} for BlockAir + * @return {@code true} if the block is normal block + */ + @API(usage = Usage.MAINTAINED, definition = Definition.UNIVERSAL) + public boolean setDisplayBlock(Block block) { + if (block != null) { + if (block.isNormalBlock()) { + blockInside = block; + int display = blockInside.getId() + | blockInside.getDamage() << 16; + setDataProperty(new ByteEntityData(DATA_MINECART_HAS_DISPLAY, 1)); + setDataProperty(new IntEntityData(DATA_MINECART_DISPLAY_BLOCK, display)); + setDisplayBlockOffset(6); + } + } else { + // Set block to air (default). + blockInside = null; + setDataProperty(new ByteEntityData(DATA_MINECART_HAS_DISPLAY, 0)); + setDataProperty(new IntEntityData(DATA_MINECART_DISPLAY_BLOCK, 0)); + setDisplayBlockOffset(0); + } + return true; + } + + /** + * Get the minecart display block + * + * @return Block of minecart display block + */ + @API(usage = Usage.STABLE, definition = Definition.UNIVERSAL) + public Block getDisplayBlock() { + return blockInside; + } + + /** + * Set the block offset. + * + * @param offset The offset + */ + @API(usage = Usage.EXPERIMENTAL, definition = Definition.PLATFORM_NATIVE) + public void setDisplayBlockOffset(int offset) { + setDataProperty(new IntEntityData(DATA_MINECART_DISPLAY_OFFSET, offset)); + } + + /** + * Get the block display offset + * + * @return integer + */ + @API(usage = Usage.EXPERIMENTAL, definition = Definition.UNIVERSAL) + public int getDisplayBlockOffset() { + return super.getDataPropertyInt(DATA_MINECART_DISPLAY_OFFSET); + } + + /** + * Is the minecart can be slowed when empty? + * + * @return boolean + */ + @API(usage = Usage.EXPERIMENTAL, definition = Definition.UNIVERSAL) + public boolean isSlowWhenEmpty() { + return slowWhenEmpty; + } + + /** + * Set the minecart slowdown flag + * + * @param slow The slowdown flag + */ + @API(usage = Usage.EXPERIMENTAL, definition = Definition.UNIVERSAL) + public void setSlowWhenEmpty(boolean slow) { + slowWhenEmpty = slow; + } + + public Vector3 getFlyingVelocityMod() { + return new Vector3(flyingX, flyingY, flyingZ); + } + + public void setFlyingVelocityMod(Vector3 flying) { + Objects.requireNonNull(flying, "Flying velocity modifiers cannot be null"); + flyingX = flying.getX(); + flyingY = flying.getY(); + flyingZ = flying.getZ(); + } + + public Vector3 getDerailedVelocityMod() { + return new Vector3(derailedX, derailedY, derailedZ); + } + + public void setDerailedVelocityMod(Vector3 derailed) { + Objects.requireNonNull(derailed, "Derailed velocity modifiers cannot be null"); + derailedX = derailed.getX(); + derailedY = derailed.getY(); + derailedZ = derailed.getZ(); + } + + public void setMaximumSpeed(double speed) { + maxSpeed = speed; + } +} diff --git a/src/main/java/cn/nukkit/entity/item/EntityMinecartChest.java b/src/main/java/cn/nukkit/entity/item/EntityMinecartChest.java index 208725bd58..01b5fe1cd0 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityMinecartChest.java +++ b/src/main/java/cn/nukkit/entity/item/EntityMinecartChest.java @@ -1,20 +1,40 @@ package cn.nukkit.entity.item; +import cn.nukkit.block.BlockChest; +import cn.nukkit.item.ItemMinecartChest; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.utils.MinecartType; /** * Created by Snake1999 on 2016/1/30. * Package cn.nukkit.entity.item in project Nukkit. */ -public class EntityMinecartChest extends EntityMinecartEmpty { +public class EntityMinecartChest extends EntityMinecartAbstract { - public static final int NETWORK_ID = 62; + public static final int NETWORK_ID = 98; public EntityMinecartChest(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); + super.setDisplayBlock(new BlockChest()); } // TODO: 2016/1/30 inventory + @Override + public MinecartType getType() { + return MinecartType.valueOf(1); + } + + @Override + public int getNetworkId() { + return NETWORK_ID; + } + + @Override + public void dropItem() { + level.dropItem(this, new ItemMinecartChest()); + } + + } diff --git a/src/main/java/cn/nukkit/entity/item/EntityMinecartEmpty.java b/src/main/java/cn/nukkit/entity/item/EntityMinecartEmpty.java index fe71a83ebd..137b83cc34 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityMinecartEmpty.java +++ b/src/main/java/cn/nukkit/entity/item/EntityMinecartEmpty.java @@ -1,46 +1,17 @@ package cn.nukkit.entity.item; -import cn.nukkit.Player; -import cn.nukkit.event.entity.EntityDamageEvent; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; -import cn.nukkit.network.protocol.AddEntityPacket; -import cn.nukkit.network.protocol.EntityEventPacket; +import cn.nukkit.utils.MinecartType; /** * Created by Snake1999 on 2016/1/30. * Package cn.nukkit.entity.item in project Nukkit. */ -public class EntityMinecartEmpty extends EntityVehicle { +public class EntityMinecartEmpty extends EntityMinecartAbstract { public static final int NETWORK_ID = 84; - public static final int DATA_VEHICLE_DISPLAY_BLOCK = 20; - public static final int DATA_VEHICLE_DISPLAY_DATA = 20; - public static final int DATA_VEHICLE_DISPLAY_OFFSET = 21; - public static final int DATA_VEHICLE_CUSTOM_DISPLAY = 22; - - // TODO: 2016/1/30 check if these numbers correct - @Override - public float getHeight() { - return 0.7f; - } - - @Override - public float getWidth() { - return 0.98f; - } - - @Override - protected float getDrag() { - return 0.1f; - } - - @Override - protected float getGravity() { - return 0.5f; - } - @Override public int getNetworkId() { return NETWORK_ID; @@ -51,51 +22,18 @@ public EntityMinecartEmpty(FullChunk chunk, CompoundTag nbt) { } @Override - protected void initEntity() { - //? - super.initEntity(); - } - - @Override - public boolean onUpdate(int currentTick) { - // TODO: 2016/1/30 run run run! - // TODO: 2016/1/30 split to onXXXRailPass, such as, protected void onActivatorRailPass(...) - return super.onUpdate(currentTick); + public MinecartType getType() { + return MinecartType.valueOf(0); } @Override - public void spawnTo(Player player) { - AddEntityPacket pk = new AddEntityPacket(); - pk.entityUniqueId = this.getId(); - pk.entityRuntimeId = this.getId(); - pk.type = EntityMinecartEmpty.NETWORK_ID; - pk.x = (float) this.x; - pk.y = (float) this.y; - pk.z = (float) this.z; - pk.speedX = 0; - pk.speedY = 0; - pk.speedZ = 0; - pk.yaw = 0; - pk.pitch = 0; - pk.metadata = this.dataProperties; - player.dataPacket(pk); - - super.spawnTo(player); - } - - @Override - public boolean attack(EntityDamageEvent source) { - if (super.attack(source)) { - EntityEventPacket pk = new EntityEventPacket(); - pk.eid = this.id; - pk.event = EntityEventPacket.HURT_ANIMATION; - for (Player aPlayer : this.getLevel().getPlayers().values()) { - aPlayer.dataPacket(pk); + protected void activate(int x, int y, int z, boolean flag) { + if (flag) { + if (this.riding != null) { + mountEntity(riding); } - return true; - } else { - return false; + // looks like MCPE and MCPC not same XD + // removed rolling feature from here because of MCPE logic? } } - } diff --git a/src/main/java/cn/nukkit/entity/item/EntityMinecartHopper.java b/src/main/java/cn/nukkit/entity/item/EntityMinecartHopper.java index 4f77c2cfae..7a26832d5f 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityMinecartHopper.java +++ b/src/main/java/cn/nukkit/entity/item/EntityMinecartHopper.java @@ -1,16 +1,34 @@ package cn.nukkit.entity.item; +import cn.nukkit.block.BlockHopper; +import cn.nukkit.item.ItemMinecartHopper; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.utils.MinecartType; -public class EntityMinecartHopper extends EntityMinecartEmpty { +public class EntityMinecartHopper extends EntityMinecartAbstract { public static final int NETWORK_ID = 96; public EntityMinecartHopper(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); + super.setDisplayBlock(new BlockHopper()); } // TODO: 2016/12/18 inventory + @Override + public MinecartType getType() { + return MinecartType.valueOf(5); + } + + @Override + public int getNetworkId() { + return NETWORK_ID; + } + + @Override + public void dropItem() { + level.dropItem(this, new ItemMinecartHopper()); + } } diff --git a/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java b/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java index ba95adbeb1..2057d1c47c 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java +++ b/src/main/java/cn/nukkit/entity/item/EntityMinecartTNT.java @@ -1,33 +1,59 @@ package cn.nukkit.entity.item; +import cn.nukkit.block.BlockTNT; +import cn.nukkit.entity.EntityExplosive; import cn.nukkit.event.entity.EntityExplosionPrimeEvent; +import cn.nukkit.item.ItemMinecartTNT; import cn.nukkit.level.Explosion; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.utils.MinecartType; + +import java.util.Random; /** - * Created by Snake1999 on 2016/1/30. - * Package cn.nukkit.entity.item in project Nukkit. + * Author: Adam Matthew [larryTheCoder] + *

+ * Nukkit Project. */ -public class EntityMinecartTNT extends EntityMinecartEmpty { +public class EntityMinecartTNT extends EntityMinecartAbstract implements EntityExplosive { - public static final int NETWORK_ID = 98; + public static final int NETWORK_ID = 97; //wtf? + private int fuse; + private boolean activated; public EntityMinecartTNT(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); + super.setDisplayBlock(new BlockTNT()); } - public boolean onUpdate(int currentTick) { - /*Block downSide = this.getLocation().floor().getLevelBlock(); - if (downSide.getId() == Block.ACTIVATOR_RAIL && downSide.isPowered()) { TODO: implement - explode(); - kill(); - }*/ - return true; + @Override + public void initEntity() { + super.initEntity(); + + this.fuse = namedTag.getInt("TNTFuse"); + this.setDataFlag(DATA_FLAGS, DATA_FLAG_CHARGED, false); } + @Override + public void activate(int i, int j, int k, boolean flag) { + // TODO: Find out why minecart doesnt have a right TNT fuse length + // Could be implemented in the future! + } + + @Override public void explode() { - EntityExplosionPrimeEvent event = new EntityExplosionPrimeEvent(this, 4); + explode(0); + } + + public void explode(double square) { + double root = Math.sqrt(square); + + if (root > 5.0D) { + root = 5.0D; + } + + EntityExplosionPrimeEvent event = new EntityExplosionPrimeEvent(this, (4.0D + new Random().nextDouble() * 1.5D * root)); server.getPluginManager().callEvent(event); if (event.isCancelled()) { return; @@ -37,6 +63,28 @@ public void explode() { explosion.explodeA(); } explosion.explodeB(); + kill(); } + @Override + public void dropItem() { + level.dropItem(this, new ItemMinecartTNT()); + } + + @Override + public MinecartType getType() { + return MinecartType.valueOf(3); + } + + @Override + public int getNetworkId() { + return EntityMinecartTNT.NETWORK_ID; + } + + @Override + public void saveNBT() { + super.saveNBT(); + + super.namedTag.putInt("TNTFuse", this.fuse); + } } diff --git a/src/main/java/cn/nukkit/entity/item/EntityPainting.java b/src/main/java/cn/nukkit/entity/item/EntityPainting.java index 14a34b2252..33f373aabb 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityPainting.java +++ b/src/main/java/cn/nukkit/entity/item/EntityPainting.java @@ -91,7 +91,7 @@ public void spawnTo(Player player) { @Override public boolean attack(EntityDamageEvent source) { - if (super.attack(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("doEntityDrops")) { diff --git a/src/main/java/cn/nukkit/entity/item/EntityPrimedTNT.java b/src/main/java/cn/nukkit/entity/item/EntityPrimedTNT.java index 68054a5b6d..f957aef28e 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityPrimedTNT.java +++ b/src/main/java/cn/nukkit/entity/item/EntityPrimedTNT.java @@ -12,7 +12,6 @@ import cn.nukkit.level.sound.TNTPrimeSound; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.AddEntityPacket; -import cn.nukkit.network.protocol.LevelSoundEventPacket; /** * @author MagicDroidX diff --git a/src/main/java/cn/nukkit/entity/item/EntityVehicle.java b/src/main/java/cn/nukkit/entity/item/EntityVehicle.java index 69c2d85590..1edfe83d18 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityVehicle.java +++ b/src/main/java/cn/nukkit/entity/item/EntityVehicle.java @@ -1,46 +1,163 @@ package cn.nukkit.entity.item; +import cn.nukkit.Player; +import cn.nukkit.Server; import cn.nukkit.entity.Entity; +import cn.nukkit.entity.EntityInteractable; import cn.nukkit.entity.EntityRideable; -import cn.nukkit.entity.data.FloatEntityData; import cn.nukkit.entity.data.IntEntityData; +import cn.nukkit.event.entity.EntityVehicleEnterEvent; +import cn.nukkit.event.entity.EntityVehicleExitEvent; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.network.protocol.SetEntityLinkPacket; + +import java.util.Objects; /** * author: MagicDroidX * Nukkit Project */ -public abstract class EntityVehicle extends Entity implements EntityRideable { - public static final int DATA_HURT_TIME = 17; - public static final int DATA_HURT_DIRECTION = 18; - public static final int DATA_DAMAGE_TAKEN = 19; +public abstract class EntityVehicle extends Entity implements EntityRideable, EntityInteractable { public EntityVehicle(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); } - public int getHurtTime() { - return this.getDataPropertyInt(EntityVehicle.DATA_HURT_TIME); + public int getRollingAmplitude() { + return this.getDataPropertyInt(DATA_HURT_TIME); + } + + public void setRollingAmplitude(int time) { + this.setDataProperty(new IntEntityData(DATA_HURT_TIME, time)); } - public void setHurtTime(int time) { - this.setDataProperty(new IntEntityData(EntityVehicle.DATA_HURT_TIME, time)); + public int getRollingDirection() { + return this.getDataPropertyInt(DATA_HURT_DIRECTION); } - public int getHurtDirection() { - return this.getDataPropertyInt(EntityVehicle.DATA_HURT_DIRECTION); + public void setRollingDirection(int direction) { + this.setDataProperty(new IntEntityData(DATA_HURT_DIRECTION, direction)); } - public void setHurtDirection(int direction) { - this.setDataProperty(new IntEntityData(EntityVehicle.DATA_HURT_DIRECTION, direction)); + public int getDamage() { + return this.getDataPropertyInt(DATA_HEALTH); // false data name (should be DATA_DAMAGE_TAKEN) } - public float getDamageTaken() { - return this.getDataPropertyFloat(EntityVehicle.DATA_DAMAGE_TAKEN); + public void setDamage(int damage) { + this.setDataProperty(new IntEntityData(DATA_HEALTH, damage)); } - public void setDamageTaken(float damage) { - this.setDataProperty(new FloatEntityData(EntityVehicle.DATA_DAMAGE_TAKEN, damage)); + @Override + public String getInteractButtonText() { + return "Mount"; + } + + @Override + public boolean canDoInteraction() { + return linkedEntity == null; + } + + /** + * Mount or Dismounts an Entity from a vehicle + * + * @param entity The target Entity + * @return {@code true} if the mounting successful + */ + @Override + public boolean mountEntity(Entity entity) { + Objects.requireNonNull(entity, "The target of the mounting entity can't be null"); + this.PitchDelta = 0.0D; + this.YawDelta = 0.0D; + if (entity.riding != null) { + EntityVehicleExitEvent ev = new EntityVehicleExitEvent(entity, this); + server.getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + return false; + } + SetEntityLinkPacket pk; + + pk = new SetEntityLinkPacket(); + pk.rider = getId(); //Weird Weird Weird + pk.riding = entity.getId(); + pk.type = 3; + Server.broadcastPacket(this.hasSpawned.values(), pk); + + if (entity instanceof Player) { + pk = new SetEntityLinkPacket(); + pk.rider = getId(); + pk.riding = entity.getId(); + pk.type = 3; + ((Player) entity).dataPacket(pk); + } + + entity.riding = null; + linkedEntity = null; + entity.setDataFlag(DATA_FLAGS, DATA_FLAG_RIDING, false); + return true; + } + EntityVehicleEnterEvent ev = new EntityVehicleEnterEvent(entity, this); + server.getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + return false; + } + + SetEntityLinkPacket pk; + + pk = new SetEntityLinkPacket(); + pk.rider = this.getId(); + pk.riding = entity.getId(); + pk.type = 2; + Server.broadcastPacket(this.hasSpawned.values(), pk); + + if (entity instanceof Player) { + pk = new SetEntityLinkPacket(); + pk.rider = this.getId(); + pk.riding = 0; + pk.type = 2; + ((Player) entity).dataPacket(pk); + } + + entity.riding = this; + linkedEntity = entity; + + entity.setDataFlag(DATA_FLAGS, DATA_FLAG_RIDING, true); + updateRiderPosition(getMountedYOffset()); + return true; + } + + @Override + public boolean onUpdate(int currentTick) { + // The rolling amplitude + if (getRollingAmplitude() > 0) { + setRollingAmplitude(getRollingAmplitude() - 1); + } + // The damage token + if (getDamage() > 0) { + setDamage(getDamage() - 1); + } + // A killer task + if (y < -16) { + kill(); + } + // Movement code + updateMovement(); + return true; + } + + protected boolean rollingDirection = true; + + protected boolean performHurtAnimation(int damage) { + if (damage >= this.getHealth()) { + return false; + } + + // Vehicle does not respond hurt animation on packets + // It only respond on vehicle data flags. Such as these + setRollingAmplitude(10); + setRollingDirection(rollingDirection ? 1 : -1); + rollingDirection = !rollingDirection; + setDamage(getDamage() + damage); + return true; } } diff --git a/src/main/java/cn/nukkit/entity/item/EntityXPOrb.java b/src/main/java/cn/nukkit/entity/item/EntityXPOrb.java index 708ec9b3f7..8779f4ffb3 100644 --- a/src/main/java/cn/nukkit/entity/item/EntityXPOrb.java +++ b/src/main/java/cn/nukkit/entity/item/EntityXPOrb.java @@ -56,9 +56,9 @@ public EntityXPOrb(FullChunk chunk, CompoundTag nbt) { super(chunk, nbt); } - private int age = 0; - private int pickupDelay = 0; - private int exp = 0; + private int age; + private int pickupDelay; + private int exp; public Player closestPlayer = null; @@ -108,6 +108,14 @@ public boolean onUpdate(int currentTick) { if (this.pickupDelay < 0) { this.pickupDelay = 0; } + } else { + for (Entity entity : this.level.getCollidingEntities(this.boundingBox, this)) { + if (entity instanceof Player) { + if (((Player) entity).pickupEntity(this, false)) { + return true; + } + } + } } this.motionY -= this.getGravity(); diff --git a/src/main/java/cn/nukkit/entity/mob/EntityMob.java b/src/main/java/cn/nukkit/entity/mob/EntityMob.java index 6568c4e6b3..b682f0ca24 100644 --- a/src/main/java/cn/nukkit/entity/mob/EntityMob.java +++ b/src/main/java/cn/nukkit/entity/mob/EntityMob.java @@ -5,7 +5,7 @@ import cn.nukkit.nbt.tag.CompoundTag; /** - * author: MagicDroidX + * author: MagicDroidX * Nukkit Project */ public abstract class EntityMob extends EntityCreature { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityBat.java b/src/main/java/cn/nukkit/entity/passive/EntityBat.java index d0a10d57b8..7cbc0c32ce 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityBat.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityBat.java @@ -7,7 +7,6 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntityBat extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityChicken.java b/src/main/java/cn/nukkit/entity/passive/EntityChicken.java index e7af98590e..514dca8a32 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityChicken.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityChicken.java @@ -7,7 +7,7 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * Author: BeYkeRYkt + * Author: BeYkeRYkt * Nukkit Project */ public class EntityChicken extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityCow.java b/src/main/java/cn/nukkit/entity/passive/EntityCow.java index 5e138ff183..4d29b74bb2 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityCow.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityCow.java @@ -7,7 +7,7 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * Author: BeYkeRYkt + * Author: BeYkeRYkt * Nukkit Project */ public class EntityCow extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityDonkey.java b/src/main/java/cn/nukkit/entity/passive/EntityDonkey.java index 3b66bf946c..193e654f47 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityDonkey.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityDonkey.java @@ -7,7 +7,6 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntityDonkey extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityHorse.java b/src/main/java/cn/nukkit/entity/passive/EntityHorse.java index 346c24656f..6fd4495184 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityHorse.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityHorse.java @@ -7,7 +7,6 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntityHorse extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityLlama.java b/src/main/java/cn/nukkit/entity/passive/EntityLlama.java index 1f6948971c..98d0bf44c9 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityLlama.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityLlama.java @@ -6,7 +6,6 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntityLlama extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityMooshroom.java b/src/main/java/cn/nukkit/entity/passive/EntityMooshroom.java index 22217389a2..e5f03265b1 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityMooshroom.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityMooshroom.java @@ -59,7 +59,7 @@ protected void initEntity() { super.initEntity(); setMaxHealth(10); } - + @Override public void spawnTo(Player player) { AddEntityPacket pk = new AddEntityPacket(); diff --git a/src/main/java/cn/nukkit/entity/passive/EntityMule.java b/src/main/java/cn/nukkit/entity/passive/EntityMule.java index 1c3c2aa505..93f21efb9e 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityMule.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityMule.java @@ -7,7 +7,6 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntityMule extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityOcelot.java b/src/main/java/cn/nukkit/entity/passive/EntityOcelot.java index 5c2408a124..1f78ec76c6 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityOcelot.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityOcelot.java @@ -7,7 +7,7 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * Author: BeYkeRYkt + * Author: BeYkeRYkt * Nukkit Project */ public class EntityOcelot extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityPig.java b/src/main/java/cn/nukkit/entity/passive/EntityPig.java index d59d29143c..acaaefaaf1 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityPig.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityPig.java @@ -7,7 +7,7 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * Author: BeYkeRYkt + * Author: BeYkeRYkt * Nukkit Project */ public class EntityPig extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityPolarBear.java b/src/main/java/cn/nukkit/entity/passive/EntityPolarBear.java index c990d222d8..946ec48b6c 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityPolarBear.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityPolarBear.java @@ -7,7 +7,6 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntityPolarBear extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityRabbit.java b/src/main/java/cn/nukkit/entity/passive/EntityRabbit.java index 3fbe53dfb9..1efbe43b32 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityRabbit.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityRabbit.java @@ -7,7 +7,7 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * Author: BeYkeRYkt + * Author: BeYkeRYkt * Nukkit Project */ public class EntityRabbit extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntitySheep.java b/src/main/java/cn/nukkit/entity/passive/EntitySheep.java index 5c651e207a..7ed8d5dac4 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntitySheep.java +++ b/src/main/java/cn/nukkit/entity/passive/EntitySheep.java @@ -13,7 +13,7 @@ import java.util.concurrent.ThreadLocalRandom; /** - * Author: BeYkeRYkt + * Author: BeYkeRYkt * Nukkit Project */ public class EntitySheep extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntitySkeletonHorse.java b/src/main/java/cn/nukkit/entity/passive/EntitySkeletonHorse.java index e419e6be39..2547ce1738 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntitySkeletonHorse.java +++ b/src/main/java/cn/nukkit/entity/passive/EntitySkeletonHorse.java @@ -7,7 +7,6 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntitySkeletonHorse extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntitySquid.java b/src/main/java/cn/nukkit/entity/passive/EntitySquid.java index f56c1ad255..2190be7fc4 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntitySquid.java +++ b/src/main/java/cn/nukkit/entity/passive/EntitySquid.java @@ -9,7 +9,6 @@ import cn.nukkit.utils.DyeColor; /** - * * @author PikyCZ */ public class EntitySquid extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityWolf.java b/src/main/java/cn/nukkit/entity/passive/EntityWolf.java index fd447a3b98..8099bb3128 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityWolf.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityWolf.java @@ -7,7 +7,7 @@ import cn.nukkit.network.protocol.AddEntityPacket; /** - * Author: BeYkeRYkt + * Author: BeYkeRYkt * Nukkit Project */ public class EntityWolf extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/passive/EntityZombieHorse.java b/src/main/java/cn/nukkit/entity/passive/EntityZombieHorse.java index 62929de37f..02450d44fc 100644 --- a/src/main/java/cn/nukkit/entity/passive/EntityZombieHorse.java +++ b/src/main/java/cn/nukkit/entity/passive/EntityZombieHorse.java @@ -1,14 +1,12 @@ package cn.nukkit.entity.passive; import cn.nukkit.Player; -import cn.nukkit.event.entity.EntityDamageByEntityEvent; import cn.nukkit.item.Item; import cn.nukkit.level.format.FullChunk; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.network.protocol.AddEntityPacket; /** - * * @author PikyCZ */ public class EntityZombieHorse extends EntityAnimal { diff --git a/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java b/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java index 82a1fec977..bbf4c35898 100644 --- a/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java +++ b/src/main/java/cn/nukkit/entity/projectile/EntityArrow.java @@ -91,6 +91,11 @@ public int getResultDamage() { return base; } + @Override + protected double getBaseDamage() { + return 2; + } + @Override public boolean onUpdate(int currentTick) { if (this.closed) { diff --git a/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java b/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java index 776a83fc68..caf8a345cf 100644 --- a/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java +++ b/src/main/java/cn/nukkit/entity/projectile/EntityProjectile.java @@ -23,7 +23,11 @@ public abstract class EntityProjectile extends Entity { public Entity shootingEntity = null; protected double getDamage() { - return namedTag.contains("damage") ? namedTag.getDouble("damage") : 2; + return namedTag.contains("damage") ? namedTag.getDouble("damage") : getBaseDamage(); + } + + protected double getBaseDamage() { + return 0; } public boolean hadCollision = false; diff --git a/src/main/java/cn/nukkit/entity/weather/EntityLightning.java b/src/main/java/cn/nukkit/entity/weather/EntityLightning.java index 1883f807f3..30fba34e45 100644 --- a/src/main/java/cn/nukkit/entity/weather/EntityLightning.java +++ b/src/main/java/cn/nukkit/entity/weather/EntityLightning.java @@ -119,8 +119,8 @@ public boolean onUpdate(int currentTick) { this.entityBaseTick(tickDiff); if (this.state == 2) { - this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_THUNDER, 93, -1, this, false, false); - this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_EXPLODE, 93, -1, this, false, false); + this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_THUNDER, 93, -1, this); + this.level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_EXPLODE, 93, -1, this); } this.state--; diff --git a/src/main/java/cn/nukkit/event/block/BlockPistonChangeEvent.java b/src/main/java/cn/nukkit/event/block/BlockPistonChangeEvent.java new file mode 100644 index 0000000000..ab5d0184f0 --- /dev/null +++ b/src/main/java/cn/nukkit/event/block/BlockPistonChangeEvent.java @@ -0,0 +1,33 @@ +package cn.nukkit.event.block; + +import cn.nukkit.block.Block; +import cn.nukkit.event.HandlerList; + +/** + * Created by CreeperFace on 2.8.2017. + */ +public class BlockPistonChangeEvent extends BlockEvent { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private int oldPower; + private int newPower; + + public BlockPistonChangeEvent(Block block, int oldPower, int newPower) { + super(block); + this.oldPower = oldPower; + this.newPower = newPower; + } + + public int getOldPower() { + return oldPower; + } + + public int getNewPower() { + return newPower; + } +} diff --git a/src/main/java/cn/nukkit/event/entity/EntityDamageEvent.java b/src/main/java/cn/nukkit/event/entity/EntityDamageEvent.java index c2a00e67a3..3652869819 100644 --- a/src/main/java/cn/nukkit/event/entity/EntityDamageEvent.java +++ b/src/main/java/cn/nukkit/event/entity/EntityDamageEvent.java @@ -120,7 +120,11 @@ public enum DamageModifier { /** * Damage reduction caused by the Resistance potion effect */ - RESISTANCE + RESISTANCE, + /** + * Damage reduction caused by the Damage absorption effect + */ + ABSORPTION //ARMOR_ENCHANTMENTS } diff --git a/src/main/java/cn/nukkit/event/entity/EntityInteractEvent.java b/src/main/java/cn/nukkit/event/entity/EntityInteractEvent.java new file mode 100644 index 0000000000..5d198f2e9c --- /dev/null +++ b/src/main/java/cn/nukkit/event/entity/EntityInteractEvent.java @@ -0,0 +1,29 @@ +package cn.nukkit.event.entity; + +import cn.nukkit.block.Block; +import cn.nukkit.entity.Entity; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +/** + * @author CreeperFace + */ +public class EntityInteractEvent extends EntityEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private Block block; + + public EntityInteractEvent(Entity entity, Block block) { + this.entity = entity; + this.block = block; + } + + public Block getBlock() { + return block; + } +} diff --git a/src/main/java/cn/nukkit/event/entity/EntityVehicleEnterEvent.java b/src/main/java/cn/nukkit/event/entity/EntityVehicleEnterEvent.java new file mode 100644 index 0000000000..0a3b505cc4 --- /dev/null +++ b/src/main/java/cn/nukkit/event/entity/EntityVehicleEnterEvent.java @@ -0,0 +1,27 @@ +package cn.nukkit.event.entity; + +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class EntityVehicleEnterEvent extends EntityEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final EntityVehicle vehicle; + + public EntityVehicleEnterEvent(Entity entity, EntityVehicle vehicle) { + this.entity = entity; + this.vehicle = vehicle; + } + + public EntityVehicle getVehicle() { + return vehicle; + } + +} diff --git a/src/main/java/cn/nukkit/event/entity/EntityVehicleExitEvent.java b/src/main/java/cn/nukkit/event/entity/EntityVehicleExitEvent.java new file mode 100644 index 0000000000..61294b9ea7 --- /dev/null +++ b/src/main/java/cn/nukkit/event/entity/EntityVehicleExitEvent.java @@ -0,0 +1,27 @@ +package cn.nukkit.event.entity; + +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class EntityVehicleExitEvent extends EntityEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final EntityVehicle vehicle; + + public EntityVehicleExitEvent(Entity entity, EntityVehicle vehicle) { + this.entity = entity; + this.vehicle = vehicle; + } + + public EntityVehicle getVehicle() { + return vehicle; + } + +} diff --git a/src/main/java/cn/nukkit/event/inventory/BrewEvent.java b/src/main/java/cn/nukkit/event/inventory/BrewEvent.java new file mode 100644 index 0000000000..e00a225aaa --- /dev/null +++ b/src/main/java/cn/nukkit/event/inventory/BrewEvent.java @@ -0,0 +1,59 @@ +package cn.nukkit.event.inventory; + +import cn.nukkit.blockentity.BlockEntityBrewingStand; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.item.Item; + +/** + * @author CreeperFace + */ +public class BrewEvent extends InventoryEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final BlockEntityBrewingStand brewingStand; + private final Item ingredient; + private final Item[] potions; + private final int fuel; + + public BrewEvent(BlockEntityBrewingStand blockEntity) { + super(blockEntity.getInventory()); + this.brewingStand = blockEntity; + this.fuel = blockEntity.fuelAmount; + + this.ingredient = blockEntity.getInventory().getIngredient(); + + this.potions = new Item[3]; + for (int i = 0; i < 3; i++) { + this.potions[i] = blockEntity.getInventory().getItem(i); + } + } + + public BlockEntityBrewingStand getBrewingStand() { + return brewingStand; + } + + public Item getIngredient() { + return ingredient; + } + + public Item[] getPotions() { + return potions; + } + + /** + * @param index Potion index in range 0 - 2 + */ + public Item getPotion(int index) { + return this.potions[index]; + } + + public int getFuel() { + return fuel; + } +} diff --git a/src/main/java/cn/nukkit/event/inventory/InventoryTransactionEvent.java b/src/main/java/cn/nukkit/event/inventory/InventoryTransactionEvent.java index c7d0328afd..c416b726e6 100644 --- a/src/main/java/cn/nukkit/event/inventory/InventoryTransactionEvent.java +++ b/src/main/java/cn/nukkit/event/inventory/InventoryTransactionEvent.java @@ -3,7 +3,7 @@ import cn.nukkit.event.Cancellable; import cn.nukkit.event.Event; import cn.nukkit.event.HandlerList; -import cn.nukkit.inventory.TransactionGroup; +import cn.nukkit.inventory.transaction.InventoryTransaction; /** * author: MagicDroidX @@ -17,14 +17,13 @@ public static HandlerList getHandlers() { return handlers; } - private final TransactionGroup transaction; + private final InventoryTransaction transaction; - public InventoryTransactionEvent(TransactionGroup transaction) { + public InventoryTransactionEvent(InventoryTransaction transaction) { this.transaction = transaction; } - public TransactionGroup getTransaction() { + public InventoryTransaction getTransaction() { return transaction; } - } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/event/inventory/StartBrewEvent.java b/src/main/java/cn/nukkit/event/inventory/StartBrewEvent.java new file mode 100644 index 0000000000..5ad96b160e --- /dev/null +++ b/src/main/java/cn/nukkit/event/inventory/StartBrewEvent.java @@ -0,0 +1,53 @@ +package cn.nukkit.event.inventory; + +import cn.nukkit.blockentity.BlockEntityBrewingStand; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.item.Item; + +/** + * @author CreeperFace + */ +public class StartBrewEvent extends InventoryEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final BlockEntityBrewingStand brewingStand; + private final Item ingredient; + private final Item[] potions; + + public StartBrewEvent(BlockEntityBrewingStand blockEntity) { + super(blockEntity.getInventory()); + this.brewingStand = blockEntity; + + this.ingredient = blockEntity.getInventory().getIngredient(); + + this.potions = new Item[3]; + for (int i = 0; i < 3; i++) { + this.potions[i] = blockEntity.getInventory().getItem(i); + } + } + + public BlockEntityBrewingStand getBrewingStand() { + return brewingStand; + } + + public Item getIngredient() { + return ingredient; + } + + public Item[] getPotions() { + return potions; + } + + /** + * @param index Potion index in range 0 - 2 + */ + public Item getPotion(int index) { + return this.potions[index]; + } +} diff --git a/src/main/java/cn/nukkit/event/player/PlayerBlockPickEvent.java b/src/main/java/cn/nukkit/event/player/PlayerBlockPickEvent.java new file mode 100644 index 0000000000..bbca5b106e --- /dev/null +++ b/src/main/java/cn/nukkit/event/player/PlayerBlockPickEvent.java @@ -0,0 +1,40 @@ +package cn.nukkit.event.player; + +import cn.nukkit.Player; +import cn.nukkit.block.Block; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.item.Item; + +/** + * @author CreeperFace + */ +public class PlayerBlockPickEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final Block blockClicked; + private Item item; + + public PlayerBlockPickEvent(Player player, Block blockClicked, Item item) { + this.blockClicked = blockClicked; + this.item = item; + this.player = player; + } + + public Item getItem() { + return item; + } + + public void setItem(Item item) { + this.item = item; + } + + public Block getBlockClicked() { + return blockClicked; + } +} diff --git a/src/main/java/cn/nukkit/event/player/PlayerFormRespondedEvent.java b/src/main/java/cn/nukkit/event/player/PlayerFormRespondedEvent.java new file mode 100644 index 0000000000..d3eee72225 --- /dev/null +++ b/src/main/java/cn/nukkit/event/player/PlayerFormRespondedEvent.java @@ -0,0 +1,49 @@ +package cn.nukkit.event.player; + +import cn.nukkit.Player; +import cn.nukkit.event.HandlerList; +import cn.nukkit.form.response.FormResponse; +import cn.nukkit.form.window.FormWindow; + +public class PlayerFormRespondedEvent extends PlayerEvent { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + protected int formID; + protected FormWindow window; + + protected boolean closed = false; + + public PlayerFormRespondedEvent(Player player, int formID, FormWindow window) { + this.player = player; + this.formID = formID; + this.window = window; + } + + public int getFormID() { + return this.formID; + } + + public FormWindow getWindow() { + return window; + } + + /** + * Can be null if player closed the window instead of submitting it + */ + public FormResponse getResponse() { + return window.getResponse(); + } + + /** + * Defines if player closed the window or submitted it + */ + public boolean wasClosed() { + return window.wasClosed(); + } + +} diff --git a/src/main/java/cn/nukkit/event/player/PlayerItemHeldEvent.java b/src/main/java/cn/nukkit/event/player/PlayerItemHeldEvent.java index b64cc57286..c38a17e63b 100644 --- a/src/main/java/cn/nukkit/event/player/PlayerItemHeldEvent.java +++ b/src/main/java/cn/nukkit/event/player/PlayerItemHeldEvent.java @@ -18,22 +18,21 @@ public static HandlerList getHandlers() { } private final Item item; - private final int slot; - private final int inventorySlot; + private final int hotbarSlot; - public PlayerItemHeldEvent(Player player, Item item, int inventorySlot, int slot) { + public PlayerItemHeldEvent(Player player, Item item, int hotbarSlot) { this.player = player; this.item = item; - this.inventorySlot = inventorySlot; - this.slot = slot; + this.hotbarSlot = hotbarSlot; } public int getSlot() { - return slot; + return this.hotbarSlot; } + @Deprecated public int getInventorySlot() { - return inventorySlot; + return hotbarSlot; } public Item getItem() { diff --git a/src/main/java/cn/nukkit/event/player/PlayerMoveEvent.java b/src/main/java/cn/nukkit/event/player/PlayerMoveEvent.java index 60edf4ff41..9dbd77466e 100644 --- a/src/main/java/cn/nukkit/event/player/PlayerMoveEvent.java +++ b/src/main/java/cn/nukkit/event/player/PlayerMoveEvent.java @@ -15,10 +15,17 @@ public static HandlerList getHandlers() { private Location from; private Location to; + private boolean resetBlocksAround; + public PlayerMoveEvent(Player player, Location from, Location to) { + this(player, from, to, true); + } + + public PlayerMoveEvent(Player player, Location from, Location to, boolean resetBlocks) { this.player = player; this.from = from; this.to = to; + this.resetBlocksAround = resetBlocks; } public Location getFrom() { @@ -36,4 +43,17 @@ public Location getTo() { public void setTo(Location to) { this.to = to; } + + public boolean isResetBlocksAround() { + return resetBlocksAround; + } + + public void setResetBlocksAround(boolean value) { + this.resetBlocksAround = value; + } + + @Override + public void setCancelled() { + super.setCancelled(); + } } diff --git a/src/main/java/cn/nukkit/event/player/PlayerServerSettingsRequestEvent.java b/src/main/java/cn/nukkit/event/player/PlayerServerSettingsRequestEvent.java new file mode 100644 index 0000000000..400dd347ed --- /dev/null +++ b/src/main/java/cn/nukkit/event/player/PlayerServerSettingsRequestEvent.java @@ -0,0 +1,39 @@ +package cn.nukkit.event.player; + +import cn.nukkit.Player; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.form.window.FormWindow; + +import java.util.Map; + +/** + * @author CreeperFace + */ +public class PlayerServerSettingsRequestEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private Map settings; + + public PlayerServerSettingsRequestEvent(Player player, Map settings) { + this.player = player; + this.settings = settings; + } + + public Map getSettings() { + return settings; + } + + public void setSettings(Map settings) { + this.settings = settings; + } + + public void setSettings(int id, FormWindow window) { + this.settings.put(id, window); + } +} diff --git a/src/main/java/cn/nukkit/event/player/PlayerSettingsRespondedEvent.java b/src/main/java/cn/nukkit/event/player/PlayerSettingsRespondedEvent.java new file mode 100644 index 0000000000..5743436543 --- /dev/null +++ b/src/main/java/cn/nukkit/event/player/PlayerSettingsRespondedEvent.java @@ -0,0 +1,55 @@ +package cn.nukkit.event.player; + +import cn.nukkit.Player; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; +import cn.nukkit.form.response.FormResponse; +import cn.nukkit.form.window.FormWindow; + +public class PlayerSettingsRespondedEvent extends PlayerEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + protected int formID; + protected FormWindow window; + + protected boolean closed = false; + + public PlayerSettingsRespondedEvent(Player player, int formID, FormWindow window) { + this.player = player; + this.formID = formID; + this.window = window; + } + + public int getFormID() { + return this.formID; + } + + public FormWindow getWindow() { + return window; + } + + /** + * Can be null if player closed the window instead of submitting it + */ + public FormResponse getResponse() { + return window.getResponse(); + } + + /** + * Defines if player closed the window or submitted it + */ + public boolean wasClosed() { + return window.wasClosed(); + } + + @Override + public void setCancelled() { + super.setCancelled(); + } + +} diff --git a/src/main/java/cn/nukkit/event/potion/PotionApplyEvent.java b/src/main/java/cn/nukkit/event/potion/PotionApplyEvent.java index a35ff85188..f1f4b0b008 100644 --- a/src/main/java/cn/nukkit/event/potion/PotionApplyEvent.java +++ b/src/main/java/cn/nukkit/event/potion/PotionApplyEvent.java @@ -3,6 +3,7 @@ import cn.nukkit.entity.Entity; import cn.nukkit.event.Cancellable; import cn.nukkit.event.HandlerList; +import cn.nukkit.potion.Effect; import cn.nukkit.potion.Potion; /** @@ -17,10 +18,13 @@ public static HandlerList getHandlers() { return handlers; } + private Effect applyEffect; + private final Entity entity; - public PotionApplyEvent(Potion potion, Entity entity) { + public PotionApplyEvent(Potion potion, Effect applyEffect, Entity entity) { super(potion); + this.applyEffect = applyEffect; this.entity = entity; } @@ -28,4 +32,11 @@ public Entity getEntity() { return entity; } + public Effect getApplyEffect() { + return applyEffect; + } + + public void setApplyEffect(Effect applyEffect) { + this.applyEffect = applyEffect; + } } diff --git a/src/main/java/cn/nukkit/event/vehicle/EntityEnterVehicleEvent.java b/src/main/java/cn/nukkit/event/vehicle/EntityEnterVehicleEvent.java new file mode 100644 index 0000000000..a654ec75dc --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/EntityEnterVehicleEvent.java @@ -0,0 +1,32 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.Player; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class EntityEnterVehicleEvent extends VehicleEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final Entity riding; + + public EntityEnterVehicleEvent(Entity riding, EntityVehicle vehicle) { + super(vehicle); + this.riding = riding; + } + + public Entity getEntity() { + return riding; + } + + public boolean isPlayer() { + return riding instanceof Player; + } + +} diff --git a/src/main/java/cn/nukkit/event/vehicle/EntityExitVehicleEvent.java b/src/main/java/cn/nukkit/event/vehicle/EntityExitVehicleEvent.java new file mode 100644 index 0000000000..39694dd25f --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/EntityExitVehicleEvent.java @@ -0,0 +1,32 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.Player; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class EntityExitVehicleEvent extends VehicleEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final Entity riding; + + public EntityExitVehicleEvent(Entity riding, EntityVehicle vehicle) { + super(vehicle); + this.riding = riding; + } + + public Entity getEntity() { + return riding; + } + + public boolean isPlayer() { + return riding instanceof Player; + } + +} diff --git a/src/main/java/cn/nukkit/event/vehicle/VehicleCreateEvent.java b/src/main/java/cn/nukkit/event/vehicle/VehicleCreateEvent.java new file mode 100644 index 0000000000..24faa86f03 --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/VehicleCreateEvent.java @@ -0,0 +1,19 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class VehicleCreateEvent extends VehicleEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + public VehicleCreateEvent(EntityVehicle vehicle) { + super(vehicle); + } + +} diff --git a/src/main/java/cn/nukkit/event/vehicle/VehicleDamageEvent.java b/src/main/java/cn/nukkit/event/vehicle/VehicleDamageEvent.java new file mode 100644 index 0000000000..0a673778c6 --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/VehicleDamageEvent.java @@ -0,0 +1,37 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class VehicleDamageEvent extends VehicleEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final Entity attacker; + private double damage; + + public VehicleDamageEvent(EntityVehicle vehicle, Entity attacker, double damage) { + super(vehicle); + this.attacker = attacker; + this.damage = damage; + } + + public Entity getAttacker() { + return attacker; + } + + public double getDamage() { + return damage; + } + + public void setDamage(double damage) { + this.damage = damage; + } + +} diff --git a/src/main/java/cn/nukkit/event/vehicle/VehicleDestroyEvent.java b/src/main/java/cn/nukkit/event/vehicle/VehicleDestroyEvent.java new file mode 100644 index 0000000000..5956c59c19 --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/VehicleDestroyEvent.java @@ -0,0 +1,27 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Cancellable; +import cn.nukkit.event.HandlerList; + +public class VehicleDestroyEvent extends VehicleEvent implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final Entity attacker; + + public VehicleDestroyEvent(EntityVehicle vehicle, Entity attacker) { + super(vehicle); + this.attacker = attacker; + } + + public Entity getAttacker() { + return attacker; + } + +} diff --git a/src/main/java/cn/nukkit/event/vehicle/VehicleEvent.java b/src/main/java/cn/nukkit/event/vehicle/VehicleEvent.java new file mode 100644 index 0000000000..ba5346212e --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/VehicleEvent.java @@ -0,0 +1,22 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.Event; + +/** + * Created by larryTheCoder at 7/5/2017. + *

+ * Nukkit Project + */ +public abstract class VehicleEvent extends Event { + + private final EntityVehicle vehicle; + + public VehicleEvent(EntityVehicle vehicle) { + this.vehicle = vehicle; + } + + public EntityVehicle getVehicle() { + return vehicle; + } +} diff --git a/src/main/java/cn/nukkit/event/vehicle/VehicleMoveEvent.java b/src/main/java/cn/nukkit/event/vehicle/VehicleMoveEvent.java new file mode 100644 index 0000000000..0cf898755e --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/VehicleMoveEvent.java @@ -0,0 +1,31 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.HandlerList; +import cn.nukkit.level.Location; + +public class VehicleMoveEvent extends VehicleEvent { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + private final Location from; + private final Location to; + + public VehicleMoveEvent(EntityVehicle vehicle, Location from, Location to) { + super(vehicle); + this.from = from; + this.to = to; + } + + public Location getFrom() { + return from; + } + + public Location getTo() { + return to; + } +} diff --git a/src/main/java/cn/nukkit/event/vehicle/VehicleUpdateEvent.java b/src/main/java/cn/nukkit/event/vehicle/VehicleUpdateEvent.java new file mode 100644 index 0000000000..175dbed211 --- /dev/null +++ b/src/main/java/cn/nukkit/event/vehicle/VehicleUpdateEvent.java @@ -0,0 +1,18 @@ +package cn.nukkit.event.vehicle; + +import cn.nukkit.entity.item.EntityVehicle; +import cn.nukkit.event.HandlerList; + +public class VehicleUpdateEvent extends VehicleEvent { + + private static final HandlerList handlers = new HandlerList(); + + public static HandlerList getHandlers() { + return handlers; + } + + public VehicleUpdateEvent(EntityVehicle vehicle) { + super(vehicle); + } + +} diff --git a/src/main/java/cn/nukkit/form/element/Element.java b/src/main/java/cn/nukkit/form/element/Element.java new file mode 100644 index 0000000000..fc80c0b7db --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/Element.java @@ -0,0 +1,5 @@ +package cn.nukkit.form.element; + +public abstract class Element { + +} diff --git a/src/main/java/cn/nukkit/form/element/ElementButton.java b/src/main/java/cn/nukkit/form/element/ElementButton.java new file mode 100644 index 0000000000..a95846582f --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementButton.java @@ -0,0 +1,33 @@ +package cn.nukkit.form.element; + +public class ElementButton { + + private String text = ""; + private ElementButtonImageData image; + + public ElementButton(String text) { + this.text = text; + } + + public ElementButton(String text, ElementButtonImageData image) { + this.text = text; + if (!image.getData().isEmpty() && !image.getType().isEmpty()) this.image = image; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public ElementButtonImageData getImage() { + return image; + } + + public void addImage(ElementButtonImageData image) { + if (!image.getData().isEmpty() && !image.getType().isEmpty()) this.image = image; + } + +} diff --git a/src/main/java/cn/nukkit/form/element/ElementButtonImageData.java b/src/main/java/cn/nukkit/form/element/ElementButtonImageData.java new file mode 100644 index 0000000000..e3d0fa4e42 --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementButtonImageData.java @@ -0,0 +1,32 @@ +package cn.nukkit.form.element; + +public class ElementButtonImageData { + + public static final String IMAGE_DATA_TYPE_PATH = "path"; + public static final String IMAGE_DATA_TYPE_URL = "url"; + + private String type; + private String data; + + public ElementButtonImageData(String type, String data) { + if (!type.equals(IMAGE_DATA_TYPE_URL) && !type.equals(IMAGE_DATA_TYPE_PATH)) return; + this.type = type; + this.data = data; + } + + public String getType() { + return type; + } + + public String getData() { + return data; + } + + public void setType(String type) { + this.type = type; + } + + public void setData(String data) { + this.data = data; + } +} diff --git a/src/main/java/cn/nukkit/form/element/ElementDropdown.java b/src/main/java/cn/nukkit/form/element/ElementDropdown.java new file mode 100644 index 0000000000..b10699759b --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementDropdown.java @@ -0,0 +1,57 @@ +package cn.nukkit.form.element; + +import java.util.ArrayList; +import java.util.List; + +public class ElementDropdown extends Element { + + private final String type = "dropdown"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String text = ""; + private List options; + private int defaultOptionIndex = 0; + + public ElementDropdown(String text) { + this(text, new ArrayList<>()); + } + + public ElementDropdown(String text, List options) { + this(text, options, 0); + } + + public ElementDropdown(String text, List options, int defaultOption) { + this.text = text; + this.options = options; + this.defaultOptionIndex = defaultOption; + } + + public int getDefaultOptionIndex() { + return defaultOptionIndex; + } + + public void setDefaultOptionIndex(int index) { + if (index >= options.size()) return; + this.defaultOptionIndex = index; + } + + public List getOptions() { + return options; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public void addOption(String option) { + addOption(option, false); + } + + public void addOption(String option, boolean isDefault) { + options.add(option); + if (isDefault) this.defaultOptionIndex = options.size() - 1; + } + +} diff --git a/src/main/java/cn/nukkit/form/element/ElementInput.java b/src/main/java/cn/nukkit/form/element/ElementInput.java new file mode 100644 index 0000000000..e9af69b817 --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementInput.java @@ -0,0 +1,47 @@ +package cn.nukkit.form.element; + +public class ElementInput extends Element { + + private final String type = "input"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String text = ""; + private String placeholder = ""; + private String defaultText = ""; + + public ElementInput(String text) { + this(text, ""); + } + + public ElementInput(String text, String placeholder) { + this(text, placeholder, ""); + } + + public ElementInput(String text, String placeholder, String defaultText) { + this.text = text; + this.placeholder = placeholder; + this.defaultText = defaultText; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getPlaceHolder() { + return placeholder; + } + + public void setPlaceHolder(String placeholder) { + this.placeholder = placeholder; + } + + public String getDefaultText() { + return defaultText; + } + + public void setDefaultText(String defaultText) { + this.defaultText = defaultText; + } +} diff --git a/src/main/java/cn/nukkit/form/element/ElementLabel.java b/src/main/java/cn/nukkit/form/element/ElementLabel.java new file mode 100644 index 0000000000..449eae0561 --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementLabel.java @@ -0,0 +1,19 @@ +package cn.nukkit.form.element; + +public class ElementLabel extends Element { + + private final String type = "label"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String text = ""; + + public ElementLabel(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/src/main/java/cn/nukkit/form/element/ElementSlider.java b/src/main/java/cn/nukkit/form/element/ElementSlider.java new file mode 100644 index 0000000000..8ed077bb75 --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementSlider.java @@ -0,0 +1,67 @@ +package cn.nukkit.form.element; + +public class ElementSlider extends Element { + + private final String type = "slider"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String text = ""; + private float min = 0f; + private float max = 100f; + private int step; + private float defaultValue; + + public ElementSlider(String text, float min, float max) { + this(text, min, max, -1); + } + + public ElementSlider(String text, float min, float max, int step) { + this(text, min, max, step, -1); + } + + public ElementSlider(String text, float min, float max, int step, float defaultValue) { + this.text = text; + this.min = min < 0f ? 0f : min; + this.max = max > this.min ? max : this.min; + if (step != -1f && step > 0) this.step = step; + if (defaultValue != -1f) this.defaultValue = defaultValue; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public float getMin() { + return min; + } + + public void setMin(float min) { + this.min = min; + } + + public float getMax() { + return max; + } + + public void setMax(float max) { + this.max = max; + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public float getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(float defaultValue) { + this.defaultValue = defaultValue; + } +} diff --git a/src/main/java/cn/nukkit/form/element/ElementStepSlider.java b/src/main/java/cn/nukkit/form/element/ElementStepSlider.java new file mode 100644 index 0000000000..443a98596c --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementStepSlider.java @@ -0,0 +1,57 @@ +package cn.nukkit.form.element; + +import java.util.ArrayList; +import java.util.List; + +public class ElementStepSlider extends Element { + + private final String type = "step_slider"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String text = ""; + private List steps; + private int defaultStepIndex = 0; + + public ElementStepSlider(String text) { + this(text, new ArrayList<>()); + } + + public ElementStepSlider(String text, List steps) { + this(text, steps, 0); + } + + public ElementStepSlider(String text, List steps, int defaultStep) { + this.text = text; + this.steps = steps; + this.defaultStepIndex = defaultStep; + } + + public int getDefaultStepIndex() { + return defaultStepIndex; + } + + public void setDefaultOptionIndex(int index) { + if (index >= steps.size()) return; + this.defaultStepIndex = index; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public List getSteps() { + return steps; + } + + public void addStep(String step) { + addStep(step, false); + } + + public void addStep(String step, boolean isDefault) { + steps.add(step); + if (isDefault) this.defaultStepIndex = steps.size() - 1; + } + +} diff --git a/src/main/java/cn/nukkit/form/element/ElementToggle.java b/src/main/java/cn/nukkit/form/element/ElementToggle.java new file mode 100644 index 0000000000..2da9d7fdf1 --- /dev/null +++ b/src/main/java/cn/nukkit/form/element/ElementToggle.java @@ -0,0 +1,33 @@ +package cn.nukkit.form.element; + +public class ElementToggle extends Element { + + private final String type = "toggle"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String text = ""; + private boolean defaultValue = false; + + public ElementToggle(String text) { + this(text, false); + } + + public ElementToggle(String text, boolean defaultValue) { + this.text = text; + this.defaultValue = defaultValue; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public boolean isDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(boolean defaultValue) { + this.defaultValue = defaultValue; + } +} diff --git a/src/main/java/cn/nukkit/form/response/FormResponse.java b/src/main/java/cn/nukkit/form/response/FormResponse.java new file mode 100644 index 0000000000..1d128d60f4 --- /dev/null +++ b/src/main/java/cn/nukkit/form/response/FormResponse.java @@ -0,0 +1,5 @@ +package cn.nukkit.form.response; + +public abstract class FormResponse { + +} diff --git a/src/main/java/cn/nukkit/form/response/FormResponseCustom.java b/src/main/java/cn/nukkit/form/response/FormResponseCustom.java new file mode 100644 index 0000000000..b7d3704c88 --- /dev/null +++ b/src/main/java/cn/nukkit/form/response/FormResponseCustom.java @@ -0,0 +1,54 @@ +package cn.nukkit.form.response; + +import java.util.HashMap; + +public class FormResponseCustom extends FormResponse { + + private HashMap responses = new HashMap<>(); + private HashMap dropdownResponses = new HashMap<>(); + private HashMap inputResponses = new HashMap<>(); + private HashMap sliderResponses = new HashMap<>(); + private HashMap stepSliderResponses = new HashMap<>(); + private HashMap toggleResponses = new HashMap<>(); + + public FormResponseCustom(HashMap responses, HashMap dropdownResponses, + HashMap inputResponses, HashMap sliderResponses, + HashMap stepSliderResponses, + HashMap toggleResponses) { + this.responses = responses; + this.dropdownResponses = dropdownResponses; + this.inputResponses = inputResponses; + this.sliderResponses = sliderResponses; + this.stepSliderResponses = stepSliderResponses; + this.toggleResponses = toggleResponses; + } + + public HashMap getResponses() { + return responses; + } + + public Object getResponse(int id) { + return responses.get(id); + } + + public FormResponseData getDropdownResponse(int id) { + return dropdownResponses.get(id); + } + + public String getInputResponse(int id) { + return inputResponses.get(id); + } + + public float getSliderResponse(int id) { + return sliderResponses.get(id); + } + + public FormResponseData getStepSliderResponse(int id) { + return stepSliderResponses.get(id); + } + + public boolean getToggleResponse(int id) { + return toggleResponses.get(id); + } + +} diff --git a/src/main/java/cn/nukkit/form/response/FormResponseData.java b/src/main/java/cn/nukkit/form/response/FormResponseData.java new file mode 100644 index 0000000000..81a3444d44 --- /dev/null +++ b/src/main/java/cn/nukkit/form/response/FormResponseData.java @@ -0,0 +1,21 @@ +package cn.nukkit.form.response; + +public class FormResponseData { + + private int elementID; + private String elementContent; + + public FormResponseData(int id, String content) { + this.elementID = id; + this.elementContent = content; + } + + public int getElementID() { + return elementID; + } + + public String getElementContent() { + return elementContent; + } + +} diff --git a/src/main/java/cn/nukkit/form/response/FormResponseModal.java b/src/main/java/cn/nukkit/form/response/FormResponseModal.java new file mode 100644 index 0000000000..17b6232722 --- /dev/null +++ b/src/main/java/cn/nukkit/form/response/FormResponseModal.java @@ -0,0 +1,21 @@ +package cn.nukkit.form.response; + +public class FormResponseModal extends FormResponse { + + private int clickedButtonId; + private String clickedButtonText; + + public FormResponseModal(int clickedButtonId, String clickedButtonText) { + this.clickedButtonId = clickedButtonId; + this.clickedButtonText = clickedButtonText; + } + + public int getClickedButtonId() { + return clickedButtonId; + } + + public String getClickedButtonText() { + return clickedButtonText; + } + +} diff --git a/src/main/java/cn/nukkit/form/response/FormResponseSimple.java b/src/main/java/cn/nukkit/form/response/FormResponseSimple.java new file mode 100644 index 0000000000..2590942b1f --- /dev/null +++ b/src/main/java/cn/nukkit/form/response/FormResponseSimple.java @@ -0,0 +1,23 @@ +package cn.nukkit.form.response; + +import cn.nukkit.form.element.ElementButton; + +public class FormResponseSimple extends FormResponse { + + private int clickedButtonId; + private ElementButton clickedButton; + + public FormResponseSimple(int clickedButtonId, ElementButton clickedButton) { + this.clickedButtonId = clickedButtonId; + this.clickedButton = clickedButton; + } + + public int getClickedButtonId() { + return clickedButtonId; + } + + public ElementButton getClickedButton() { + return clickedButton; + } + +} diff --git a/src/main/java/cn/nukkit/form/window/FormWindow.java b/src/main/java/cn/nukkit/form/window/FormWindow.java new file mode 100644 index 0000000000..2d4ca78988 --- /dev/null +++ b/src/main/java/cn/nukkit/form/window/FormWindow.java @@ -0,0 +1,19 @@ +package cn.nukkit.form.window; + +import cn.nukkit.form.response.FormResponse; + +public abstract class FormWindow { + + protected boolean closed = false; + + public abstract String getJSONData(); + + public abstract void setResponse(String data); + + public abstract FormResponse getResponse(); + + public boolean wasClosed() { + return closed; + } + +} diff --git a/src/main/java/cn/nukkit/form/window/FormWindowCustom.java b/src/main/java/cn/nukkit/form/window/FormWindowCustom.java new file mode 100644 index 0000000000..4ce0aaf1fe --- /dev/null +++ b/src/main/java/cn/nukkit/form/window/FormWindowCustom.java @@ -0,0 +1,155 @@ +package cn.nukkit.form.window; + +import cn.nukkit.form.element.*; +import cn.nukkit.form.response.FormResponseCustom; +import cn.nukkit.form.response.FormResponseData; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class FormWindowCustom extends FormWindow { + + private final String type = "custom_form"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String title = ""; + private ElementButtonImageData icon; + private List content; + + private FormResponseCustom response; + + public FormWindowCustom(String title) { + this(title, new ArrayList<>()); + } + + public FormWindowCustom(String title, List contents) { + this(title, contents, ""); + } + + public FormWindowCustom(String title, List contents, String icon) { + this.title = title; + this.content = contents; + if (!icon.isEmpty()) this.icon = new ElementButtonImageData(ElementButtonImageData.IMAGE_DATA_TYPE_URL, icon); + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public List getElements() { + return content; + } + + public void addElement(Element element) { + content.add(element); + } + + public ElementButtonImageData getIcon() { + return icon; + } + + public void setIcon(String icon) { + if (!icon.isEmpty()) this.icon = new ElementButtonImageData(ElementButtonImageData.IMAGE_DATA_TYPE_URL, icon); + } + + public String getJSONData() { + String toModify = new Gson().toJson(this); + //We need to replace this due to Java not supporting declaring class field 'default' + return toModify.replace("defaultOptionIndex", "default") + .replace("defaultText", "default") + .replace("defaultValue", "default") + .replace("defaultStepIndex", "default"); + } + + public FormResponseCustom getResponse() { + return response; + } + + public void setResponse(String data) { + if (data.equals("null")) { + this.closed = true; + return; + } + + List elementResponses = new Gson().fromJson(data, new TypeToken>() { + }.getType()); + //elementResponses.remove(elementResponses.size() - 1); //submit button //maybe mojang removed that? + + int i = 0; + + HashMap dropdownResponses = new HashMap<>(); + HashMap inputResponses = new HashMap<>(); + HashMap sliderResponses = new HashMap<>(); + HashMap stepSliderResponses = new HashMap<>(); + HashMap toggleResponses = new HashMap<>(); + HashMap responses = new HashMap<>(); + + for (String elementData : elementResponses) { + if (i >= content.size()) { + break; + } + + Element e = content.get(i); + if (e == null) break; + if (e instanceof ElementLabel) { + i++; + continue; + } + if (e instanceof ElementDropdown) { + String answer = ((ElementDropdown) e).getOptions().get(Integer.parseInt(elementData)); + dropdownResponses.put(i, new FormResponseData(Integer.parseInt(elementData), answer)); + responses.put(i, answer); + } else if (e instanceof ElementInput) { + inputResponses.put(i, elementData); + responses.put(i, elementData); + } else if (e instanceof ElementSlider) { + Float answer = Float.parseFloat(elementData); + sliderResponses.put(i, answer); + responses.put(i, answer); + } else if (e instanceof ElementStepSlider) { + String answer = ((ElementStepSlider) e).getSteps().get(Integer.parseInt(elementData)); + stepSliderResponses.put(i, new FormResponseData(Integer.parseInt(elementData), answer)); + responses.put(i, answer); + } else if (e instanceof ElementToggle) { + Boolean answer = Boolean.parseBoolean(elementData); + toggleResponses.put(i, answer); + responses.put(i, answer); + } + i++; + } + + this.response = new FormResponseCustom(responses, dropdownResponses, inputResponses, + sliderResponses, stepSliderResponses, toggleResponses); + } + + /** + * Set Elements from Response + * Used on ServerSettings Form Response. After players set settings, we need to sync these settings to the server. + */ + public void setElementsFromResponse() { + if (this.response != null) { + this.response.getResponses().forEach((i, response) -> { + Element e = content.get(i); + if (e != null) { + if (e instanceof ElementDropdown) { + ((ElementDropdown) e).setDefaultOptionIndex(((ElementDropdown) e).getOptions().indexOf(response)); + } else if (e instanceof ElementInput) { + ((ElementInput) e).setDefaultText((String)response); + } else if (e instanceof ElementSlider) { + ((ElementSlider) e).setDefaultValue((Float)response); + } else if (e instanceof ElementStepSlider) { + ((ElementStepSlider) e).setDefaultOptionIndex(((ElementStepSlider) e).getSteps().indexOf(response)); + } else if (e instanceof ElementToggle) { + ((ElementToggle) e).setDefaultValue((Boolean)response); + } + } + }); + } + } + +} diff --git a/src/main/java/cn/nukkit/form/window/FormWindowModal.java b/src/main/java/cn/nukkit/form/window/FormWindowModal.java new file mode 100644 index 0000000000..f0139942b0 --- /dev/null +++ b/src/main/java/cn/nukkit/form/window/FormWindowModal.java @@ -0,0 +1,72 @@ +package cn.nukkit.form.window; + +import cn.nukkit.form.response.FormResponseModal; +import com.google.gson.Gson; + +public class FormWindowModal extends FormWindow { + + private final String type = "modal"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String title = ""; + private String content = ""; + private String button1 = ""; + private String button2 = ""; + + private FormResponseModal response = null; + + public FormWindowModal(String title, String content, String trueButonText, String falseButtonText) { + this.title = title; + this.content = content; + this.button1 = trueButonText; + this.button2 = falseButtonText; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getButton1() { + return button1; + } + + public void setButton1(String button1) { + this.button1 = button1; + } + + public String getButton2() { + return button2; + } + + public void setButton2(String button2) { + this.button2 = button2; + } + + public String getJSONData() { + return new Gson().toJson(this); + } + + public FormResponseModal getResponse() { + return response; + } + + public void setResponse(String data) { + if (data.equals("null")) { + closed = true; + return; + } + if (data.equals("true")) response = new FormResponseModal(0, button1); + else response = new FormResponseModal(1, button2); + } + +} diff --git a/src/main/java/cn/nukkit/form/window/FormWindowSimple.java b/src/main/java/cn/nukkit/form/window/FormWindowSimple.java new file mode 100644 index 0000000000..3df90500c6 --- /dev/null +++ b/src/main/java/cn/nukkit/form/window/FormWindowSimple.java @@ -0,0 +1,79 @@ +package cn.nukkit.form.window; + +import cn.nukkit.form.element.ElementButton; +import cn.nukkit.form.response.FormResponseSimple; +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.List; + +public class FormWindowSimple extends FormWindow { + + private final String type = "form"; //This variable is used for JSON import operations. Do NOT delete :) -- @Snake1999 + private String title = ""; + private String content = ""; + private List buttons; + + private FormResponseSimple response = null; + + public FormWindowSimple(String title, String content) { + this(title, content, new ArrayList<>()); + } + + public FormWindowSimple(String title, String content, List buttons) { + this.title = title; + this.content = content; + this.buttons = buttons; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public List getButtons() { + return buttons; + } + + public void addButton(ElementButton button) { + this.buttons.add(button); + } + + public String getJSONData() { + return new Gson().toJson(this); + } + + public FormResponseSimple getResponse() { + return response; + } + + public void setResponse(String data) { + if (data.equals("null")) { + this.closed = true; + return; + } + int buttonID; + try { + buttonID = Integer.parseInt(data); + } catch (Exception e) { + return; + } + if (buttonID >= this.buttons.size()) { + this.response = new FormResponseSimple(buttonID, null); + return; + } + this.response = new FormResponseSimple(buttonID, buttons.get(buttonID)); + } + +} diff --git a/src/main/java/cn/nukkit/inventory/AnvilInventory.java b/src/main/java/cn/nukkit/inventory/AnvilInventory.java index 05340c1303..b246c49593 100644 --- a/src/main/java/cn/nukkit/inventory/AnvilInventory.java +++ b/src/main/java/cn/nukkit/inventory/AnvilInventory.java @@ -33,12 +33,12 @@ public boolean onRename(Player player, Item resultItem) { Item local = getItem(TARGET); Item second = getItem(SACRIFICE); - if (!resultItem.deepEquals(local, true, false) || resultItem.getCount() != local.getCount()) { + 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.deepEquals(resultItem)) { + if (local.equals(resultItem)) { //just item transaction return true; } @@ -130,6 +130,7 @@ public boolean onRename(Player player, Item resultItem) { public void onClose(Player who) { super.onClose(who); who.craftingType = Player.CRAFTING_SMALL; + who.resetCraftingGridType(); for (int i = 0; i < 2; ++i) { this.getHolder().getLevel().dropItem(this.getHolder().add(0.5, 0.5, 0.5), this.getItem(i)); @@ -142,4 +143,19 @@ public void onOpen(Player who) { super.onOpen(who); who.craftingType = Player.CRAFTING_ANVIL; } + + /*@Override + public boolean setItem(int index, Item item, boolean send) { + return super.setItem(index, item, false); + } + + @Override + public void sendSlot(int index, Player... players) { + + } + + @Override + public void sendContents(Player... player) { + + }*/ } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/inventory/BaseInventory.java b/src/main/java/cn/nukkit/inventory/BaseInventory.java index 0d921a4dc3..dbbf73ac10 100644 --- a/src/main/java/cn/nukkit/inventory/BaseInventory.java +++ b/src/main/java/cn/nukkit/inventory/BaseInventory.java @@ -8,8 +8,8 @@ import cn.nukkit.event.inventory.InventoryOpenEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; -import cn.nukkit.network.protocol.ContainerSetContentPacket; -import cn.nukkit.network.protocol.ContainerSetSlotPacket; +import cn.nukkit.network.protocol.InventoryContentPacket; +import cn.nukkit.network.protocol.InventorySlotPacket; import java.util.*; @@ -139,7 +139,7 @@ public void setContents(Map items) { } @Override - public boolean setItem(int index, Item item) { + public boolean setItem(int index, Item item, boolean send) { item = item.clone(); if (index < 0 || index >= this.size) { return false; @@ -161,7 +161,7 @@ public boolean setItem(int index, Item item) { Item old = this.getItem(index); this.slots.put(index, item.clone()); - this.onSlotChange(index, old); + this.onSlotChange(index, old, send); return true; } @@ -169,7 +169,7 @@ public boolean setItem(int index, Item item) { @Override public boolean contains(Item item) { int count = Math.max(1, item.getCount()); - boolean checkDamage = item.hasMeta(); + boolean checkDamage = item.hasMeta() && item.getDamage() >= 0; boolean checkTag = item.getCompoundTag() != null; for (Item i : this.getContents().values()) { if (item.equals(i, checkDamage, checkTag)) { @@ -186,7 +186,7 @@ public boolean contains(Item item) { @Override public Map all(Item item) { Map slots = new HashMap<>(); - boolean checkDamage = item.hasMeta(); + boolean checkDamage = item.hasMeta() && item.getDamage() >= 0; boolean checkTag = item.getCompoundTag() != null; for (Map.Entry entry : this.getContents().entrySet()) { if (item.equals(entry.getValue(), checkDamage, checkTag)) { @@ -209,12 +209,12 @@ public void remove(Item item) { } @Override - public int first(Item item) { + public int first(Item item, boolean exact) { int count = Math.max(1, item.getCount()); boolean checkDamage = item.hasMeta(); boolean checkTag = item.getCompoundTag() != null; for (Map.Entry entry : this.getContents().entrySet()) { - if (item.equals(entry.getValue(), checkDamage, checkTag) && entry.getValue().getCount() >= count) { + if (item.equals(entry.getValue(), checkDamage, checkTag) && (entry.getValue().getCount() == count || (!exact && entry.getValue().getCount() > count))) { return entry.getKey(); } } @@ -350,7 +350,7 @@ public Item[] removeItem(Item... slots) { } @Override - public boolean clear(int index) { + public boolean clear(int index, boolean send) { if (this.slots.containsKey(index)) { Item item = new ItemBlock(new BlockAir(), null, 0); Item old = this.slots.get(index); @@ -371,7 +371,7 @@ public boolean clear(int index) { this.slots.remove(index); } - this.onSlotChange(index, old); + this.onSlotChange(index, old, send); } return true; @@ -427,8 +427,10 @@ public void onClose(Player who) { } @Override - public void onSlotChange(int index, Item before) { - this.sendSlot(index, this.getViewers()); + public void onSlotChange(int index, Item before, boolean send) { + if (send) { + this.sendSlot(index, this.getViewers()); + } } @Override @@ -437,21 +439,20 @@ public void sendContents(Player player) { } @Override - public void sendContents(Player[] players) { - ContainerSetContentPacket pk = new ContainerSetContentPacket(); + public void sendContents(Player... players) { + InventoryContentPacket pk = new InventoryContentPacket(); pk.slots = new Item[this.getSize()]; for (int i = 0; i < this.getSize(); ++i) { pk.slots[i] = this.getItem(i); } for (Player player : players) { - pk.eid = player.getId(); int id = player.getWindowId(this); if (id == -1 || !player.spawned) { this.close(player); continue; } - pk.windowid = (byte) id; + pk.inventoryId = id; player.dataPacket(pk); } } @@ -515,8 +516,8 @@ public void sendSlot(int index, Player player) { } @Override - public void sendSlot(int index, Player[] players) { - ContainerSetSlotPacket pk = new ContainerSetSlotPacket(); + public void sendSlot(int index, Player... players) { + InventorySlotPacket pk = new InventorySlotPacket(); pk.slot = index; pk.item = this.getItem(index).clone(); @@ -526,7 +527,7 @@ public void sendSlot(int index, Player[] players) { this.close(player); continue; } - pk.windowid = (byte) id; + pk.inventoryId = id; player.dataPacket(pk); } } diff --git a/src/main/java/cn/nukkit/inventory/BaseTransaction.java b/src/main/java/cn/nukkit/inventory/BaseTransaction.java deleted file mode 100644 index 855a920e0d..0000000000 --- a/src/main/java/cn/nukkit/inventory/BaseTransaction.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.nukkit.inventory; - -import cn.nukkit.item.Item; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public class BaseTransaction implements Transaction { - - protected final Inventory inventory; - - protected final int slot; - - protected final Item sourceItem; - - protected final Item targetItem; - - protected final long creationTime; - - public BaseTransaction(Inventory inventory, int slot, Item sourceItem, Item targetItem) { - this.inventory = inventory; - this.slot = slot; - this.sourceItem = sourceItem.clone(); - this.targetItem = targetItem.clone(); - this.creationTime = System.currentTimeMillis(); - } - - @Override - public long getCreationTime() { - return creationTime; - } - - @Override - public Inventory getInventory() { - return inventory; - } - - @Override - public int getSlot() { - return slot; - } - - @Override - public Item getSourceItem() { - return sourceItem.clone(); - } - - @Override - public Item getTargetItem() { - return targetItem.clone(); - } -} diff --git a/src/main/java/cn/nukkit/inventory/BigCraftingGrid.java b/src/main/java/cn/nukkit/inventory/BigCraftingGrid.java new file mode 100644 index 0000000000..c4aebc8bc1 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/BigCraftingGrid.java @@ -0,0 +1,16 @@ +package cn.nukkit.inventory; + +/** + * @author CreeperFace + */ +public class BigCraftingGrid extends CraftingGrid { + + public BigCraftingGrid(InventoryHolder holder) { + super(holder, 9); + } + + @Override + public int getSize() { + return 9; + } +} diff --git a/src/main/java/cn/nukkit/inventory/BigShapedRecipe.java b/src/main/java/cn/nukkit/inventory/BigShapedRecipe.java index 1db57cb889..a84b3374aa 100644 --- a/src/main/java/cn/nukkit/inventory/BigShapedRecipe.java +++ b/src/main/java/cn/nukkit/inventory/BigShapedRecipe.java @@ -8,6 +8,6 @@ */ public class BigShapedRecipe extends ShapedRecipe { public BigShapedRecipe(Item result, String... shape) { - super(result, shape); + super(result, 0, 0); } } diff --git a/src/main/java/cn/nukkit/inventory/BrewingInventory.java b/src/main/java/cn/nukkit/inventory/BrewingInventory.java index e488581656..dd276f878a 100644 --- a/src/main/java/cn/nukkit/inventory/BrewingInventory.java +++ b/src/main/java/cn/nukkit/inventory/BrewingInventory.java @@ -22,9 +22,21 @@ public void setIngredient(Item item) { setItem(0, item); } + public void setFuel(Item fuel) { + setItem(4, fuel); + } + + public Item getFuel() { + return getItem(4); + } + @Override - public void onSlotChange(int index, Item before) { - super.onSlotChange(index, before); + public void onSlotChange(int index, Item before, boolean send) { + super.onSlotChange(index, before, send); + + if (index >= 0 && index <= 2) { + this.getHolder().updateBlock(); + } this.getHolder().scheduleUpdate(); } diff --git a/src/main/java/cn/nukkit/inventory/ChestInventory.java b/src/main/java/cn/nukkit/inventory/ChestInventory.java index 3d8ac1560d..0faf34ba36 100644 --- a/src/main/java/cn/nukkit/inventory/ChestInventory.java +++ b/src/main/java/cn/nukkit/inventory/ChestInventory.java @@ -4,6 +4,7 @@ import cn.nukkit.blockentity.BlockEntityChest; import cn.nukkit.level.Level; import cn.nukkit.network.protocol.BlockEventPacket; +import cn.nukkit.network.protocol.LevelSoundEventPacket; /** * author: MagicDroidX @@ -34,6 +35,7 @@ public void onOpen(Player who) { Level level = this.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_OPEN, 1, -1, this.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.getHolder().getX() >> 4, (int) this.getHolder().getZ() >> 4, pk); } } @@ -51,6 +53,7 @@ public void onClose(Player who) { Level level = this.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_CLOSED, 1, -1, this.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.getHolder().getX() >> 4, (int) this.getHolder().getZ() >> 4, pk); } } diff --git a/src/main/java/cn/nukkit/inventory/ContainerInventory.java b/src/main/java/cn/nukkit/inventory/ContainerInventory.java index 519db2b712..6238f2d7cd 100644 --- a/src/main/java/cn/nukkit/inventory/ContainerInventory.java +++ b/src/main/java/cn/nukkit/inventory/ContainerInventory.java @@ -34,8 +34,8 @@ public ContainerInventory(InventoryHolder holder, InventoryType type, Map(), overrideSize); + } + + @Override + public int getSize() { + return 4; + } + + public void setSize(int size) { + throw new RuntimeException("Cannot change the size of a crafting grid"); + } + + public String getName() { + return "Crafting"; + } + + public void removeFromAll(Item item) { + int count = item.getCount(); + + for (int i = 0; i < this.size; i++) { + Item target = this.getItem(i); + + if (target.equals(item, true, false)) { + count--; + target.count--; + this.setItem(i, target); + if (count <= 0) break; + } + } + + if (count != 0) { + MainLogger.getLogger().debug("Unexpected ingredient count (" + count + ") in crafting grid"); + } + } + + public void sendSlot(int index, Player... target) { + //we can't send a inventorySlot of a client-sided inventory window + } + + public void sendContents(Player... target) { + //we can't send the contents of a client-sided inventory window + } +} diff --git a/src/main/java/cn/nukkit/inventory/CraftingInventory.java b/src/main/java/cn/nukkit/inventory/CraftingInventory.java deleted file mode 100644 index 005903c6a3..0000000000 --- a/src/main/java/cn/nukkit/inventory/CraftingInventory.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.nukkit.inventory; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public class CraftingInventory extends BaseInventory { - - private Inventory resultInventory; - - public CraftingInventory(InventoryHolder holder, Inventory resultInventory, InventoryType type) { - super(holder, type); - if (!type.getDefaultTitle().equals("Crafting")) { - throw new IllegalStateException("Invalid Inventory type, expected CRAFTING or WORKBENCH"); - } - this.resultInventory = resultInventory; - } - - public Inventory getResultInventory() { - return resultInventory; - } - - @Override - public int getSize() { - return this.getResultInventory().getSize() + super.getSize(); - } -} diff --git a/src/main/java/cn/nukkit/inventory/CraftingManager.java b/src/main/java/cn/nukkit/inventory/CraftingManager.java index a7d7108b1d..41a7e21497 100644 --- a/src/main/java/cn/nukkit/inventory/CraftingManager.java +++ b/src/main/java/cn/nukkit/inventory/CraftingManager.java @@ -1,11 +1,17 @@ package cn.nukkit.inventory; -import cn.nukkit.block.*; +import cn.nukkit.Server; +import cn.nukkit.block.BlockAir; import cn.nukkit.item.Item; -import cn.nukkit.item.ItemDye; +import cn.nukkit.item.ItemBlock; import cn.nukkit.item.ItemPotion; +import cn.nukkit.network.protocol.CraftingDataPacket; +import cn.nukkit.utils.Config; +import cn.nukkit.utils.MainLogger; import cn.nukkit.utils.Utils; +import java.io.File; +import java.io.IOException; import java.util.*; /** @@ -24,865 +30,80 @@ public class CraftingManager { private static int RECIPE_COUNT = 0; - public CraftingManager() { - /*try { //TODO: json - Utils.writeFile(Server.getInstance().getDataPath() + "recipes.json", Server.class.getClassLoader().getResourceAsStream("recipes.json")); - } catch (IOException e) { - MainLogger.getLogger().logException(e); - } + public static CraftingDataPacket packet = null; + public CraftingManager() { + String path = Server.getInstance().getDataPath() + "recipes.json"; - Config recipes = new Config(Server.getInstance().getDataPath() + "recipes.json", Config.JSON); - - MainLogger.getLogger().info("Loading recipes..."); - for (Object obj : recipes.getAll().values()) { - ConfigSection recipe; - if (obj instanceof ConfigSection) { - recipe = (ConfigSection) obj; - } else { - continue; + if (!new File(path).exists()) { + try { + Utils.writeFile(path, Server.class.getClassLoader().getResourceAsStream("recipes.json")); + } catch (IOException e) { + MainLogger.getLogger().logException(e); } + } - - switch (recipe.getInt("type")) { + List recipes = new Config(path, Config.JSON).getMapList("recipes"); + MainLogger.getLogger().info("Loading recipes..."); + for (Map recipe : recipes) { //TODO: implement this better + switch (Utils.toInt(recipe.get("type"))) { case 0: // TODO: handle multiple result items - Map first = recipe.getMapList("output").get(0); - ShapelessRecipe result = new ShapelessRecipe(Item.get((int) first.get("id"), (int) first.get("damage"), (int) first.get("count"), first.get("nbt").toString().getBytes())); - for (Map ingredient : recipe.getMapList("input")) { - result.addIngredient(Item.get((int) ingredient.get("id"), (int) ingredient.get("damage"), (int) ingredient.get("count"), ingredient.get("nbt").toString().getBytes())); + Map first = ((List) recipe.get("output")).get(0); + ShapelessRecipe result = new ShapelessRecipe(Item.get(Utils.toInt(first.get("id")), Utils.toInt(first.get("damage")), Utils.toInt(first.get("count")), first.get("nbt").toString().getBytes())); + for (Map ingredient : ((List) recipe.get("input"))) { + result.addIngredient(Item.get(Utils.toInt(ingredient.get("id")), Utils.toInt(ingredient.get("damage")), Utils.toInt(ingredient.get("count")), ingredient.get("nbt").toString().getBytes())); + } + + String id = (String) recipe.get("uuid"); + if (id != null && !id.isEmpty()) { + UUID uuid = UUID.fromString(id); + result.setId(uuid); + this.registerShapelessRecipe(result); } - this.registerRecipe(result); break; case 1: // TODO: handle multiple result items - first = recipe.getMapList("output").get(0); - ShapedRecipe shapedRecipe = new ShapedRecipe(Item.get((int) first.get("id"), (int) first.get("damage"), (int) first.get("count"), first.get("nbt").toString().getBytes())); - List>> shape = Utils.toChunk(recipe.getList("input"), recipe.getInt("width")); + first = ((List) recipe.get("output")).get(0); + ShapedRecipe shapedRecipe = new ShapedRecipe(Item.get(Utils.toInt(first.get("id")), Utils.toInt(first.get("damage")), Utils.toInt(first.get("count")), first.get("nbt").toString().getBytes()), Utils.toInt(recipe.get("height")), Utils.toInt(recipe.get("width"))); + Object[][] shape = Utils.splitArray(((List) recipe.get("input")).stream().toArray(), Utils.toInt(recipe.get("width"))); + for (int y = 0; y < shape.length; y++) { + Object[] row = shape[y]; + for (int x = 0; x < row.length; x++) { + Object data = row[x]; + + if (data instanceof Map) { + Map ingredient = (Map) data; + shapedRecipe.addIngredient(x, y, Item.get(Utils.toInt(ingredient.get("id")), Utils.toInt(ingredient.get("damage")), Utils.toInt(ingredient.get("count")), ingredient.get("nbt").toString().getBytes())); + } else { + shapedRecipe.addIngredient(x, y, new ItemBlock(new BlockAir())); + } + } + } - for (int y = 0; y < shape.size(); y++) { - List> row = shape.get(y); + id = (String) recipe.get("uuid"); - for (int x = 0; x < row.size(); x++) { - Map ingredient = row.get(x); - shapedRecipe.addIngredient(x, y, Item.get((int) ingredient.get("id"), (int) ingredient.get("damage"), (int) ingredient.get("count"), ingredient.get("nbt").toString().getBytes())); - } + if (id != null && !id.isEmpty()) { + UUID uuid = UUID.fromString(id); + shapedRecipe.setId(uuid); + this.registerShapedRecipe(shapedRecipe); } - this.registerRecipe(shapedRecipe); break; case 2: case 3: - ConfigSection resultMap = recipe.getSection("output"); - Item resultItem = Item.get(resultMap.getInt("id"), resultMap.getInt("damage"), resultMap.getInt("count"), resultMap.getString("nbt").getBytes()); - this.registerRecipe(new FurnaceRecipe(resultItem, Item.get(recipe.getInt("id"), recipe.getInt("damage", -1), 1))); + Map resultMap = (Map) recipe.get("output"); + Item resultItem = Item.get(Utils.toInt(resultMap.get("id")), Utils.toInt(resultMap.get("damage")), Utils.toInt(resultMap.get("count")), ((String) resultMap.get("nbt")).getBytes()); + this.registerRecipe(new FurnaceRecipe(resultItem, Item.get(Utils.toInt(recipe.get("inputId")), recipe.containsKey("inputDamage") ? Utils.toInt(recipe.get("inputDamage")) : 0, 1))); break; default: break; } - }*/ - - this.registerFurnace(); - this.registerBrewing(); - this.registerDyes(); - this.registerIngots(); - this.registerTools(); - this.registerWeapons(); - this.registerArmor(); - this.registerFood(); - this.registerWoodenDoors(); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.PRISMARINE, 0, 1), - "XX", - "XX" - )).setIngredient("X", Item.get(Item.PRISMARINE_SHARD))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.PRISMARINE, 1, 1), - "XXX", - "XXX", - "XXX" - )).setIngredient("X", Item.get(Item.PRISMARINE_SHARD))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.PRISMARINE, 2, 1), - "XXX", - "XIX", - "XXX" - )).setIngredient("X", Item.get(Item.PRISMARINE_SHARD)).setIngredient("I", Item.get(Item.DYE))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.PRISMARINE, 2, 1), - "XXX", - "XIX", - "XXX" - )).setIngredient("X", Item.get(Item.PRISMARINE_SHARD)).setIngredient("I", Item.get(Item.DYE))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SEA_LANTERN, 0, 1), - "XSX", - "SSS", - "XSX" - )).setIngredient("X", Item.get(Item.PRISMARINE_SHARD)).setIngredient("S", Item.get(Item.PRISMARINE_CRYSTALS))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BEACON, 0, 1), - "GGG", - "GSG", - "OOO" - )).setIngredient("G", Item.get(Item.GLASS)).setIngredient("S", Item.get(Item.NETHER_STAR)).setIngredient("O", Item.get(Item.OBSIDIAN))); - - - this.registerRecipe((new ShapedRecipe(Item.get(Item.CLAY_BLOCK, 0, 1), - "XX ", - "XX ", - " " - )).setIngredient("X", Item.get(Item.CLAY, 0, 4))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WORKBENCH, 0, 1), - "XX", - "XX" - )).setIngredient("X", Item.get(Item.WOODEN_PLANK, null))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.GLOWSTONE_BLOCK, 0, 1), - "XX", - "XX" - )).setIngredient("X", Item.get(Item.GLOWSTONE_DUST, 0, 1))); - this.registerRecipe((new ShapedRecipe(Item.get(Item.LIT_PUMPKIN, 0, 1), - "X ", - "Y " - )).setIngredient("X", Item.get(Item.PUMPKIN, 0, 1)).setIngredient("Y", Item.get(Item.TORCH, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.SNOW_BLOCK, 0, 1), - "XX", - "XX" - )).setIngredient("X", Item.get(Item.SNOWBALL, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.SNOW_LAYER, 0, 6), - " ", - "XXX", - " " - )).setIngredient("X", Item.get(Item.SNOW_BLOCK, 0, 1))); - - - this.registerRecipe((new ShapedRecipe(Item.get(Item.STICK, 0, 4), - "X ", - "X " - )).setIngredient("X", Item.get(Item.WOODEN_PLANK, null))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOODEN_PLANK, BlockPlanks.OAK, 4), - "X" - )).setIngredient("X", Item.get(Item.WOOD, BlockWood.OAK, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOODEN_PLANK, BlockPlanks.SPRUCE, 4), - "X" - )).setIngredient("X", Item.get(Item.WOOD, BlockWood.SPRUCE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOODEN_PLANK, BlockPlanks.BIRCH, 4), - "X" - )).setIngredient("X", Item.get(Item.WOOD, BlockWood.BIRCH, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOODEN_PLANK, BlockPlanks.JUNGLE, 4), - "X" - )).setIngredient("X", Item.get(Item.WOOD, BlockWood.JUNGLE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOODEN_PLANK, BlockPlanks.ACACIA, 4), - "X" - )).setIngredient("X", Item.get(Item.WOOD2, BlockWood2.ACACIA, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOODEN_PLANK, BlockPlanks.DARK_OAK, 4), - "X" - )).setIngredient("X", Item.get(Item.WOOD2, BlockWood2.DARK_OAK, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOOL, 0, 1), - "XX", - "XX" - )).setIngredient("X", Item.get(Item.STRING, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.TORCH, 0, 4), - "C ", - "S" - )).setIngredient("C", Item.get(Item.COAL, 0, 1)).setIngredient("S", Item.get(Item.STICK, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.TORCH, 0, 4), - "C ", - "S" - )).setIngredient("C", Item.get(Item.COAL, 1, 1)).setIngredient("S", Item.get(Item.STICK, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.SUGAR, 0, 1), - "S" - )).setIngredient("S", Item.get(Item.SUGARCANE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BED, 0, 1), - "WWW", - "PPP", - " " - )).setIngredient("W", Item.get(Item.WOOL, null, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.CHEST, 0, 1), - "PPP", - "P P", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.ENCHANTMENT_TABLE, 0, 1), - " B ", - "DOD", - "OOO" - )).setIngredient("D", Item.get(Item.DIAMOND, 0, 2)).setIngredient("O", Item.get(Item.OBSIDIAN, 0, 4)).setIngredient("B", Item.get(Item.BOOK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE, BlockPlanks.OAK, 3), - "PSP", - "PSP", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE, BlockPlanks.SPRUCE, 3), - "PSP", - "PSP", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.SPRUCE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE, BlockPlanks.BIRCH, 3), - "PSP", - "PSP", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.BIRCH, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE, BlockPlanks.JUNGLE, 3), - "PSP", - "PSP", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.JUNGLE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE, BlockPlanks.ACACIA, 3), - "PSP", - "PSP", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.ACACIA, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE, BlockPlanks.DARK_OAK, 3), - "PSP", - "PSP", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.DARK_OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.NETHER_BRICK_FENCE, 0, 3), - "PPP", - "PPP", - " " - )).setIngredient("P", Item.get(Item.NETHER_BRICK_BLOCK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE_GATE, 0, 1), - "SPS", - "SPS", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE_GATE_SPRUCE, 0, 1), - "SPS", - "SPS", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.SPRUCE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE_GATE_BIRCH, 0, 1), - "SPS", - "SPS", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.BIRCH, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE_GATE_JUNGLE, 0, 1), - "SPS", - "SPS", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.JUNGLE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE_GATE_DARK_OAK, 0, 1), - "SPS", - "SPS", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.DARK_OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FENCE_GATE_ACACIA, 0, 1), - "SPS", - "SPS", - " " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.ACACIA, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.COBBLESTONE_WALL, BlockWall.NONE_MOSSY_WALL, 6), - "CCC", - "CCC", - " " - )).setIngredient("C", Item.get(Item.COBBLESTONE, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.COBBLESTONE_WALL, BlockWall.MOSSY_WALL, 6), - "MMM", - "MMM", - " " - )).setIngredient("M", Item.get(Item.MOSS_STONE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.FURNACE, 0, 1), - "CCC", - "C C", - "CCC" - )).setIngredient("C", Item.get(Item.COBBLESTONE, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.GLASS_PANE, 0, 16), - "GGG", - "GGG", - " " - )).setIngredient("G", Item.get(Item.GLASS, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.LADDER, 0, 3), - "S S", - "SSS", - "S S" - )).setIngredient("S", Item.get(Item.STICK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.TRAPDOOR, 0, 2), - "PPP", - "PPP", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.IRON_DOOR, 0, 1), - "II ", - "II ", - "II " - )).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOAT, 0, 1), - "PSP", - "PPP", - " " - )).setIngredient("S", Item.get(Item.WOODEN_SHOVEL, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOAT, 1, 1), - "PSP", - "PPP", - " " - )).setIngredient("S", Item.get(Item.WOODEN_SHOVEL, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.SPRUCE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOAT, 2, 1), - "PSP", - "PPP", - " " - )).setIngredient("S", Item.get(Item.WOODEN_SHOVEL, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.BIRCH, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOAT, 3, 1), - "PSP", - "PPP", - " " - )).setIngredient("S", Item.get(Item.WOODEN_SHOVEL, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.JUNGLE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOAT, 4, 1), - "PSP", - "PPP", - " " - )).setIngredient("S", Item.get(Item.WOODEN_SHOVEL, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.ACACIA, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOAT, 5, 1), - "PSP", - "PPP", - " " - )).setIngredient("S", Item.get(Item.WOODEN_SHOVEL, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.DARK_OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SANDSTONE_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.SANDSTONE, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOODEN_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SPRUCE_WOOD_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.SPRUCE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BIRCH_WOOD_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.BIRCH, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.JUNGLE_WOOD_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.JUNGLE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.ACACIA_WOOD_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.ACACIA, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.DARK_OAK_WOOD_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.DARK_OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.COBBLESTONE_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.COBBLESTONE, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BRICK_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.BRICKS_BLOCK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE_BRICK_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.STONE_BRICK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.QUARTZ_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.QUARTZ_BLOCK, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.NETHER_BRICKS_STAIRS, 0, 4), - "P ", - "PP ", - "PPP" - )).setIngredient("P", Item.get(Item.NETHER_BRICK_BLOCK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SLAB, BlockSlabStone.STONE, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.STONE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SLAB, BlockSlabStone.SANDSTONE, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.SANDSTONE, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOOD_SLAB, BlockPlanks.OAK, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOOD_SLAB, BlockPlanks.SPRUCE, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.SPRUCE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOOD_SLAB, BlockPlanks.BIRCH, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.BIRCH, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOOD_SLAB, BlockPlanks.JUNGLE, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.JUNGLE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOOD_SLAB, BlockPlanks.ACACIA, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.ACACIA, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOOD_SLAB, BlockPlanks.DARK_OAK, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, BlockPlanks.DARK_OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SLAB, BlockSlabStone.COBBLESTONE, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.COBBLESTONE, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SLAB, BlockSlabStone.BRICK, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.BRICK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SLAB, BlockSlabStone.STONE_BRICK, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.STONE_BRICK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SLAB, BlockSlabStone.QUARTZ, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.QUARTZ_BLOCK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SLAB, BlockSlabStone.NETHER_BRICK, 6), - "PPP", - " ", - " " - )).setIngredient("P", Item.get(Item.NETHER_BRICK_BLOCK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.QUARTZ_BLOCK, BlockQuartz.QUARTZ_NORMAL, 1), - "NN ", - "NN ", - " " - )).setIngredient("N", Item.get(Item.NETHER_QUARTZ, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.QUARTZ_BLOCK, BlockQuartz.QUARTZ_PILLAR, 2), - "N ", - "N ", - " " - )).setIngredient("N", Item.get(Item.QUARTZ_BLOCK, BlockQuartz.QUARTZ_NORMAL, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.QUARTZ_BLOCK, BlockQuartz.QUARTZ_CHISELED, 1), - " ", - " N ", - " N " - )).setIngredient("N", Item.get(Item.SLAB, BlockSlabStone.QUARTZ, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SANDSTONE, BlockSandstone.NORMAL, 1), - " ", - "SS ", - "SS " - )).setIngredient("S", Item.get(Item.SAND, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SANDSTONE, BlockSandstone.SMOOTH, 4), - " ", - "SS ", - "SS " - )).setIngredient("S", Item.get(Item.SANDSTONE, BlockSandstone.NORMAL, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SANDSTONE, BlockSandstone.CHISELED, 1), - " ", - " N ", - " N " - )).setIngredient("N", Item.get(Item.SLAB, BlockSlabStone.SANDSTONE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE_BRICK, BlockBricksStone.NORMAL, 4), - " ", - "SS ", - "SS " - )).setIngredient("S", Item.get(Item.STONE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE_BRICK, BlockBricksStone.MOSSY, 1), - " ", - "SL ", - " " - )).setIngredient("S", Item.get(Item.STONE_BRICK, BlockBricksStone.NORMAL, 1)).setIngredient("L", Item.get(Item.VINES, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE_BRICK, BlockBricksStone.CHISELED, 1), - " ", - " S ", - " S " - )).setIngredient("S", Item.get(Item.SLAB, BlockSlabStone.STONE_BRICK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.MOSS_STONE, 0, 1), - " ", - "SL ", - " " - )).setIngredient("S", Item.get(Item.COBBLESTONE, null, 1)).setIngredient("L", Item.get(Item.VINES, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE, BlockStone.GRANITE, 1), - " ", - "DN ", - " " - )).setIngredient("D", Item.get(Item.STONE, BlockStone.DIORITE, 1)).setIngredient("N", Item.get(Item.NETHER_QUARTZ, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE, BlockStone.POLISHED_GRANITE, 4), - " ", - "GG ", - "GG " - )).setIngredient("G", Item.get(Item.STONE, BlockStone.GRANITE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE, BlockStone.DIORITE, 2), - " ", - "CN ", - "NC " - )).setIngredient("C", Item.get(Item.COBBLESTONE, null, 1)).setIngredient("N", Item.get(Item.NETHER_QUARTZ, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE, BlockStone.POLISHED_DIORITE, 4), - " ", - "DD ", - "DD " - )).setIngredient("D", Item.get(Item.STONE, BlockStone.DIORITE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE, BlockStone.ANDESITE, 2), - " ", - "DC ", - " " - )).setIngredient("D", Item.get(Item.STONE, BlockStone.DIORITE, 1)).setIngredient("C", Item.get(Item.COBBLESTONE, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE, BlockStone.POLISHED_ANDESITE, 4), - " ", - "AA ", - "AA " - )).setIngredient("A", Item.get(Item.STONE, BlockStone.ANDESITE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BUCKET, 0, 1), - "I I", - " I ", - " " - )).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.CLOCK, 0, 1), - " G ", - "GRG", - " G " - )).setIngredient("G", Item.get(Item.GOLD_INGOT, 0, 1)).setIngredient("R", Item.get(Item.REDSTONE_DUST, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.COMPASS, 0, 1), - " I ", - "IRI", - " I " - )).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 1)).setIngredient("R", Item.get(Item.REDSTONE_DUST, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.TNT, 0, 1), - "GSG", - "SGS", - "GSG" - )).setIngredient("G", Item.get(Item.GUNPOWDER, 0, 1)).setIngredient("S", Item.get(Item.SAND, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOWL, 0, 4), - "P P", - " P ", - " " - )).setIngredient("P", Item.get(Item.WOODEN_PLANKS, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.MINECART, 0, 1), - "I I", - "III" - )).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOOK, 0, 1), - "P P", - " P " - )).setIngredient("P", Item.get(Item.PAPER, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOOKSHELF, 0, 1), - "PPP", - "BBB", - "PPP" - )).setIngredient("P", Item.get(Item.WOODEN_PLANK, null, 1)).setIngredient("B", Item.get(Item.BOOK, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.PAINTING, 0, 1), - "SSS", - "SWS", - "SSS" - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("W", Item.get(Item.WOOL, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.PAPER, 0, 3), - " ", - "SSS", - " " - )).setIngredient("S", Item.get(Item.SUGARCANE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.SIGN, 0, 3), - "PPP", - "PPP", - " S " - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("P", Item.get(Item.WOODEN_PLANKS, null, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.IRON_BARS, 0, 16), - " ", - "III", - "III" - )).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.GLASS_BOTTLE, 0, 3), - "I I", - " I ", - " " - )).setIngredient("I", Item.get(Item.GLASS, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BREWING_STAND, 0, 1), - " I ", - "CCC" - )).setIngredient("C", Item.get(Item.COBBLESTONE, null, 1)).setIngredient("I", Item.get(Item.BLAZE_ROD, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.ITEM_FRAME, 0, 3), - "SSS", - "SLS", - "SSS" - )).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("L", Item.get(Item.LEATHER, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.RED_SANDSTONE_STAIRS, 0, 4), - " S", - " SS", - "SSS" - )).setIngredient("S", Item.get(Item.RED_SANDSTONE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.RED_SANDSTONE_SLAB, 0, 6), - " ", - "SSS", - " " - )).setIngredient("S", Item.get(Item.RED_SANDSTONE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.RED_SANDSTONE, BlockSandstone.NORMAL, 1), - " ", - "SS ", - "SS " - )).setIngredient("S", Item.get(Item.SAND, 1, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.RED_SANDSTONE, BlockSandstone.SMOOTH, 4), - " ", - "SS ", - "SS " - )).setIngredient("S", Item.get(Item.RED_SANDSTONE, BlockSandstone.NORMAL, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.RED_SANDSTONE, BlockSandstone.CHISELED, 1), - " ", - " N ", - " N " - )).setIngredient("N", Item.get(Item.RED_SANDSTONE_SLAB, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STONE_BUTTON, 0, 1), - " ", - " N ", - " " - )).setIngredient("N", Item.get(Item.STONE, 0, 1))); - - - //all planks... - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOODEN_BUTTON, 0, 1), - " ", - " N ", - " " - )).setIngredient("N", Item.get(Item.PLANKS, BlockPlanks.OAK, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOODEN_BUTTON, 0, 1), - " ", - " N ", - " " - )).setIngredient("N", Item.get(Item.PLANKS, BlockPlanks.SPRUCE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOODEN_BUTTON, 0, 1), - " ", - " N ", - " " - )).setIngredient("N", Item.get(Item.PLANKS, BlockPlanks.BIRCH, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOODEN_BUTTON, 0, 1), - " ", - " N ", - " " - )).setIngredient("N", Item.get(Item.PLANKS, BlockPlanks.JUNGLE, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOODEN_BUTTON, 0, 1), - " ", - " N ", - " " - )).setIngredient("N", Item.get(Item.PLANKS, BlockPlanks.ACACIA, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.WOODEN_BUTTON, 0, 1), - " ", - " N ", - " " - )).setIngredient("N", Item.get(Item.PLANKS, BlockPlanks.DARK_OAK, 1))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.ENDER_CHEST, 0, 1), - "PPP", - "PSP", - "PPP" - ).setIngredient("P", Item.get(Item.OBSIDIAN, 0, 1)).setIngredient("S", Item.get(Item.ENDER_EYE, 0, 1))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.REDSTONE_TORCH, 0, 1), - " R", - " S" - ).setIngredient("R", Item.get(Item.REDSTONE_DUST, 0, 1)).setIngredient("S", Item.get(Item.STICK, 0, 1))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.LEVER, 0, 1), - " S", - " C" - ).setIngredient("C", Item.get(Item.COBBLESTONE, null, 1)).setIngredient("S", Item.get(Item.STICK, 0, 1))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.STONE_BUTTON, 0, 1), - " S" - ).setIngredient("S", Item.get(Item.STONE, 0, 1))); - - for (int i = 0; i < 6; i++) { - this.registerRecipe(new BigShapedRecipe(Item.get(Item.WOODEN_BUTTON, 0, 1), - " W" - ).setIngredient("W", Item.get(Item.PLANKS, i, 1))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.WOODEN_PRESSURE_PLATE, 0, 1), - "WW " - ).setIngredient("W", Item.get(Item.PLANKS, i, 2))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.TRIPWIRE_HOOK, 0, 2), - " I ", - " S ", - " P " - ).setIngredient("P", Item.get(Item.PLANKS, i, 1)).setIngredient("S", Item.get(Item.STICK, 0, 1)).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 1))); } - this.registerRecipe(new BigShapedRecipe(Item.get(Item.STONE_PRESSURE_PLATE, 0, 1), - "SS " - ).setIngredient("S", Item.get(Item.STONE, 0, 2))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.HEAVY_WEIGHTED_PRESSURE_PLATE, 0, 1), - "II " - ).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 2))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.LIGHT_WEIGHTED_PRESSURE_PLATE, 0, 1), - "GG " - ).setIngredient("G", Item.get(Item.GOLD_INGOT, 0, 2))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.REDSTONE_LAMP, 0, 1), - " R ", - "RGR", - " R " - ).setIngredient("R", Item.get(Item.REDSTONE_DUST, 0, 4)).setIngredient("G", Item.get(Item.GLOWSTONE_BLOCK, 0, 1))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.REPEATER, 0, 1), - " ", - "TRT", - "SSS" - ).setIngredient("R", Item.get(Item.REDSTONE_DUST, 0, 1)).setIngredient("T", Item.get(Item.REDSTONE_TORCH, 0, 2)).setIngredient("S", Item.get(Item.STONE, 0, 3))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.COMPARATOR, 0, 1), - " T ", - "TQT", - "SSS" - ).setIngredient("Q", Item.get(Item.QUARTZ, 0, 1)).setIngredient("T", Item.get(Item.REDSTONE_TORCH, 0, 3)).setIngredient("S", Item.get(Item.STONE, 0, 3))); - - this.registerRecipe(new BigShapedRecipe(Item.get(Item.HOPPER, 0, 1), - "I I", - "ICI", - " I " - ).setIngredient("I", Item.get(Item.IRON_INGOT, 0, 5)).setIngredient("C", Item.get(Item.CHEST, 0, 1))); - } - - protected void registerFurnace() { - this.registerRecipe(new FurnaceRecipe(Item.get(Item.STONE, 0, 1), Item.get(Item.COBBLESTONE, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.STONE_BRICK, BlockBricksStone.CRACKED, 1), Item.get(Item.STONE_BRICK, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.GLASS, 0, 1), Item.get(Item.SAND, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.COAL, 1, 1), Item.get(Item.TRUNK, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.GOLD_INGOT, 0, 1), Item.get(Item.GOLD_ORE, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.IRON_INGOT, 0, 1), Item.get(Item.IRON_ORE, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.EMERALD, 0, 1), Item.get(Item.EMERALD_ORE, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.DIAMOND, 0, 1), Item.get(Item.DIAMOND_ORE, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.NETHER_BRICK, 0, 1), Item.get(Item.NETHERRACK, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.COOKED_PORKCHOP, 0, 1), Item.get(Item.RAW_PORKCHOP, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.BRICK, 0, 1), Item.get(Item.CLAY, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.COOKED_FISH, 0, 1), Item.get(Item.RAW_FISH, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.COOKED_FISH, 1, 1), Item.get(Item.RAW_FISH, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.DYE, 2, 1), Item.get(Item.CACTUS, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.DYE, 1, 1), Item.get(Item.RED_MUSHROOM, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.STEAK, 0, 1), Item.get(Item.RAW_BEEF, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.COOKED_CHICKEN, 0, 1), Item.get(Item.RAW_CHICKEN, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.BAKED_POTATO, 0, 1), Item.get(Item.POTATO, null, 1))); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.COOKED_MUTTON, 0, 1), Item.get(Item.RAW_MUTTON, null, 1))); + this.registerBrewing(); + this.rebuildPacket(); - this.registerRecipe(new FurnaceRecipe(Item.get(Item.TERRACOTTA, 0, 1), Item.get(Item.CLAY_BLOCK, null, 1))); + MainLogger.getLogger().info("Loaded " + this.recipes.size() + " recipes."); } protected void registerBrewing() { @@ -927,340 +148,26 @@ protected void registerBrewing() { registerBrewingRecipe(new BrewingRecipe(Item.get(Item.POTION, ItemPotion.HARMING_II, 1), Item.get(Item.FERMENTED_SPIDER_EYE, 0, 1), Item.get(Item.POTION, ItemPotion.POISON_LONG, 1))); } - protected void registerFood() { - this.registerRecipe((new BigShapedRecipe(Item.get(Item.MELON_BLOCK, 0, 1), - "XXX", - "XXX", - "XXX" - )).setIngredient("X", Item.get(Item.MELON_SLICE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BEETROOT_SOUP, 0, 1), - "XXX", - "XXX", - " Y " - )).setIngredient("X", Item.get(Item.BEETROOT, 0, 1)).setIngredient("Y", Item.get(Item.BOWL, 0, 1))); - - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BREAD, 0, 1), - " ", - " ", - "XXX" - )).setIngredient("X", Item.get(Item.WHEAT, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.CAKE, 0, 1), - "XXX", - "YZY", - "AAA" - )).setIngredient("X", Item.get(Item.BUCKET, 1, 1)).setIngredient("Y", Item.get(Item.SUGAR, 0, 1)).setIngredient("Z", Item.get(Item.EGG, 0, 1)).setIngredient("A", Item.get(Item.WHEAT, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.COOKIE, 0, 8), - " ", - " ", - "XYX" - )).setIngredient("X", Item.get(Item.WHEAT, 0, 1)).setIngredient("Y", Item.get(Item.DYE, 3, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.GOLDEN_APPLE, 0, 1), - "XXX", - "XYX", - "XXX" - )).setIngredient("X", Item.get(Item.GOLD_INGOT, 0, 1)).setIngredient("Y", Item.get(Item.APPLE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.GOLDEN_APPLE_ENCHANTED, 1, 1), - "XXX", - "XYX", - "XXX" - )).setIngredient("X", Item.get(Item.GOLD_BLOCK, 0, 1)).setIngredient("Y", Item.get(Item.APPLE, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.MUSHROOM_STEW, 0, 1), - " X ", - " Y ", - " Z " - )).setIngredient("X", Item.get(Item.RED_MUSHROOM, 0, 1)).setIngredient("Y", Item.get(Item.BROWN_MUSHROOM, 0, 1)).setIngredient("Z", Item.get(Item.BOWL, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.PUMPKIN_PIE, 0, 1), - " ", - "XY ", - "Z " - )).setIngredient("X", Item.get(Item.PUMPKIN, 0, 1)).setIngredient("Y", Item.get(Item.EGG, 0, 1)).setIngredient("Z", Item.get(Item.SUGAR, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.MELON_SEEDS, 0, 1), - "X ", - " " - )).setIngredient("X", Item.get(Item.MELON_SLICE, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.PUMPKIN_SEEDS, 0, 4), - "X ", - " " - )).setIngredient("X", Item.get(Item.PUMPKIN, 0, 1))); - } - - protected void registerArmor() { - int[][] types = { - {Item.LEATHER, Item.FIRE, Item.IRON_INGOT, Item.DIAMOND, Item.GOLD_INGOT}, - {Item.LEATHER_CAP, Item.CHAIN_HELMET, Item.IRON_HELMET, Item.DIAMOND_HELMET, Item.GOLD_HELMET}, - {Item.LEATHER_TUNIC, Item.CHAIN_CHESTPLATE, Item.IRON_CHESTPLATE, Item.DIAMOND_CHESTPLATE, Item.GOLD_CHESTPLATE}, - {Item.LEATHER_PANTS, Item.CHAIN_LEGGINGS, Item.IRON_LEGGINGS, Item.DIAMOND_LEGGINGS, Item.GOLD_LEGGINGS}, - {Item.LEATHER_BOOTS, Item.CHAIN_BOOTS, Item.IRON_BOOTS, Item.DIAMOND_BOOTS, Item.GOLD_BOOTS}, - }; + public void rebuildPacket() { + CraftingDataPacket pk = new CraftingDataPacket(); + pk.cleanRecipes = true; - String[][] shapes = new String[][]{ - new String[]{ - "XXX", - "X X", - " " - }, - new String[]{ - "X X", - "XXX", - "XXX" - }, - new String[]{ - "XXX", - "X X", - "X X" - }, - new String[]{ - " ", - "X X", - "X X" - } - }; - for (int i = 1; i < 5; ++i) { - for (int j = 0; j < types[i].length; j++) { - int type = types[i][j]; - this.registerRecipe((new BigShapedRecipe(Item.get(type, 0, 1), shapes[i - 1])).setIngredient("X", Item.get(types[0][j], 0, 1))); + for (Recipe recipe : this.getRecipes().values()) { + if (recipe instanceof ShapedRecipe) { + pk.addShapedRecipe((ShapedRecipe) recipe); + } else if (recipe instanceof ShapelessRecipe) { + pk.addShapelessRecipe((ShapelessRecipe) recipe); } } - } - protected void registerWeapons() { - int[][] types = { - {Item.WOODEN_PLANK, Item.COBBLESTONE, Item.IRON_INGOT, Item.DIAMOND, Item.GOLD_INGOT}, - {Item.WOODEN_SWORD, Item.STONE_SWORD, Item.IRON_SWORD, Item.DIAMOND_SWORD, Item.GOLD_SWORD}, - }; - for (int i = 1; i < 2; ++i) { - for (int j = 0; j < types[i].length; j++) { - int type = types[i][j]; - this.registerRecipe((new BigShapedRecipe(Item.get(type, 0, 1), - " X ", - " X ", - " I " - )).setIngredient("X", Item.get(types[0][j], null)).setIngredient("I", Item.get(Item.STICK))); - } + for (FurnaceRecipe recipe : this.getFurnaceRecipes().values()) { + pk.addFurnaceRecipe(recipe); } - this.registerRecipe((new BigShapedRecipe(Item.get(Item.ARROW, 0, 1), - " F ", - " S ", - " P " - )).setIngredient("S", Item.get(Item.STICK)).setIngredient("F", Item.get(Item.FLINT)).setIngredient("P", Item.get(Item.FEATHER))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.BOW, 0, 1), - " X~", - "X ~", - " X~" - )).setIngredient("~", Item.get(Item.STRING)).setIngredient("X", Item.get(Item.STICK))); - } - - protected void registerTools() { - int[][] types = { - {Item.WOODEN_PLANK, Item.COBBLESTONE, Item.IRON_INGOT, Item.DIAMOND, Item.GOLD_INGOT}, - {Item.WOODEN_PICKAXE, Item.STONE_PICKAXE, Item.IRON_PICKAXE, Item.DIAMOND_PICKAXE, Item.GOLD_PICKAXE}, - {Item.WOODEN_SHOVEL, Item.STONE_SHOVEL, Item.IRON_SHOVEL, Item.DIAMOND_SHOVEL, Item.GOLD_SHOVEL}, - {Item.WOODEN_AXE, Item.STONE_AXE, Item.IRON_AXE, Item.DIAMOND_AXE, Item.GOLD_AXE}, - {Item.WOODEN_HOE, Item.STONE_HOE, Item.IRON_HOE, Item.DIAMOND_HOE, Item.GOLD_HOE}, - }; - String[][] shapes = new String[][]{ - new String[]{ - "XXX", - " I ", - " I " - }, - new String[]{ - " X ", - " I ", - " I " - }, - new String[]{ - "XX ", - "XI ", - " I " - }, - new String[]{ - "XX ", - " I ", - " I " - } - }; - for (int i = 1; i < 5; ++i) { - for (int j = 0; j < types[i].length; j++) { - int type = types[i][j]; - this.registerRecipe((new BigShapedRecipe(Item.get(type, 0, 1), shapes[i - 1])).setIngredient('X', Item.get(types[0][j], null)).setIngredient('I', Item.get(Item.STICK))); - } - } - - this.registerRecipe((new ShapedRecipe(Item.get(Item.FLINT_AND_STEEL, 0, 1), - " S", - "F " - )).setIngredient('F', Item.get(Item.FLINT)).setIngredient("S", Item.get(Item.IRON_INGOT))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.SHEARS, 0, 1), - " X", - "X " - )).setIngredient("X", Item.get(Item.IRON_INGOT))); - } - - protected void registerWoodenDoors() { - int[] items = new int[]{Item.WOODEN_DOOR, Item.SPRUCE_DOOR, Item.BIRCH_DOOR, Item.JUNGLE_DOOR, Item.ACACIA_DOOR, Item.DARK_OAK_DOOR}; - - for (int i = 0; i < 6; i++) { - this.registerRecipe((new BigShapedRecipe(Item.get(items[i], 0, 3), - "XX ", - "XX ", - "XX " - )).setIngredient("X", Item.get(Item.PLANKS, i, 1))); - } - } - - protected void registerPlanks() { - //TODO - } - - protected void registerDyes() { - for (int i = 0; i < 16; ++i) { - this.registerRecipe((new ShapedRecipe(Item.get(Item.WOOL, 15 - i, 1), - " ", - "DW" - )).setIngredient('D', Item.get(Item.DYE, i, 1)).setIngredient('W', Item.get(Item.WOOL, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(Item.STAINED_TERRACOTTA, 15 - i, 8), - "CCC", - "CDC", - "CCC" - )).setIngredient('D', Item.get(Item.DYE, i, 1)).setIngredient('C', Item.get(Item.TERRACOTTA, 0, 1))); - //TODO: add glass things? - - this.registerRecipe((new ShapedRecipe(Item.get(Item.CARPET, i, 3), - " ", - "WW" - )).setIngredient('W', Item.get(Item.WOOL, i, 1))); - } - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.YELLOW, 2), - " ", - "D " - )).setIngredient('D', Item.get(Item.DANDELION, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.WHITE, 3), - " ", - "B " - )).setIngredient('B', Item.get(Item.BONE, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.BROWN, 2), - " B", - "O " - )).setIngredient('O', Item.get(Item.DYE, ItemDye.ORANGE, 1)).setIngredient('B', Item.get(Item.DYE, ItemDye.BLACK, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.BROWN, 3), - "RB", - "Y " - )).setIngredient('R', Item.get(Item.DYE, ItemDye.RED, 1)).setIngredient('B', Item.get(Item.DYE, ItemDye.BLACK, 1)).setIngredient('Y', Item.get(Item.DYE, ItemDye.YELLOW, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.PINK, 2), - " R", - "W " - )).setIngredient('W', Item.get(Item.DYE, ItemDye.WHITE, 1)).setIngredient('R', Item.get(Item.DYE, ItemDye.RED, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.ORANGE, 2), - " R", - "Y " - )).setIngredient('Y', Item.get(Item.DYE, ItemDye.YELLOW, 1)).setIngredient('R', Item.get(Item.DYE, ItemDye.RED, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.LIME, 2), - " G", - "W " - )).setIngredient('G', Item.get(Item.DYE, ItemDye.GREEN, 1)).setIngredient('W', Item.get(Item.DYE, ItemDye.WHITE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.LIGHT_BLUE, 2), - " B", - "W " - )).setIngredient('B', Item.get(Item.DYE, ItemDye.BLUE, 1)).setIngredient('W', Item.get(Item.DYE, ItemDye.WHITE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.CYAN, 2), - " G", - "B " - )).setIngredient('B', Item.get(Item.DYE, ItemDye.BLUE, 1)).setIngredient('G', Item.get(Item.DYE, ItemDye.GREEN, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.PURPLE, 2), - " R", - "B " - )).setIngredient('B', Item.get(Item.DYE, ItemDye.BLUE, 1)).setIngredient('R', Item.get(Item.DYE, ItemDye.RED, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.MAGENTA, 3), - "RW", - "B " - )).setIngredient('B', Item.get(Item.DYE, ItemDye.BLUE, 1)).setIngredient('R', Item.get(Item.DYE, ItemDye.RED, 1)).setIngredient('W', Item.get(Item.DYE, ItemDye.WHITE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.RED, 1), - " ", - "B " - )).setIngredient('B', Item.get(Item.BEETROOT, 0, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.MAGENTA, 4), - "RB", - "WR" - )).setIngredient('W', Item.get(Item.DYE, ItemDye.WHITE, 1)).setIngredient('R', Item.get(Item.DYE, ItemDye.RED, 1)).setIngredient('B', Item.get(Item.DYE, ItemDye.BLUE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.MAGENTA, 2), - " D", - "P " - )).setIngredient('P', Item.get(Item.DYE, ItemDye.PURPLE, 1)).setIngredient('D', Item.get(Item.DYE, ItemDye.PINK, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.GRAY, 2), - " W", - "B " - )).setIngredient('B', Item.get(Item.DYE, ItemDye.BLACK, 1)).setIngredient('W', Item.get(Item.DYE, ItemDye.WHITE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.LIGHT_GRAY, 3), - "B ", - "WW" - )).setIngredient('B', Item.get(Item.DYE, ItemDye.BLACK, 1)).setIngredient('W', Item.get(Item.DYE, ItemDye.WHITE, 1))); - - this.registerRecipe((new ShapedRecipe(Item.get(Item.DYE, ItemDye.LIGHT_GRAY, 2), - " G", - "B " - )).setIngredient('B', Item.get(Item.DYE, ItemDye.BLACK, 1)).setIngredient('G', Item.get(Item.DYE, ItemDye.GRAY, 1))); - } - - protected void registerIngots() { - int[][] ingots = new int[][]{ - {Item.GOLD_BLOCK, Item.GOLD_INGOT}, - {Item.GOLD_INGOT, Item.GOLD_NUGGET}, - {Item.IRON_BLOCK, Item.IRON_INGOT}, - {Item.DIAMOND_BLOCK, Item.DIAMOND}, - {Item.EMERALD_BLOCK, Item.EMERALD}, - {Item.REDSTONE_BLOCK, Item.REDSTONE_DUST}, - {Item.COAL_BLOCK, Item.COAL}, - {Item.HAY_BALE, Item.WHEAT}, - {Item.LAPIS_BLOCK, Item.DYE, 4} - }; - - for (int[] e : ingots) { - int block = e[0]; - int ingot = e[1]; - int ingot_meta = e.length > 2 ? e[2] : 0; - - this.registerRecipe((new ShapedRecipe(Item.get(ingot, ingot_meta, 9), - "X" - )).setIngredient("X", Item.get(block, 0, 1))); - - this.registerRecipe((new BigShapedRecipe(Item.get(block, 0, 1), - "XXX", - "XXX", - "XXX" - )).setIngredient("X", Item.get(ingot, ingot_meta, 1))); - } + pk.encode(); + pk.isEncoded = true; + packet = pk; } public final Comparator comparator = (i1, i2) -> { @@ -1413,7 +320,7 @@ public boolean matchRecipe(ShapelessRecipe recipe) { } public void registerRecipe(Recipe recipe) { - recipe.setId(Utils.dataToUUID(String.valueOf(++RECIPE_COUNT), String.valueOf(recipe.getResult().getId()), String.valueOf(recipe.getResult().getDamage()), String.valueOf(recipe.getResult().getCount()), Arrays.toString(recipe.getResult().getCompoundTag()))); + //recipe.setId(Utils.dataToUUID(String.valueOf(++RECIPE_COUNT), String.valueOf(recipe.getResult().getId()), String.valueOf(recipe.getResult().getDamage()), String.valueOf(recipe.getResult().getCount()), Arrays.toString(recipe.getResult().getCompoundTag()))); if (recipe instanceof ShapedRecipe) { this.registerShapedRecipe((ShapedRecipe) recipe); @@ -1425,6 +332,8 @@ public void registerRecipe(Recipe recipe) { } public Recipe[] getRecipesByResult(Item result) { + Map lookup = recipeLookup.get(result.getId() + ":" + result.getDamage()); + if (lookup == null) return new Recipe[0]; return recipeLookup.get(result.getId() + ":" + result.getDamage()).values().stream().toArray(Recipe[]::new); } diff --git a/src/main/java/cn/nukkit/inventory/DoubleChestInventory.java b/src/main/java/cn/nukkit/inventory/DoubleChestInventory.java index a2337ead25..522e030bae 100644 --- a/src/main/java/cn/nukkit/inventory/DoubleChestInventory.java +++ b/src/main/java/cn/nukkit/inventory/DoubleChestInventory.java @@ -5,6 +5,7 @@ import cn.nukkit.item.Item; import cn.nukkit.level.Level; import cn.nukkit.network.protocol.BlockEventPacket; +import cn.nukkit.network.protocol.LevelSoundEventPacket; import java.util.HashMap; import java.util.Map; @@ -57,8 +58,8 @@ public Item getItem(int index) { } @Override - public boolean setItem(int index, Item item) { - return index < this.left.getSize() ? this.left.setItem(index, item) : this.right.setItem(index - this.right.getSize(), item); + public boolean setItem(int index, Item item, boolean send) { + return index < this.left.getSize() ? this.left.setItem(index, item, send) : this.right.setItem(index - this.right.getSize(), item, send); } @Override @@ -105,6 +106,8 @@ public void setContents(Map items) { @Override public void onOpen(Player who) { super.onOpen(who); + this.left.viewers.add(who); + this.right.viewers.add(who); if (this.getViewers().size() == 1) { BlockEventPacket pk1 = new BlockEventPacket(); @@ -115,6 +118,7 @@ public void onOpen(Player who) { pk1.case2 = 2; Level level = this.left.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_OPEN, 1, -1, this.left.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.left.getHolder().getX() >> 4, (int) this.left.getHolder().getZ() >> 4, pk1); } @@ -127,6 +131,7 @@ public void onOpen(Player who) { level = this.right.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_OPEN, 1, -1, this.right.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.right.getHolder().getX() >> 4, (int) this.right.getHolder().getZ() >> 4, pk2); } } @@ -144,6 +149,7 @@ public void onClose(Player who) { Level level = this.right.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_CLOSED, 1, -1, this.right.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.right.getHolder().getX() >> 4, (int) this.right.getHolder().getZ() >> 4, pk1); } @@ -156,10 +162,13 @@ public void onClose(Player who) { level = this.left.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_CLOSED, 1, -1, this.left.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.left.getHolder().getX() >> 4, (int) this.left.getHolder().getZ() >> 4, pk2); } } + this.left.viewers.remove(who); + this.right.viewers.remove(who); super.onClose(who); } diff --git a/src/main/java/cn/nukkit/inventory/EnchantInventory.java b/src/main/java/cn/nukkit/inventory/EnchantInventory.java index 38476755dc..0f74ff60a5 100644 --- a/src/main/java/cn/nukkit/inventory/EnchantInventory.java +++ b/src/main/java/cn/nukkit/inventory/EnchantInventory.java @@ -1,12 +1,14 @@ package cn.nukkit.inventory; import cn.nukkit.Player; -import cn.nukkit.Server; +import cn.nukkit.block.Block; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBookEnchanted; import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.item.enchantment.EnchantmentEntry; import cn.nukkit.item.enchantment.EnchantmentList; +import cn.nukkit.level.Level; +import cn.nukkit.level.Location; import cn.nukkit.level.Position; import cn.nukkit.math.NukkitRandom; import cn.nukkit.network.protocol.CraftingDataPacket; @@ -67,8 +69,8 @@ public void onOpen(Player who) { } @Override - public void onSlotChange(int index, Item before) { - super.onSlotChange(index, before); + public void onSlotChange(int index, Item before, boolean send) { + super.onSlotChange(index, before, send); if (index == 0) { Item item = this.getItem(0); @@ -218,8 +220,38 @@ public void onEnchant(Player who, Item before, Item after) { } public int countBookshelf() { - return 15; - //todo calculate bookshelf + int count = 0; + Location loc = this.getHolder().getLocation(); + Level level = loc.getLevel(); + + for (int y = 0; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + if (z == 0 && x == 0) continue; + if (level.getBlock(loc.add(x, 0, z)).isTransparent()) { + if (level.getBlock(loc.add(0, 1, 0)).isTransparent()) { + //diagonal and straight + if (level.getBlock(loc.add(x << 1, y, z << 1)).getId() == Block.BOOKSHELF) { + count++; + } + + if (x != 0 && z != 0) { + //one block diagonal and one straight + if (level.getBlock(loc.add(x << 1, y, z)).getId() == Block.BOOKSHELF) { + ++count; + } + + if (level.getBlock(loc.add(x, y, z << 1)).getId() == Block.BOOKSHELF) { + ++count; + } + } + } + } + } + } + } + + return count; } public void sendEnchantmentList() { @@ -232,7 +264,21 @@ public void sendEnchantmentList() { pk.addEnchantList(list); } - Server.broadcastPacket(this.getViewers(), pk); + //Server.broadcastPacket(this.getViewers(), pk); //TODO: fix this, causes crash in 1.2 + } + + /*@Override + public void sendSlot(int index, Player... players) { + } + @Override + public void sendContents(Player... players) { + + } + + @Override + public boolean setItem(int index, Item item, boolean send) { + return super.setItem(index, item, false); + }*/ } diff --git a/src/main/java/cn/nukkit/inventory/FurnaceInventory.java b/src/main/java/cn/nukkit/inventory/FurnaceInventory.java index ad45f761a0..34cef28646 100644 --- a/src/main/java/cn/nukkit/inventory/FurnaceInventory.java +++ b/src/main/java/cn/nukkit/inventory/FurnaceInventory.java @@ -43,8 +43,8 @@ public boolean setSmelting(Item item) { } @Override - public void onSlotChange(int index, Item before) { - super.onSlotChange(index, before); + public void onSlotChange(int index, Item before, boolean send) { + super.onSlotChange(index, before, send); this.getHolder().scheduleUpdate(); } diff --git a/src/main/java/cn/nukkit/inventory/Inventory.java b/src/main/java/cn/nukkit/inventory/Inventory.java index 8074ffc57a..ba7e4cdf6e 100644 --- a/src/main/java/cn/nukkit/inventory/Inventory.java +++ b/src/main/java/cn/nukkit/inventory/Inventory.java @@ -27,7 +27,11 @@ public interface Inventory { Item getItem(int index); - boolean setItem(int index, Item item); + default boolean setItem(int index, Item item) { + return setItem(index, item, true); + } + + boolean setItem(int index, Item item, boolean send); Item[] addItem(Item... slots); @@ -41,13 +45,13 @@ public interface Inventory { void sendContents(Player player); - void sendContents(Player[] players); + void sendContents(Player... players); void sendContents(Collection players); void sendSlot(int index, Player player); - void sendSlot(int index, Player[] players); + void sendSlot(int index, Player... players); void sendSlot(int index, Collection players); @@ -55,13 +59,21 @@ public interface Inventory { Map all(Item item); - int first(Item item); + default int first(Item item) { + return first(item, false); + } + + int first(Item item, boolean exact); int firstEmpty(Item item); void remove(Item item); - boolean clear(int index); + default boolean clear(int index) { + return clear(index, true); + } + + boolean clear(int index, boolean send); void clearAll(); @@ -83,5 +95,5 @@ public interface Inventory { void onClose(Player who); - void onSlotChange(int index, Item before); + void onSlotChange(int index, Item before, boolean send); } diff --git a/src/main/java/cn/nukkit/inventory/InventoryType.java b/src/main/java/cn/nukkit/inventory/InventoryType.java index bac09d0e96..f6ac77f11c 100644 --- a/src/main/java/cn/nukkit/inventory/InventoryType.java +++ b/src/main/java/cn/nukkit/inventory/InventoryType.java @@ -8,16 +8,17 @@ public enum InventoryType { CHEST(27, "Chest", 0), ENDER_CHEST(27, "Ender Chest", 0), DOUBLE_CHEST(27 + 27, "Double Chest", 0), - PLAYER(40, "Player", 0), //36 CONTAINER, 4 ARMOR + PLAYER(40, "Player", -1), //36 CONTAINER, 4 ARMOR FURNACE(3, "Furnace", 2), CRAFTING(5, "Crafting", 1), //4 CRAFTING slots, 1 RESULT WORKBENCH(10, "Crafting", 1), //9 CRAFTING slots, 1 RESULT - BREWING_STAND(4, "Brewing", 4), //1 INPUT, 3 POTION + 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 DROPPER(9, "Dropper", 7), //9 CONTAINER - HOPPER(5, "Hopper", 8); //5 CONTAINER + HOPPER(5, "Hopper", 8), //5 CONTAINER + CURSOR(1, "Cursor", -1); private final int size; private final String title; diff --git a/src/main/java/cn/nukkit/inventory/PlayerCursorInventory.java b/src/main/java/cn/nukkit/inventory/PlayerCursorInventory.java new file mode 100644 index 0000000000..5d94813115 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/PlayerCursorInventory.java @@ -0,0 +1,58 @@ +package cn.nukkit.inventory; + +import cn.nukkit.Player; +import cn.nukkit.network.protocol.InventorySlotPacket; +import cn.nukkit.network.protocol.types.ContainerIds; + +/** + * @author CreeperFace + */ +public class PlayerCursorInventory extends BaseInventory { + + public PlayerCursorInventory(Player holder) { + super(holder, InventoryType.CURSOR); + } + + public String getName() { + return "Cursor"; + } + + public int getSize() { + return 1; + } + + public void setSize(int size) { + throw new RuntimeException("Cursor can only carry one item at a time"); + } + + public void sendSlot(int index, Player... target) { + InventorySlotPacket pk = new InventorySlotPacket(); + pk.slot = index; + pk.item = this.getItem(index); + + for (Player p : target) { + if (p == this.getHolder()) { + pk.inventoryId = ContainerIds.CURSOR; + p.dataPacket(pk); + } else { + int id; + + if ((id = p.getWindowId(this)) == ContainerIds.NONE) { + this.close(p); + continue; + } + pk.inventoryId = id; + p.dataPacket(pk); + } + } + } + + /** + * This override is here for documentation and code completion purposes only. + * + * @return Player + */ + public Player getHolder() { + return (Player) this.holder; + } +} diff --git a/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java b/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java old mode 100644 new mode 100755 index 64472789f1..d72fa0d603 --- a/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java +++ b/src/main/java/cn/nukkit/inventory/PlayerEnderChestInventory.java @@ -8,6 +8,7 @@ import cn.nukkit.network.protocol.BlockEventPacket; import cn.nukkit.network.protocol.ContainerClosePacket; import cn.nukkit.network.protocol.ContainerOpenPacket; +import cn.nukkit.network.protocol.LevelSoundEventPacket; public class PlayerEnderChestInventory extends BaseInventory { @@ -27,8 +28,8 @@ public void onOpen(Player who) { } super.onOpen(who); ContainerOpenPacket containerOpenPacket = new ContainerOpenPacket(); - containerOpenPacket.windowid = (byte) who.getWindowId(this); - containerOpenPacket.type = (byte) this.getType().getNetworkType(); + containerOpenPacket.windowId = who.getWindowId(this); + containerOpenPacket.type = this.getType().getNetworkType(); BlockEnderChest chest = who.getViewingEnderChest(); if (chest != null) { containerOpenPacket.x = (int) chest.getX(); @@ -52,6 +53,7 @@ public void onOpen(Player who) { Level level = this.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_OPEN, 1, -1, this.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.getHolder().getX() >> 4, (int) this.getHolder().getZ() >> 4, blockEventPacket); } } @@ -60,7 +62,7 @@ public void onOpen(Player who) { @Override public void onClose(Player who) { ContainerClosePacket containerClosePacket = new ContainerClosePacket(); - containerClosePacket.windowid = (byte) who.getWindowId(this); + containerClosePacket.windowId = who.getWindowId(this); who.dataPacket(containerClosePacket); super.onClose(who); @@ -75,6 +77,7 @@ public void onClose(Player who) { Level level = this.getHolder().getLevel(); if (level != null) { + level.addLevelSoundEvent(LevelSoundEventPacket.SOUND_CHEST_CLOSED, 1, -1, this.getHolder().add(0.5, 0.5, 0.5)); level.addChunkPacket((int) this.getHolder().getX() >> 4, (int) this.getHolder().getZ() >> 4, blockEventPacket); } diff --git a/src/main/java/cn/nukkit/inventory/PlayerInventory.java b/src/main/java/cn/nukkit/inventory/PlayerInventory.java index 4065f7c76f..dde668532b 100644 --- a/src/main/java/cn/nukkit/inventory/PlayerInventory.java +++ b/src/main/java/cn/nukkit/inventory/PlayerInventory.java @@ -10,10 +10,11 @@ import cn.nukkit.event.player.PlayerItemHeldEvent; import cn.nukkit.item.Item; import cn.nukkit.item.ItemBlock; -import cn.nukkit.network.protocol.ContainerSetContentPacket; -import cn.nukkit.network.protocol.ContainerSetSlotPacket; +import cn.nukkit.network.protocol.InventoryContentPacket; +import cn.nukkit.network.protocol.InventorySlotPacket; import cn.nukkit.network.protocol.MobArmorEquipmentPacket; import cn.nukkit.network.protocol.MobEquipmentPacket; +import cn.nukkit.network.protocol.types.ContainerIds; import java.util.Collection; @@ -24,15 +25,16 @@ public class PlayerInventory extends BaseInventory { protected int itemInHandIndex = 0; - - protected final int[] hotbar; + private int[] hotbar; public PlayerInventory(EntityHumanType player) { super(player, InventoryType.PLAYER); this.hotbar = new int[this.getHotbarSize()]; + for (int i = 0; i < this.hotbar.length; i++) { this.hotbar[i] = i; } + } @Override @@ -46,30 +48,69 @@ public void setSize(int size) { this.sendContents(this.getViewers()); } + /** + * Called when a client equips a hotbar inventorySlot. This method should not be used by plugins. + * This method will call PlayerItemHeldEvent. + * + * @param slot hotbar slot Number of the hotbar slot to equip. + * @return boolean if the equipment change was successful, false if not. + */ + public boolean equipItem(int slot) { + if (!isHotbarSlot(slot)) { + this.sendContents((Player) this.getHolder()); + return false; + } + + if (this.getHolder() instanceof Player) { + PlayerItemHeldEvent ev = new PlayerItemHeldEvent((Player) this.getHolder(), this.getItem(slot), slot); + this.getHolder().getLevel().getServer().getPluginManager().callEvent(ev); + + if (ev.isCancelled()) { + this.sendContents(this.getViewers()); + return false; + } + } + + this.setHeldItemIndex(slot, false); + return true; + } + + private boolean isHotbarSlot(int slot) { + return slot >= 0 && slot <= this.getHotbarSize(); + } + + @Deprecated public int getHotbarSlotIndex(int index) { - return (index >= 0 && index < this.getHotbarSize()) ? this.hotbar[index] : -1; + return index; } + @Deprecated public void setHotbarSlotIndex(int index, int slot) { - if (index >= 0 && index < this.getHotbarSize() && slot >= -1 && slot < this.getSize()) { - this.hotbar[index] = slot; - } + } public int getHeldItemIndex() { - return itemInHandIndex; + return this.itemInHandIndex; } public void setHeldItemIndex(int index) { + setHeldItemIndex(index, true); + } + + public void setHeldItemIndex(int index, boolean send) { if (index >= 0 && index < this.getHotbarSize()) { this.itemInHandIndex = index; + if (this.getHolder() instanceof Player && send) { + this.sendHeldItem((Player) this.getHolder()); + } + this.sendHeldItem(this.getHolder().getViewers().values()); } } public Item getItemInHand() { - Item item = this.getItem(this.getHeldItemSlot()); + Item item = this.getItem(this.getHeldItemIndex()); if (item != null) { return item; } else { @@ -78,60 +119,40 @@ public Item getItemInHand() { } public boolean setItemInHand(Item item) { - return this.setItem(this.getHeldItemSlot(), item); + return this.setItem(this.getHeldItemIndex(), item); } + @Deprecated public int getHeldItemSlot() { - return this.getHotbarSlotIndex(this.itemInHandIndex); + return this.itemInHandIndex; } public void setHeldItemSlot(int slot) { - if (slot >= -1 && slot < this.getSize()) { - Item item = this.getItem(slot); - - int itemIndex = this.getHeldItemIndex(); - - if (this.getHolder() instanceof Player) { - PlayerItemHeldEvent ev = new PlayerItemHeldEvent((Player) this.getHolder(), item, slot, itemIndex); - Server.getInstance().getPluginManager().callEvent(ev); - if (ev.isCancelled()) { - this.sendContents((Player) this.getHolder()); - return; - } - } - - this.setHotbarSlotIndex(itemIndex, slot); + if (!isHotbarSlot(slot)) { + return; } - } - - public void sendHeldItem(Player player) { - Item item = this.getItemInHand(); - MobEquipmentPacket pk = new MobEquipmentPacket(); - pk.eid = this.getHolder().getId(); - pk.item = item; - pk.slot = (byte) this.getHeldItemSlot(); - pk.selectedSlot = (byte) this.getHeldItemIndex(); + this.itemInHandIndex = slot; - player.dataPacket(pk); - if (player.equals(this.getHolder())) { - this.sendSlot(this.getHeldItemSlot(), player); + if (this.getHolder() instanceof Player) { + this.sendHeldItem((Player) this.getHolder()); } + + this.sendHeldItem(this.getViewers()); } - public void sendHeldItem(Player[] players) { + public void sendHeldItem(Player... players) { Item item = this.getItemInHand(); MobEquipmentPacket pk = new MobEquipmentPacket(); pk.item = item; - pk.slot = (byte) this.getHeldItemSlot(); - pk.selectedSlot = (byte) this.getHeldItemIndex(); + pk.inventorySlot = pk.hotbarSlot = this.getHeldItemIndex(); for (Player player : players) { pk.eid = this.getHolder().getId(); if (player.equals(this.getHolder())) { pk.eid = player.getId(); - this.sendSlot(this.getHeldItemSlot(), player); + this.sendSlot(this.getHeldItemIndex(), player); } player.dataPacket(pk); @@ -143,17 +164,17 @@ public void sendHeldItem(Collection players) { } @Override - public void onSlotChange(int index, Item before) { + public void onSlotChange(int index, Item before, boolean send) { EntityHuman holder = this.getHolder(); if (holder instanceof Player && !((Player) holder).spawned) { return; } - super.onSlotChange(index, before); - if (index >= this.getSize()) { this.sendArmorSlot(index, this.getViewers()); this.sendArmorSlot(index, this.getHolder().getViewers().values()); + } else { + super.onSlotChange(index, before, send); } } @@ -207,10 +228,10 @@ public boolean setBoots(Item boots) { @Override public boolean setItem(int index, Item item) { - return setItem(index, item, false); + return setItem(index, item, true, false); } - private boolean setItem(int index, Item item, boolean ignoreArmorEvents) { + private boolean setItem(int index, Item item, boolean send, boolean ignoreArmorEvents) { if (index < 0 || index >= this.size) { return false; } else if (item.getId() == 0 || item.getCount() <= 0) { @@ -235,16 +256,14 @@ private boolean setItem(int index, Item item, boolean ignoreArmorEvents) { } item = ev.getNewItem(); } - Item old = this.getItem(index); this.slots.put(index, item.clone()); - this.onSlotChange(index, old); - + this.onSlotChange(index, old, send); return true; } @Override - public boolean clear(int index) { + public boolean clear(int index, boolean send) { if (this.slots.containsKey(index)) { Item item = new ItemBlock(new BlockAir(), null, 0); Item old = this.slots.get(index); @@ -280,7 +299,7 @@ public boolean clear(int index) { this.slots.remove(index); } - this.onSlotChange(index, old); + this.onSlotChange(index, old, send); } return true; @@ -318,9 +337,8 @@ public void sendArmorContents(Player[] players) { for (Player player : players) { if (player.equals(this.getHolder())) { - ContainerSetContentPacket pk2 = new ContainerSetContentPacket(); - pk2.windowid = ContainerSetContentPacket.SPECIAL_ARMOR; - pk2.eid = player.getId(); + InventoryContentPacket pk2 = new InventoryContentPacket(); + pk2.inventoryId = InventoryContentPacket.SPECIAL_ARMOR; pk2.slots = armor; player.dataPacket(pk2); } else { @@ -344,7 +362,7 @@ public void setArmorContents(Item[] items) { if (items[i].getId() == Item.AIR) { this.clear(this.getSize() + i); } else { - this.setItem(this.getSize() + 1, items[i]); + this.setItem(this.getSize() + i, items[i]); } } } @@ -368,8 +386,8 @@ public void sendArmorSlot(int index, Player[] players) { for (Player player : players) { if (player.equals(this.getHolder())) { - ContainerSetSlotPacket pk2 = new ContainerSetSlotPacket(); - pk2.windowid = ContainerSetContentPacket.SPECIAL_ARMOR; + InventorySlotPacket pk2 = new InventorySlotPacket(); + pk2.inventoryId = InventoryContentPacket.SPECIAL_ARMOR; pk2.slot = index - this.getSize(); pk2.item = this.getItem(index); player.dataPacket(pk2); @@ -395,33 +413,28 @@ public void sendContents(Collection players) { @Override public void sendContents(Player[] players) { - ContainerSetContentPacket pk = new ContainerSetContentPacket(); - pk.slots = new Item[this.getSize() + + this.getHotbarSize()]; + InventoryContentPacket pk = new InventoryContentPacket(); + pk.slots = new Item[this.getSize()]; for (int i = 0; i < this.getSize(); ++i) { pk.slots[i] = this.getItem(i); } - //Because PE is stupid and shows 9 less slots than you send it, give it 9 dummy slots so it shows all the REAL slots. + /*//Because PE is stupid and shows 9 less slots than you send it, give it 9 dummy slots so it shows all the REAL slots. for(int i = this.getSize(); i < this.getSize() + this.getHotbarSize(); ++i){ - pk.slots[i] = Item.get(Item.AIR, 0, 0); + pk.slots[i] = new ItemBlock(new BlockAir()); } + pk.slots[i] = new ItemBlock(new BlockAir()); + }*/ for (Player player : players) { - if (player.equals(this.getHolder())) { - pk.hotbar = new int[this.getHotbarSize()]; - for (int i = 0; i < this.getHotbarSize(); ++i) { - int index = this.getHotbarSlotIndex(i); - pk.hotbar[i] = index <= -1 ? -1 : index + 9; - } - } int id = player.getWindowId(this); if (id == -1 || !player.spawned) { this.close(player); continue; } - pk.eid = player.getId(); - pk.windowid = (byte) id; + pk.inventoryId = id; player.dataPacket(pk.clone()); + } } @@ -436,14 +449,14 @@ public void sendSlot(int index, Collection players) { } @Override - public void sendSlot(int index, Player[] players) { - ContainerSetSlotPacket pk = new ContainerSetSlotPacket(); + public void sendSlot(int index, Player... players) { + InventorySlotPacket pk = new InventorySlotPacket(); pk.slot = index; pk.item = this.getItem(index).clone(); for (Player player : players) { if (player.equals(this.getHolder())) { - pk.windowid = 0; + pk.inventoryId = ContainerIds.INVENTORY; player.dataPacket(pk); } else { int id = player.getWindowId(this); @@ -451,12 +464,28 @@ public void sendSlot(int index, Player[] players) { this.close(player); continue; } - pk.windowid = (byte) id; + pk.inventoryId = id; player.dataPacket(pk.clone()); } } } + public void sendCreativeContents() { + if (!(this.getHolder() instanceof Player)) { + return; + } + Player p = (Player) this.getHolder(); + + InventoryContentPacket pk = new InventoryContentPacket(); + pk.inventoryId = ContainerIds.CREATIVE; + + if (!p.isSpectator()) { //fill it for all gamemodes except spectator + pk.slots = Item.getCreativeItems().stream().toArray(Item[]::new); + } + + p.dataPacket(pk); + } + @Override public EntityHuman getHolder() { return (EntityHuman) super.getHolder(); diff --git a/src/main/java/cn/nukkit/inventory/Recipe.java b/src/main/java/cn/nukkit/inventory/Recipe.java index 5fcaa90a11..a5c0a42973 100644 --- a/src/main/java/cn/nukkit/inventory/Recipe.java +++ b/src/main/java/cn/nukkit/inventory/Recipe.java @@ -17,4 +17,8 @@ public interface Recipe { UUID getId(); void setId(UUID id); + + default boolean requiresCraftingTable() { + return false; + } } diff --git a/src/main/java/cn/nukkit/inventory/ShapedRecipe.java b/src/main/java/cn/nukkit/inventory/ShapedRecipe.java index 3ddce3cd15..f479015760 100644 --- a/src/main/java/cn/nukkit/inventory/ShapedRecipe.java +++ b/src/main/java/cn/nukkit/inventory/ShapedRecipe.java @@ -3,7 +3,10 @@ import cn.nukkit.Server; import cn.nukkit.item.Item; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; /** * author: MagicDroidX @@ -21,40 +24,19 @@ public class ShapedRecipe implements Recipe { private final Map> shapeItems = new HashMap<>(); - public ShapedRecipe(Item result, String... shape) { - if (shape.length == 0) { - throw new IllegalArgumentException("Must provide a shape"); - } - - if (shape.length > 3) { - throw new IllegalStateException("Crafting recipes should be 1, 2, 3 rows, not " + shape.length); - } - - - for (int y = 0; y < shape.length; y++) { - String row = shape[y]; - if (row.length() == 0 || row.length() > 3) { - throw new IllegalStateException("Crafting rows should be 1, 2, 3 characters, not " + row.length()); + public ShapedRecipe(Item result, int height, int width) { + for (int y = 0; y < height; y++) { + if (width == 0 || width > 3) { + throw new IllegalStateException("Crafting rows should be 1, 2, 3 characters, not " + width); } this.ingredients.put(y, new HashMap() { { - for (int i = 0; i < row.length(); i++) { + for (int i = 0; i < width; i++) { put(i, null); } } }); - - int len = row.length(); - for (int i = 0; i < len; i++) { - this.shapes.put(row.charAt(i), null); - - if (!this.shapeItems.containsKey(row.charAt(i))) { - this.shapeItems.put(row.charAt(i), new ArrayList<>(Arrays.asList(new Entry[]{new Entry(i, y)}))); - } else { - this.shapeItems.get(row.charAt(i)).add(new Entry(i, y)); - } - } } this.output = result.clone(); @@ -152,6 +134,11 @@ public void registerToCraftingManager() { Server.getInstance().getCraftingManager().registerShapedRecipe(this); } + @Override + public boolean requiresCraftingTable() { + return this.getHeight() > 2 || this.getWidth() > 2; + } + public static class Entry { public final int x; public final int y; diff --git a/src/main/java/cn/nukkit/inventory/ShapelessRecipe.java b/src/main/java/cn/nukkit/inventory/ShapelessRecipe.java index cb813a04aa..8892a80ecd 100644 --- a/src/main/java/cn/nukkit/inventory/ShapelessRecipe.java +++ b/src/main/java/cn/nukkit/inventory/ShapelessRecipe.java @@ -94,4 +94,9 @@ public int getIngredientCount() { public void registerToCraftingManager() { Server.getInstance().getCraftingManager().registerShapelessRecipe(this); } + + @Override + public boolean requiresCraftingTable() { + return this.ingredients.size() > 4; + } } diff --git a/src/main/java/cn/nukkit/inventory/SimpleTransactionGroup.java b/src/main/java/cn/nukkit/inventory/SimpleTransactionGroup.java deleted file mode 100644 index fb3f78100b..0000000000 --- a/src/main/java/cn/nukkit/inventory/SimpleTransactionGroup.java +++ /dev/null @@ -1,155 +0,0 @@ -package cn.nukkit.inventory; - -import cn.nukkit.Player; -import cn.nukkit.Server; -import cn.nukkit.event.inventory.InventoryTransactionEvent; -import cn.nukkit.item.Item; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public class SimpleTransactionGroup implements TransactionGroup { - - private final long creationTime; - protected boolean hasExecuted = false; - - protected Player source = null; - - protected final Set inventories = new HashSet<>(); - - protected final Set transactions = new HashSet<>(); - - public SimpleTransactionGroup() { - this(null); - } - - public SimpleTransactionGroup(Player source) { - this.creationTime = System.currentTimeMillis(); - this.source = source; - } - - public Player getSource() { - return source; - } - - @Override - public long getCreationTime() { - return creationTime; - } - - @Override - public Set getInventories() { - return inventories; - } - - @Override - public Set getTransactions() { - return transactions; - } - - @Override - public void addTransaction(Transaction transaction) { - if (this.transactions.contains(transaction)) { - return; - } - - for (Transaction tx : new HashSet<>(this.transactions)) { - if (tx.getInventory().equals(transaction.getInventory()) && tx.getSlot() == transaction.getSlot()) { - if (transaction.getCreationTime() >= tx.getCreationTime()) { - this.transactions.remove(tx); - } else { - return; - } - } - } - - this.transactions.add(transaction); - this.inventories.add(transaction.getInventory()); - } - - protected boolean matchItems(List needItems, List haveItems) { - for (Transaction ts : this.transactions) { - if (ts.getTargetItem().getId() != Item.AIR) { - needItems.add(ts.getTargetItem()); - } - Item checkSourceItem = ts.getInventory().getItem(ts.getSlot()); - Item sourceItem = ts.getSourceItem(); - if (!checkSourceItem.deepEquals(sourceItem) || sourceItem.getCount() != checkSourceItem.getCount()) { - return false; - } - if (sourceItem.getId() != Item.AIR) { - haveItems.add(sourceItem); - } - } - - for (Item needItem : new ArrayList<>(needItems)) { - for (Item haveItem : new ArrayList<>(haveItems)) { - if (needItem.deepEquals(haveItem)) { - int amount = Math.min(haveItem.getCount(), needItem.getCount()); - needItem.setCount(needItem.getCount() - amount); - haveItem.setCount(haveItem.getCount() - amount); - if (haveItem.getCount() == 0) { - haveItems.remove(haveItem); - } - if (needItem.getCount() == 0) { - needItems.remove(needItem); - break; - } - } - } - } - - return true; - } - - @Override - public boolean canExecute() { - List haveItems = new ArrayList<>(); - List needItems = new ArrayList<>(); - - return this.matchItems(needItems, haveItems) && haveItems.isEmpty() && needItems.isEmpty() && !this.transactions.isEmpty(); - } - - @Override - public boolean execute() { - return execute(false); - } - - @Override - public boolean execute(boolean force) { - if (this.hasExecuted || (!force && !this.canExecute())) { - return false; - } - - InventoryTransactionEvent ev = new InventoryTransactionEvent(this); - Server.getInstance().getPluginManager().callEvent(ev); - if (ev.isCancelled()) { - for (Inventory inventory : this.inventories) { - if (inventory instanceof PlayerInventory) { - ((PlayerInventory) inventory).sendArmorContents(this.getSource()); - } - inventory.sendContents(this.getSource()); - } - return false; - } - - for (Transaction transaction : this.transactions) { - transaction.getInventory().setItem(transaction.getSlot(), transaction.getTargetItem()); - } - - this.hasExecuted = true; - - return true; - } - - @Override - public boolean hasExecuted() { - return this.hasExecuted; - } -} diff --git a/src/main/java/cn/nukkit/inventory/Transaction.java b/src/main/java/cn/nukkit/inventory/Transaction.java deleted file mode 100644 index 5d20235803..0000000000 --- a/src/main/java/cn/nukkit/inventory/Transaction.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.nukkit.inventory; - -import cn.nukkit.item.Item; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public interface Transaction { - - Inventory getInventory(); - - int getSlot(); - - Item getSourceItem(); - - Item getTargetItem(); - - long getCreationTime(); -} diff --git a/src/main/java/cn/nukkit/inventory/TransactionGroup.java b/src/main/java/cn/nukkit/inventory/TransactionGroup.java deleted file mode 100644 index 5e71cf53a2..0000000000 --- a/src/main/java/cn/nukkit/inventory/TransactionGroup.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.nukkit.inventory; - -import java.util.Set; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public interface TransactionGroup { - - long getCreationTime(); - - Set getTransactions(); - - Set getInventories(); - - void addTransaction(Transaction transaction); - - boolean canExecute(); - - boolean execute(); - - boolean execute(boolean force); - - boolean hasExecuted(); -} diff --git a/src/main/java/cn/nukkit/inventory/transaction/InventoryTransaction.java b/src/main/java/cn/nukkit/inventory/transaction/InventoryTransaction.java new file mode 100644 index 0000000000..4496afc5b3 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/InventoryTransaction.java @@ -0,0 +1,26 @@ +package cn.nukkit.inventory.transaction; + +import cn.nukkit.inventory.Inventory; +import cn.nukkit.inventory.transaction.action.InventoryAction; + +import java.util.Set; + +/** + * @author CreeperFace + */ +public interface InventoryTransaction { + + long getCreationTime(); + + Set getActions(); + + Set getInventories(); + + void addAction(InventoryAction action); + + boolean canExecute(); + + boolean execute(); + + boolean hasExecuted(); +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/SimpleInventoryTransaction.java b/src/main/java/cn/nukkit/inventory/transaction/SimpleInventoryTransaction.java new file mode 100644 index 0000000000..48df171900 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/SimpleInventoryTransaction.java @@ -0,0 +1,252 @@ +package cn.nukkit.inventory.transaction; + +import cn.nukkit.Player; +import cn.nukkit.Server; +import cn.nukkit.event.inventory.InventoryTransactionEvent; +import cn.nukkit.inventory.Inventory; +import cn.nukkit.inventory.transaction.action.InventoryAction; +import cn.nukkit.inventory.transaction.action.SlotChangeAction; +import cn.nukkit.item.Item; +import cn.nukkit.utils.MainLogger; + +import java.util.*; +import java.util.Map.Entry; + +/** + * @author CreeperFace + */ +public class SimpleInventoryTransaction implements InventoryTransaction { + + private long creationTime; + protected boolean hasExecuted; + + protected Player source; + + protected Set inventories = new HashSet<>(); + + protected Set actions = new HashSet<>(); + + public SimpleInventoryTransaction(Player source, List actions) { + creationTime = System.currentTimeMillis(); + this.source = source; + + for (InventoryAction action : actions) { + this.addAction(action); + } + } + + public Player getSource() { + return source; + } + + @Override + public long getCreationTime() { + return creationTime; + } + + @Override + public Set getInventories() { + return inventories; + } + + @Override + public Set getActions() { + return actions; + } + + public void addAction(InventoryAction action) { + if (this.actions.contains(action)) { + return; + } + + if (action instanceof SlotChangeAction) { + this.inventories.add(((SlotChangeAction) action).getInventory()); + } + + this.actions.add(action); + } + + protected boolean matchItems(List needItems, List haveItems) { + for (InventoryAction action : this.actions) { + if (action.getTargetItem().getId() != Item.AIR) { + needItems.add(action.getTargetItem()); + } + + if (!action.isValid(this.source)) { + return false; + } + + if (action.getSourceItem().getId() != Item.AIR) { + haveItems.add(action.getSourceItem()); + } + } + + for (Item needItem : new ArrayList<>(needItems)) { + for (Item haveItem : new ArrayList<>(haveItems)) { + if (needItem.equals(haveItem)) { + int amount = Math.min(haveItem.getCount(), needItem.getCount()); + needItem.setCount(needItem.getCount() - amount); + haveItem.setCount(haveItem.getCount() - amount); + if (haveItem.getCount() == 0) { + haveItems.remove(haveItem); + } + if (needItem.getCount() == 0) { + needItems.remove(needItem); + break; + } + } + } + } + + return haveItems.isEmpty() && needItems.isEmpty(); + } + + /** + * Iterates over SlotChangeActions in this transaction and compacts any which refer to the same inventorySlot in the same + * inventory so they can be correctly handled. + *

+ * Under normal circumstances, the same inventorySlot would never be changed more than once in a single transaction. However, + * due to the way things like the crafting grid are "implemented" in MCPE 1.2 (a.k.a. hacked-in), we may get + * multiple inventorySlot changes referring to the same inventorySlot in a single transaction. These multiples are not even guaranteed + * to be in the correct order (inventorySlot splitting in the crafting grid for example, causes the actions to be sent in the + * wrong order), so this method also tries to chain them into order. + * + * @return bool + */ + protected boolean squashDuplicateSlotChanges() { + Map> slotChanges = new HashMap<>(); + + for (InventoryAction action : this.actions) { + if (action instanceof SlotChangeAction) { + int hash = Objects.hash(((SlotChangeAction) action).getInventory(), ((SlotChangeAction) action).getSlot()); + + List list = slotChanges.get(hash); + if (list == null) { + list = new ArrayList<>(); + } + + list.add((SlotChangeAction) action); + + slotChanges.put(hash, list); + } + } + + for (Entry> entry : new ArrayList<>(slotChanges.entrySet())) { + int hash = entry.getKey(); + List list = entry.getValue(); + + if (list.size() == 1) { //No need to compact inventorySlot changes if there is only one on this inventorySlot + slotChanges.remove(hash); + continue; + } + + List originalList = new ArrayList<>(list); + + SlotChangeAction originalAction = null; + Item lastTargetItem = null; + + for (int i = 0; i < list.size(); i++) { + SlotChangeAction action = list.get(i); + + if (action.isValid(this.source)) { + originalAction = action; + lastTargetItem = action.getTargetItem(); + list.remove(i); + break; + } + } + + if (originalAction == null) { + return false; //Couldn't find any actions that had a source-item matching the current inventory inventorySlot + } + + int sortedThisLoop; + + do { + sortedThisLoop = 0; + for (int i = 0; i < list.size(); i++) { + SlotChangeAction action = list.get(i); + + Item actionSource = action.getSourceItem(); + if (actionSource.equalsExact(lastTargetItem)) { + lastTargetItem = action.getTargetItem(); + list.remove(i); + sortedThisLoop++; + } + else if (actionSource.equals(lastTargetItem)) { + lastTargetItem.count -= actionSource.count; + list.remove(i); + if (lastTargetItem.count == 0) sortedThisLoop++; + } + } + } while (sortedThisLoop > 0); + + if (list.size() > 0) { //couldn't chain all the actions together + MainLogger.getLogger().debug("Failed to compact " + originalList.size() + " actions for " + this.source.getName()); + return false; + } + + for (SlotChangeAction action : originalList) { + this.actions.remove(action); + } + + this.addAction(new SlotChangeAction(originalAction.getInventory(), originalAction.getSlot(), originalAction.getSourceItem(), lastTargetItem)); + + MainLogger.getLogger().debug("Successfully compacted " + originalList.size() + " actions for " + this.source.getName()); + } + + return true; + } + + public boolean canExecute() { + this.squashDuplicateSlotChanges(); + + List haveItems = new ArrayList<>(); + List needItems = new ArrayList<>(); + return matchItems(needItems, haveItems) && this.actions.size() > 0 && haveItems.size() == 0 && needItems.size() == 0; + } + + /** + * @return bool + */ + public boolean execute() { + if (this.hasExecuted() || !this.canExecute()) { + return false; + } + + InventoryTransactionEvent ev = new InventoryTransactionEvent(this); + Server.getInstance().getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + this.handleFailed(); + return true; + } + + for (InventoryAction action : this.actions) { + if (!action.onPreExecute(this.source)) { + this.handleFailed(); + return true; + } + } + + for (InventoryAction action : this.actions) { + if (action.execute(this.source)) { + action.onExecuteSuccess(this.source); + } else { + action.onExecuteFail(this.source); + } + } + + this.hasExecuted = true; + return true; + } + + protected void handleFailed() { + for (InventoryAction action : this.actions) { + action.onExecuteFail(this.source); + } + } + + public boolean hasExecuted() { + return this.hasExecuted; + } +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/action/CreativeInventoryAction.java b/src/main/java/cn/nukkit/inventory/transaction/action/CreativeInventoryAction.java new file mode 100644 index 0000000000..eaa94eec75 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/action/CreativeInventoryAction.java @@ -0,0 +1,54 @@ +package cn.nukkit.inventory.transaction.action; + +import cn.nukkit.Player; +import cn.nukkit.item.Item; + +/** + * @author CreeperFace + */ +public class CreativeInventoryAction extends InventoryAction { + /** + * Player put an item into the creative window to destroy it. + */ + public static final int TYPE_DELETE_ITEM = 0; + /** + * Player took an item from the creative window. + */ + public static final int TYPE_CREATE_ITEM = 1; + + protected int actionType; + + public CreativeInventoryAction(Item source, Item target, int action) { + super(source, target); + } + + /** + * Checks that the player is in creative, and (if creating an item) that the item exists in the creative inventory. + */ + public boolean isValid(Player source) { + return source.isCreative() && + (this.actionType == TYPE_DELETE_ITEM || Item.getCreativeItemIndex(this.sourceItem) != -1); + } + + /** + * Returns the type of the action. + */ + public int getActionType() { + return actionType; + } + + /** + * No need to do anything extra here: this type just provides a place for items to disappear or appear from. + */ + public boolean execute(Player source) { + return true; + } + + public void onExecuteSuccess(Player source) { + + } + + public void onExecuteFail(Player source) { + + } +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/action/DropItemAction.java b/src/main/java/cn/nukkit/inventory/transaction/action/DropItemAction.java new file mode 100644 index 0000000000..b2cdea9ddf --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/action/DropItemAction.java @@ -0,0 +1,49 @@ +package cn.nukkit.inventory.transaction.action; + +import cn.nukkit.Player; +import cn.nukkit.event.player.PlayerDropItemEvent; +import cn.nukkit.item.Item; + +/** + * @author CreeperFace + */ +public class DropItemAction extends InventoryAction { + + public DropItemAction(Item source, Item target) { + super(source, target); + } + + /** + * Verifies that the source item of a drop-item action must be air. This is not strictly necessary, just a sanity + * check. + */ + public boolean isValid(Player source) { + return this.sourceItem.isNull(); + } + + @Override + public boolean onPreExecute(Player source) { + PlayerDropItemEvent ev; + source.getServer().getPluginManager().callEvent(ev = new PlayerDropItemEvent(source, this.targetItem)); + if (ev.isCancelled()) { + return false; + } + + return true; + } + + /** + * Drops the target item in front of the player. + */ + public boolean execute(Player source) { + return source.dropItem(this.targetItem); + } + + public void onExecuteSuccess(Player source) { + + } + + public void onExecuteFail(Player source) { + + } +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/action/InventoryAction.java b/src/main/java/cn/nukkit/inventory/transaction/action/InventoryAction.java new file mode 100644 index 0000000000..e853cbe085 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/action/InventoryAction.java @@ -0,0 +1,74 @@ +package cn.nukkit.inventory.transaction.action; + +import cn.nukkit.Player; +import cn.nukkit.item.Item; + +/** + * @author CreeperFace + */ +public abstract class InventoryAction { + + + private long creationTime; + + protected Item sourceItem; + + protected Item targetItem; + + public InventoryAction(Item sourceItem, Item targetItem) { + this.sourceItem = sourceItem; + this.targetItem = targetItem; + + this.creationTime = System.currentTimeMillis(); + } + + public long getCreationTime() { + return creationTime; + } + + /** + * Returns the item that was present before the action took place. + * + * @return Item + */ + public Item getSourceItem() { + return sourceItem.clone(); + } + + /** + * Returns the item that the action attempted to replace the source item with. + */ + public Item getTargetItem() { + return targetItem.clone(); + } + + /** + * Called by inventory transactions before any actions are processed. If this returns false, the transaction will + * be cancelled. + */ + public boolean onPreExecute(Player source) { + return true; + } + + /** + * Returns whether this action is currently valid. This should perform any necessary sanity checks. + */ + abstract public boolean isValid(Player source); + + /** + * Performs actions needed to complete the inventory-action server-side. Returns if it was successful. Will return + * false if plugins cancelled events. This will only be called if the transaction which it is part of is considered + * valid. + */ + abstract public boolean execute(Player source); + + /** + * Performs additional actions when this inventory-action completed successfully. + */ + abstract public void onExecuteSuccess(Player $source); + + /** + * Performs additional actions when this inventory-action did not complete successfully. + */ + abstract public void onExecuteFail(Player source); +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/action/SlotChangeAction.java b/src/main/java/cn/nukkit/inventory/transaction/action/SlotChangeAction.java new file mode 100644 index 0000000000..e1e77987eb --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/action/SlotChangeAction.java @@ -0,0 +1,70 @@ +package cn.nukkit.inventory.transaction.action; + +import cn.nukkit.Player; +import cn.nukkit.inventory.Inventory; +import cn.nukkit.item.Item; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author CreeperFace + */ +public class SlotChangeAction extends InventoryAction { + + protected Inventory inventory; + private int inventorySlot; + + public SlotChangeAction(Inventory inventory, int inventorySlot, Item sourceItem, Item targetItem) { + super(sourceItem, targetItem); + this.inventory = inventory; + this.inventorySlot = inventorySlot; + } + + /** + * Returns the inventory involved in this action. + */ + public Inventory getInventory() { + return this.inventory; + } + + /** + * Returns the inventorySlot in the inventory which this action modified. + */ + public int getSlot() { + return inventorySlot; + } + + /** + * Checks if the item in the inventory at the specified inventorySlot is the same as this action's source item. + */ + public boolean isValid(Player source) { + Item check = inventory.getItem(this.inventorySlot); + + return check.equalsExact(this.sourceItem); + } + + /** + * Sets the item into the target inventory. + */ + public boolean execute(Player source) { + return this.inventory.setItem(this.inventorySlot, this.targetItem, false); + } + + /** + * Sends inventorySlot changes to other viewers of the inventory. This will not send any change back to the source Player. + */ + public void onExecuteSuccess(Player source) { + Set viewers = new HashSet<>(this.inventory.getViewers()); + viewers.remove(source); + + this.inventory.sendSlot(this.inventorySlot, viewers); + } + + /** + * Sends the original inventorySlot contents to the source player to revert the action. + */ + public void onExecuteFail(Player source) { + this.inventory.sendSlot(this.inventorySlot, source); + } +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/data/ReleaseItemData.java b/src/main/java/cn/nukkit/inventory/transaction/data/ReleaseItemData.java new file mode 100644 index 0000000000..9ad8db1038 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/data/ReleaseItemData.java @@ -0,0 +1,15 @@ +package cn.nukkit.inventory.transaction.data; + +import cn.nukkit.item.Item; +import cn.nukkit.math.Vector3; + +/** + * @author CreeperFace + */ +public class ReleaseItemData implements TransactionData { + + public int actionType; + public int hotbarSlot; + public Item itemInHand; + public Vector3 headRot; +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/data/TransactionData.java b/src/main/java/cn/nukkit/inventory/transaction/data/TransactionData.java new file mode 100644 index 0000000000..f594453b90 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/data/TransactionData.java @@ -0,0 +1,7 @@ +package cn.nukkit.inventory.transaction.data; + +/** + * @author CreeperFace + */ +public interface TransactionData { +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/data/UseItemData.java b/src/main/java/cn/nukkit/inventory/transaction/data/UseItemData.java new file mode 100644 index 0000000000..00c5cae5e3 --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/data/UseItemData.java @@ -0,0 +1,22 @@ +package cn.nukkit.inventory.transaction.data; + +import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; +import cn.nukkit.math.BlockVector3; +import cn.nukkit.math.Vector3; +import cn.nukkit.math.Vector3f; + +/** + * @author CreeperFace + */ +public class UseItemData implements TransactionData { + + public int actionType; + public BlockVector3 blockPos; + public BlockFace face; + public int hotbarSlot; + public Item itemInHand; + public Vector3 playerPos; + public Vector3f clickPos; + +} diff --git a/src/main/java/cn/nukkit/inventory/transaction/data/UseItemOnEntityData.java b/src/main/java/cn/nukkit/inventory/transaction/data/UseItemOnEntityData.java new file mode 100644 index 0000000000..ca09c09d0d --- /dev/null +++ b/src/main/java/cn/nukkit/inventory/transaction/data/UseItemOnEntityData.java @@ -0,0 +1,18 @@ +package cn.nukkit.inventory.transaction.data; + +import cn.nukkit.item.Item; +import cn.nukkit.math.Vector3; + +/** + * @author CreeperFace + */ +public class UseItemOnEntityData implements TransactionData { + + public long entityRuntimeId; + public int actionType; + public int hotbarSlot; + public Item itemInHand; + public Vector3 vector1; + public Vector3 vector2; + +} diff --git a/src/main/java/cn/nukkit/item/Item.java b/src/main/java/cn/nukkit/item/Item.java index 2eab29b9c9..a1728c361d 100644 --- a/src/main/java/cn/nukkit/item/Item.java +++ b/src/main/java/cn/nukkit/item/Item.java @@ -1,27 +1,33 @@ package cn.nukkit.item; import cn.nukkit.Player; +import cn.nukkit.Server; import cn.nukkit.block.Block; import cn.nukkit.block.BlockAir; -import cn.nukkit.block.BlockFence; -import cn.nukkit.block.BlockFlower; import cn.nukkit.entity.Entity; import cn.nukkit.inventory.Fuel; import cn.nukkit.item.enchantment.Enchantment; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; +import cn.nukkit.math.Vector3; import cn.nukkit.nbt.NBTIO; import cn.nukkit.nbt.tag.CompoundTag; import cn.nukkit.nbt.tag.ListTag; import cn.nukkit.nbt.tag.StringTag; import cn.nukkit.nbt.tag.Tag; import cn.nukkit.utils.Binary; +import cn.nukkit.utils.Config; +import cn.nukkit.utils.MainLogger; +import cn.nukkit.utils.Utils; +import javax.xml.bind.DatatypeConverter; +import java.io.File; import java.io.IOException; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.regex.Pattern; /** @@ -30,22 +36,6 @@ */ public class Item implements Cloneable { - private static CompoundTag parseCompoundTag(byte[] tag) { - try { - return NBTIO.read(tag, ByteOrder.LITTLE_ENDIAN); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private byte[] writeCompoundTag(CompoundTag tag) { - try { - return NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - //All Block IDs are here too public static final int AIR = 0; public static final int STONE = 1; @@ -160,7 +150,7 @@ private byte[] writeCompoundTag(CompoundTag tag) { public static final int CLAY_BLOCK = 82; public static final int REEDS = 83; public static final int SUGARCANE_BLOCK = 83; - + public static final int JUKEBOX = 84; public static final int FENCE = 85; public static final int PUMPKIN = 86; public static final int NETHERRACK = 87; @@ -311,6 +301,11 @@ private byte[] writeCompoundTag(CompoundTag tag) { public static final int END_ROD = 208; public static final int END_GATEWAY = 209; + public static final int MAGMA = 213; + public static final int BLOCK_NETHER_WART_BLOCK = 214; + public static final int RED_NETHER_BRICK = 215; + public static final int BONE_BLOCK = 216; + public static final int SHULKER_BOX = 218; public static final int PURPLE_GLAZED_TERRACOTTA = 219; public static final int WHITE_GLAZED_TERRACOTTA = 220; @@ -490,7 +485,8 @@ private byte[] writeCompoundTag(CompoundTag tag) { public static final int SPAWN_EGG = 383; public static final int EXPERIENCE_BOTTLE = 384; public static final int FIRE_CHARGE = 385; - + public static final int BOOK_AND_QUILL = 386; + public static final int WRITTEN_BOOK = 387; public static final int EMERALD = 388; public static final int ITEM_FRAME = 389; public static final int FLOWER_POT = 390; @@ -562,6 +558,19 @@ private byte[] writeCompoundTag(CompoundTag tag) { public static final int GOLDEN_APPLE_ENCHANTED = 466; + public static final int RECORD_13 = 500; + public static final int RECORD_CAT = 501; + public static final int RECORD_BLOCKS = 502; + public static final int RECORD_CHIRP = 503; + public static final int RECORD_FAR = 504; + public static final int RECORD_MALL = 505; + public static final int RECORD_MELLOHI = 506; + public static final int RECORD_STAL = 507; + public static final int RECORD_STRAD = 508; + public static final int RECORD_WARD = 509; + public static final int RECORD_11 = 510; + public static final int RECORD_WAIT = 511; + public static Class[] list = null; protected Block block = null; @@ -588,7 +597,7 @@ public Item(int id, Integer meta, int count) { public Item(int id, Integer meta, int count, String name) { this.id = id & 0xffff; - if (meta != null) { + if (meta != null && meta >= 0) { this.meta = meta & 0xffff; } else { this.hasMeta = false; @@ -741,7 +750,8 @@ public static void init() { list[SPAWN_EGG] = ItemSpawnEgg.class; //383 list[EXPERIENCE_BOTTLE] = ItemExpBottle.class; //384 //TODO: list[FIRE_CHARGE] = ItemFireCharge.class; //385 - + //TODO: list[BOOK_AND_QUILL] = ItemBookAndQuill.class; //386 + list[WRITTEN_BOOK] = ItemBookWritten.class; //387 list[EMERALD] = ItemEmerald.class; //388 list[ITEM_FRAME] = ItemItemFrame.class; //389 list[FLOWER_POT] = ItemFlowerPot.class; //390 @@ -805,6 +815,18 @@ public static void init() { list[COOKED_SALMON] = ItemSalmonCooked.class; //463 list[GOLDEN_APPLE_ENCHANTED] = ItemAppleGoldEnchanted.class; //466 + /*list[RECORD_11] = ItemRecord11.class; + list[RECORD_CAT] = ItemRecordCat.class; + list[RECORD_13] = ItemRecord13.class; + list[RECORD_BLOCKS] = ItemRecordBlocks.class; + list[RECORD_CHIRP] = ItemRecordChirp.class; + list[RECORD_FAR] = ItemRecordFar.class; + list[RECORD_WARD] = ItemRecordWard.class; + list[RECORD_MALL] = ItemRecordMall.class; + list[RECORD_MELLOHI] = ItemRecordMellohi.class; + list[RECORD_STAL] = ItemRecordStal.class; + list[RECORD_STRAD] = ItemRecordStrad.class; + list[RECORD_WAIT] = ItemRecordWait.class;*/ for (int i = 0; i < 256; ++i) { if (Block.list[i] != null) { @@ -820,644 +842,31 @@ public static void init() { private static void initCreativeItems() { clearCreativeItems(); + Server server = Server.getInstance(); - //Building - addCreativeItem(Item.get(Item.COBBLESTONE, 0)); - addCreativeItem(Item.get(Item.STONE_BRICKS, 0)); - addCreativeItem(Item.get(Item.STONE_BRICKS, 1)); - addCreativeItem(Item.get(Item.STONE_BRICKS, 2)); - addCreativeItem(Item.get(Item.STONE_BRICKS, 3)); - addCreativeItem(Item.get(Item.MOSS_STONE, 0)); - addCreativeItem(Item.get(Item.WOODEN_PLANKS, 0)); - addCreativeItem(Item.get(Item.WOODEN_PLANKS, 1)); - addCreativeItem(Item.get(Item.WOODEN_PLANKS, 2)); - addCreativeItem(Item.get(Item.WOODEN_PLANKS, 3)); - addCreativeItem(Item.get(Item.WOODEN_PLANKS, 4)); - addCreativeItem(Item.get(Item.WOODEN_PLANKS, 5)); - addCreativeItem(Item.get(Item.BRICKS, 0)); - - addCreativeItem(Item.get(Item.STONE, 0)); - addCreativeItem(Item.get(Item.STONE, 1)); - addCreativeItem(Item.get(Item.STONE, 2)); - addCreativeItem(Item.get(Item.STONE, 3)); - addCreativeItem(Item.get(Item.STONE, 4)); - addCreativeItem(Item.get(Item.STONE, 5)); - addCreativeItem(Item.get(Item.STONE, 6)); - addCreativeItem(Item.get(Item.DIRT, 0)); - addCreativeItem(Item.get(Item.PODZOL, 0)); - addCreativeItem(Item.get(Item.GRASS, 0)); - addCreativeItem(Item.get(Item.MYCELIUM, 0)); - addCreativeItem(Item.get(Item.CLAY_BLOCK, 0)); - addCreativeItem(Item.get(Item.TERRACOTTA, 0)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 0)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 1)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 2)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 3)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 4)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 5)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 6)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 7)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 8)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 9)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 10)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 11)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 12)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 13)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 14)); - addCreativeItem(Item.get(Item.STAINED_TERRACOTTA, 15)); - addCreativeItem(Item.get(Item.SANDSTONE, 0)); - addCreativeItem(Item.get(Item.SANDSTONE, 1)); - addCreativeItem(Item.get(Item.SANDSTONE, 2)); - addCreativeItem(Item.get(Item.RED_SANDSTONE, 0)); - addCreativeItem(Item.get(Item.RED_SANDSTONE, 1)); - addCreativeItem(Item.get(Item.RED_SANDSTONE, 2)); - addCreativeItem(Item.get(Item.SAND, 0)); - addCreativeItem(Item.get(Item.SAND, 1)); - addCreativeItem(Item.get(Item.GRAVEL, 0)); - addCreativeItem(Item.get(Item.TRUNK, 0)); - addCreativeItem(Item.get(Item.TRUNK, 1)); - addCreativeItem(Item.get(Item.TRUNK, 2)); - addCreativeItem(Item.get(Item.TRUNK, 3)); - addCreativeItem(Item.get(Item.TRUNK2, 0)); - addCreativeItem(Item.get(Item.TRUNK2, 1)); - addCreativeItem(Item.get(Item.NETHER_BRICKS, 0)); - addCreativeItem(Item.get(Item.NETHERRACK, 0)); - addCreativeItem(Item.get(Item.SOUL_SAND, 0)); - addCreativeItem(Item.get(Item.BEDROCK, 0)); - addCreativeItem(Item.get(Item.COBBLESTONE_STAIRS, 0)); - addCreativeItem(Item.get(Item.OAK_WOODEN_STAIRS, 0)); - addCreativeItem(Item.get(Item.SPRUCE_WOODEN_STAIRS, 0)); - addCreativeItem(Item.get(Item.BIRCH_WOODEN_STAIRS, 0)); - addCreativeItem(Item.get(Item.JUNGLE_WOODEN_STAIRS, 0)); - addCreativeItem(Item.get(Item.ACACIA_WOODEN_STAIRS, 0)); - addCreativeItem(Item.get(Item.DARK_OAK_WOODEN_STAIRS, 0)); - addCreativeItem(Item.get(Item.BRICK_STAIRS, 0)); - addCreativeItem(Item.get(Item.SANDSTONE_STAIRS, 0)); - addCreativeItem(Item.get(Item.RED_SANDSTONE_STAIRS, 0)); - addCreativeItem(Item.get(Item.STONE_BRICK_STAIRS, 0)); - addCreativeItem(Item.get(Item.NETHER_BRICKS_STAIRS, 0)); - addCreativeItem(Item.get(Item.QUARTZ_STAIRS, 0)); - addCreativeItem(Item.get(Item.PURPUR_STAIRS, 0)); - addCreativeItem(Item.get(Item.SLAB, 0)); - addCreativeItem(Item.get(Item.SLAB, 3)); - addCreativeItem(Item.get(Item.WOODEN_SLAB, 0)); - addCreativeItem(Item.get(Item.WOODEN_SLAB, 1)); - addCreativeItem(Item.get(Item.WOODEN_SLAB, 2)); - addCreativeItem(Item.get(Item.WOODEN_SLAB, 3)); - addCreativeItem(Item.get(Item.WOODEN_SLAB, 4)); - addCreativeItem(Item.get(Item.WOODEN_SLAB, 5)); - addCreativeItem(Item.get(Item.SLAB, 4)); - addCreativeItem(Item.get(Item.SLAB, 1)); - addCreativeItem(Item.get(Item.RED_SANDSTONE_SLAB)); - addCreativeItem(Item.get(Item.SLAB, 5)); - addCreativeItem(Item.get(Item.SLAB, 7)); - addCreativeItem(Item.get(Item.SLAB, 6)); - addCreativeItem(Item.get(Item.RED_SANDSTONE_SLAB, 1)); - addCreativeItem(Item.get(Item.QUARTZ_BLOCK, 0)); - addCreativeItem(Item.get(Item.QUARTZ_BLOCK, 2)); - addCreativeItem(Item.get(Item.QUARTZ_BLOCK, 1)); - addCreativeItem(Item.get(Item.PRISMARINE, 0)); - addCreativeItem(Item.get(Item.PRISMARINE, 1)); - addCreativeItem(Item.get(Item.PRISMARINE, 2)); - addCreativeItem(Item.get(Item.PURPUR_BLOCK, 0)); - addCreativeItem(Item.get(Item.PURPUR_BLOCK, 2)); - addCreativeItem(Item.get(Item.COAL_ORE, 0)); - addCreativeItem(Item.get(Item.IRON_ORE, 0)); - addCreativeItem(Item.get(Item.GOLD_ORE, 0)); - addCreativeItem(Item.get(Item.DIAMOND_ORE, 0)); - addCreativeItem(Item.get(Item.LAPIS_ORE, 0)); - addCreativeItem(Item.get(Item.REDSTONE_ORE, 0)); - addCreativeItem(Item.get(Item.EMERALD_ORE, 0)); - addCreativeItem(Item.get(Item.QUARTZ_ORE, 0)); - addCreativeItem(Item.get(Item.OBSIDIAN, 0)); - addCreativeItem(Item.get(Item.ICE, 0)); - addCreativeItem(Item.get(Item.PACKED_ICE, 0)); - addCreativeItem(Item.get(Item.SNOW_BLOCK, 0)); - addCreativeItem(Item.get(Item.END_BRICKS, 0)); - addCreativeItem(Item.get(Item.END_STONE, 0)); - - //Decoration - addCreativeItem(Item.get(Item.BEACON, 0)); - addCreativeItem(Item.get(Item.COBBLESTONE_WALL, 0)); - addCreativeItem(Item.get(Item.COBBLESTONE_WALL, 1)); - addCreativeItem(Item.get(Item.WATER_LILY, 0)); - addCreativeItem(Item.get(Item.SEA_LANTERN, 0)); - addCreativeItem(Item.get(Item.CHORUS_PLANT, 0)); - addCreativeItem(Item.get(Item.CHORUS_FLOWER, 0)); - addCreativeItem(Item.get(Item.GOLD_BLOCK, 0)); - addCreativeItem(Item.get(Item.IRON_BLOCK, 0)); - addCreativeItem(Item.get(Item.DIAMOND_BLOCK, 0)); - addCreativeItem(Item.get(Item.LAPIS_BLOCK, 0)); - addCreativeItem(Item.get(Item.COAL_BLOCK, 0)); - addCreativeItem(Item.get(Item.EMERALD_BLOCK, 0)); - addCreativeItem(Item.get(Item.REDSTONE_BLOCK, 0)); - addCreativeItem(Item.get(Item.SNOW_LAYER, 0)); - addCreativeItem(Item.get(Item.GLASS, 0)); - addCreativeItem(Item.get(Item.GLOWSTONE_BLOCK, 0)); - addCreativeItem(Item.get(Item.VINES, 0)); - addCreativeItem(Item.get(Item.LADDER, 0)); - addCreativeItem(Item.get(Item.SPONGE, 0)); - addCreativeItem(Item.get(Item.SPONGE, 1)); - addCreativeItem(Item.get(Item.GLASS_PANE, 0)); - addCreativeItem(Item.get(Item.WOODEN_DOOR, 0)); - addCreativeItem(Item.get(Item.SPRUCE_DOOR, 0)); - addCreativeItem(Item.get(Item.BIRCH_DOOR, 0)); - addCreativeItem(Item.get(Item.JUNGLE_DOOR, 0)); - addCreativeItem(Item.get(Item.ACACIA_DOOR, 0)); - addCreativeItem(Item.get(Item.DARK_OAK_DOOR, 0)); - addCreativeItem(Item.get(Item.IRON_DOOR, 0)); - addCreativeItem(Item.get(Item.TRAPDOOR, 0)); - addCreativeItem(Item.get(Item.IRON_TRAPDOOR, 0)); - addCreativeItem(Item.get(Item.FENCE, BlockFence.FENCE_OAK)); - addCreativeItem(Item.get(Item.FENCE, BlockFence.FENCE_SPRUCE)); - addCreativeItem(Item.get(Item.FENCE, BlockFence.FENCE_BIRCH)); - addCreativeItem(Item.get(Item.FENCE, BlockFence.FENCE_JUNGLE)); - addCreativeItem(Item.get(Item.FENCE, BlockFence.FENCE_ACACIA)); - addCreativeItem(Item.get(Item.FENCE, BlockFence.FENCE_DARK_OAK)); - addCreativeItem(Item.get(Item.NETHER_BRICK_FENCE, 0)); - addCreativeItem(Item.get(Item.FENCE_GATE, 0)); - addCreativeItem(Item.get(Item.FENCE_GATE_SPRUCE, 0)); - addCreativeItem(Item.get(Item.FENCE_GATE_BIRCH, 0)); - addCreativeItem(Item.get(Item.FENCE_GATE_JUNGLE, 0)); - addCreativeItem(Item.get(Item.FENCE_GATE_ACACIA, 0)); - addCreativeItem(Item.get(Item.FENCE_GATE_DARK_OAK, 0)); - addCreativeItem(Item.get(Item.IRON_BARS, 0)); - int[] dyeColors = {0, 8, 7, 15, 12, 14, 1, 4, 5, 13, 9, 3, 11, 10, 2, 6}; - for (int color : dyeColors) { - addCreativeItem(Item.get(Item.BED, color)); - } - addCreativeItem(Item.get(Item.BOOKSHELF, 0)); - addCreativeItem(Item.get(Item.SIGN, 0)); - addCreativeItem(Item.get(Item.PAINTING, 0)); - addCreativeItem(Item.get(Item.ITEM_FRAME, 0)); - addCreativeItem(Item.get(Item.WORKBENCH, 0)); - addCreativeItem(Item.get(Item.STONECUTTER, 0)); - addCreativeItem(Item.get(Item.CHEST, 0)); - addCreativeItem(Item.get(Item.TRAPPED_CHEST, 0)); - addCreativeItem(Item.get(Item.FURNACE, 0)); - addCreativeItem(Item.get(Item.BREWING_STAND, 0)); - addCreativeItem(Item.get(Item.CAULDRON, 0)); - addCreativeItem(Item.get(Item.NOTEBLOCK, 0)); - addCreativeItem(Item.get(Item.END_ROD, 0)); - addCreativeItem(Item.get(Item.END_PORTAL_FRAME, 0)); - addCreativeItem(Item.get(Item.ANVIL, 0)); - addCreativeItem(Item.get(Item.ANVIL, 4)); - addCreativeItem(Item.get(Item.ANVIL, 8)); - addCreativeItem(Item.get(Item.DANDELION, 0)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_POPPY)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_BLUE_ORCHID)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_ALLIUM)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_AZURE_BLUET)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_RED_TULIP)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_ORANGE_TULIP)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_WHITE_TULIP)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_PINK_TULIP)); - addCreativeItem(Item.get(Item.RED_FLOWER, BlockFlower.TYPE_OXEYE_DAISY)); - addCreativeItem(Item.get(Item.DOUBLE_PLANT, 0)); // SUNFLOWER - addCreativeItem(Item.get(Item.DOUBLE_PLANT, 1)); // Lilac - addCreativeItem(Item.get(Item.DOUBLE_PLANT, 2)); // Double Tall Grass - addCreativeItem(Item.get(Item.DOUBLE_PLANT, 3)); // Large fern - addCreativeItem(Item.get(Item.DOUBLE_PLANT, 4)); // Rose bush - addCreativeItem(Item.get(Item.DOUBLE_PLANT, 5)); // Peony - addCreativeItem(Item.get(Item.BROWN_MUSHROOM, 0)); - addCreativeItem(Item.get(Item.RED_MUSHROOM, 0)); - addCreativeItem(Item.get(Item.BROWN_MUSHROOM_BLOCK, 14)); - addCreativeItem(Item.get(Item.RED_MUSHROOM_BLOCK, 14)); - addCreativeItem(Item.get(Item.BROWN_MUSHROOM_BLOCK, 0)); - addCreativeItem(Item.get(Item.RED_MUSHROOM_BLOCK, 15)); - addCreativeItem(Item.get(Item.CACTUS, 0)); - addCreativeItem(Item.get(Item.MELON_BLOCK, 0)); - addCreativeItem(Item.get(Item.PUMPKIN, 0)); - addCreativeItem(Item.get(Item.LIT_PUMPKIN, 0)); - addCreativeItem(Item.get(Item.COBWEB, 0)); - addCreativeItem(Item.get(Item.HAY_BALE, 0)); - addCreativeItem(Item.get(Item.TALL_GRASS, 1)); - addCreativeItem(Item.get(Item.TALL_GRASS, 2)); - addCreativeItem(Item.get(Item.DEAD_BUSH, 0)); - addCreativeItem(Item.get(Item.SAPLING, 0)); - addCreativeItem(Item.get(Item.SAPLING, 1)); - addCreativeItem(Item.get(Item.SAPLING, 2)); - addCreativeItem(Item.get(Item.SAPLING, 3)); - addCreativeItem(Item.get(Item.SAPLING, 4)); - addCreativeItem(Item.get(Item.SAPLING, 5)); - addCreativeItem(Item.get(Item.LEAVES, 0)); - addCreativeItem(Item.get(Item.LEAVES, 1)); - addCreativeItem(Item.get(Item.LEAVES, 2)); - addCreativeItem(Item.get(Item.LEAVES, 3)); - addCreativeItem(Item.get(Item.LEAVES2, 0)); - addCreativeItem(Item.get(Item.LEAVES2, 1)); - - addCreativeItem(Item.get(Item.WHITE_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.SILVER_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.GRAY_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.BLACK_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.BROWN_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.RED_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.ORANGE_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.LIME_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.GREEN_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.CYAN_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.LIGHT_BLUE_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.BLUE_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.PURPLE_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.MAGENTA_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.WHITE_GLAZED_TERRACOTTA)); - addCreativeItem(Item.get(Item.PINK_GLAZED_TERRACOTTA)); - - addCreativeItem(Item.get(Item.CAKE, 0)); - - addCreativeItem(Item.get(Item.SKULL, ItemSkull.SKELETON_SKULL)); - addCreativeItem(Item.get(Item.SKULL, ItemSkull.WITHER_SKELETON_SKULL)); - addCreativeItem(Item.get(Item.SKULL, ItemSkull.ZOMBIE_HEAD)); - addCreativeItem(Item.get(Item.SKULL, ItemSkull.HEAD)); - addCreativeItem(Item.get(Item.SKULL, ItemSkull.CREEPER_HEAD)); - addCreativeItem(Item.get(Item.SKULL, ItemSkull.DRAGON_HEAD)); - - addCreativeItem(Item.get(Item.FLOWER_POT, 0)); - - addCreativeItem(Item.get(Item.MONSTER_EGG, 0)); - addCreativeItem(Item.get(Item.MONSTER_EGG, 1)); - addCreativeItem(Item.get(Item.MONSTER_EGG, 2)); - addCreativeItem(Item.get(Item.MONSTER_EGG, 3)); - addCreativeItem(Item.get(Item.MONSTER_EGG, 4)); - addCreativeItem(Item.get(Item.MONSTER_EGG, 5)); - - addCreativeItem(Item.get(Item.DRAGON_EGG, 0)); - addCreativeItem(Item.get(Item.END_CRYSTAL, 0)); - addCreativeItem(Item.get(Item.MONSTER_SPAWNER, 0)); - addCreativeItem(Item.get(Item.ENCHANTMENT_TABLE, 0)); - addCreativeItem(Item.get(Item.SLIME_BLOCK, 0)); - addCreativeItem(Item.get(Item.ENDER_CHEST, 0)); - - for (int color : dyeColors) { - addCreativeItem(Item.get(Item.WOOL, color)); + String path = server.getDataPath() + "creativeitems.json"; + if (!new File(path).exists()) { + try { + Utils.writeFile(path, Server.class.getClassLoader().getResourceAsStream("creativeitems.json")); + } catch (IOException e) { + MainLogger.getLogger().logException(e); + return; + } } + List list = new Config(path, Config.YAML).getMapList("items"); - addCreativeItem(Item.get(Item.CARPET, 0)); - addCreativeItem(Item.get(Item.CARPET, 8)); - addCreativeItem(Item.get(Item.CARPET, 7)); - addCreativeItem(Item.get(Item.CARPET, 15)); - addCreativeItem(Item.get(Item.CARPET, 12)); - addCreativeItem(Item.get(Item.CARPET, 14)); - addCreativeItem(Item.get(Item.CARPET, 1)); - addCreativeItem(Item.get(Item.CARPET, 4)); - addCreativeItem(Item.get(Item.CARPET, 5)); - addCreativeItem(Item.get(Item.CARPET, 13)); - addCreativeItem(Item.get(Item.CARPET, 9)); - addCreativeItem(Item.get(Item.CARPET, 3)); - addCreativeItem(Item.get(Item.CARPET, 11)); - addCreativeItem(Item.get(Item.CARPET, 10)); - addCreativeItem(Item.get(Item.CARPET, 2)); - addCreativeItem(Item.get(Item.CARPET, 6)); - - - //Tools - addCreativeItem(Item.get(Item.RAIL, 0)); - addCreativeItem(Item.get(Item.POWERED_RAIL, 0)); - addCreativeItem(Item.get(Item.DETECTOR_RAIL, 0)); - addCreativeItem(Item.get(Item.ACTIVATOR_RAIL, 0)); - addCreativeItem(Item.get(Item.TORCH, 0)); - addCreativeItem(Item.get(Item.BUCKET, 0)); - addCreativeItem(Item.get(Item.BUCKET, 1)); // milk - addCreativeItem(Item.get(Item.BUCKET, 8)); // water - addCreativeItem(Item.get(Item.BUCKET, 10)); // lava - addCreativeItem(Item.get(Item.TNT, 0)); - addCreativeItem(Item.get(Item.LEAD, 0)); - addCreativeItem(Item.get(Item.NAME_TAG, 0)); - addCreativeItem(Item.get(Item.REDSTONE, 0)); - addCreativeItem(Item.get(Item.BOW, 0)); - addCreativeItem(Item.get(Item.FISHING_ROD, 0)); - addCreativeItem(Item.get(Item.FLINT_AND_STEEL, 0)); - addCreativeItem(Item.get(Item.SHEARS, 0)); - addCreativeItem(Item.get(Item.CLOCK, 0)); - addCreativeItem(Item.get(Item.COMPASS, 0)); - addCreativeItem(Item.get(Item.MINECART, 0)); - addCreativeItem(Item.get(Item.MINECART_WITH_CHEST, 0)); - addCreativeItem(Item.get(Item.MINECART_WITH_HOPPER, 0)); - addCreativeItem(Item.get(Item.MINECART_WITH_TNT, 0)); - addCreativeItem(Item.get(Item.BOAT, 0)); // Oak - addCreativeItem(Item.get(Item.BOAT, 1)); // Spruce - addCreativeItem(Item.get(Item.BOAT, 2)); // Birch - addCreativeItem(Item.get(Item.BOAT, 3)); // Jungle - addCreativeItem(Item.get(Item.BOAT, 4)); // Acacia - addCreativeItem(Item.get(Item.BOAT, 5)); // Dark Oak - addCreativeItem(Item.get(Item.SADDLE, 0)); - addCreativeItem(Item.get(Item.LEATHER_HORSE_ARMOR, 0)); - addCreativeItem(Item.get(Item.IRON_HORSE_ARMOR, 0)); - addCreativeItem(Item.get(Item.GOLD_HORSE_ARMOR, 0)); - addCreativeItem(Item.get(Item.DIAMOND_HORSE_ARMOR, 0)); - - addCreativeItem(Item.get(Item.SPAWN_EGG, 10)); //Chicken - addCreativeItem(Item.get(Item.SPAWN_EGG, 11)); //Cow - addCreativeItem(Item.get(Item.SPAWN_EGG, 12)); //Pig - addCreativeItem(Item.get(Item.SPAWN_EGG, 13)); //Sheep - addCreativeItem(Item.get(Item.SPAWN_EGG, 15)); //Villager - addCreativeItem(Item.get(Item.SPAWN_EGG, 16)); //Mooshroom - addCreativeItem(Item.get(Item.SPAWN_EGG, 17)); //Squid - addCreativeItem(Item.get(Item.SPAWN_EGG, 19)); //Bat - //addCreativeItem(Item.get(Item.SPAWN_EGG, 20)); //Iron Golem - //addCreativeItem(Item.get(Item.SPAWN_EGG, 21)); //Snow Golem - addCreativeItem(Item.get(Item.SPAWN_EGG, 22)); //Ocelot - addCreativeItem(Item.get(Item.SPAWN_EGG, 23)); //Horse - addCreativeItem(Item.get(Item.SPAWN_EGG, 24)); //Donkey - addCreativeItem(Item.get(Item.SPAWN_EGG, 25)); //Mule - addCreativeItem(Item.get(Item.SPAWN_EGG, 26)); //SkeletonHorse - addCreativeItem(Item.get(Item.SPAWN_EGG, 27)); //ZombieHorse - addCreativeItem(Item.get(Item.SPAWN_EGG, 28)); //PolarBear - addCreativeItem(Item.get(Item.SPAWN_EGG, 29)); //Llama - addCreativeItem(Item.get(Item.SPAWN_EGG, 32)); //Zombie - addCreativeItem(Item.get(Item.SPAWN_EGG, 33)); //Creeper - addCreativeItem(Item.get(Item.SPAWN_EGG, 34)); //Skeleton - addCreativeItem(Item.get(Item.SPAWN_EGG, 35)); //Spider - addCreativeItem(Item.get(Item.SPAWN_EGG, 36)); //Zombie Pigman - addCreativeItem(Item.get(Item.SPAWN_EGG, 37)); //Slime - addCreativeItem(Item.get(Item.SPAWN_EGG, 38)); //Enderman - addCreativeItem(Item.get(Item.SPAWN_EGG, 39)); //Silverfish - addCreativeItem(Item.get(Item.SPAWN_EGG, 40)); //Cave spider - addCreativeItem(Item.get(Item.SPAWN_EGG, 41)); //Ghast - addCreativeItem(Item.get(Item.SPAWN_EGG, 42)); //MagmaCube - addCreativeItem(Item.get(Item.SPAWN_EGG, 43)); //Blaze - addCreativeItem(Item.get(Item.SPAWN_EGG, 45)); //Witch - addCreativeItem(Item.get(Item.SPAWN_EGG, 46)); //Stray - addCreativeItem(Item.get(Item.SPAWN_EGG, 47)); //Husk - addCreativeItem(Item.get(Item.SPAWN_EGG, 49)); //Guardian - addCreativeItem(Item.get(Item.SPAWN_EGG, 50)); //ElderGuardian - addCreativeItem(Item.get(Item.SPAWN_EGG, 54)); //Shulker - - addCreativeItem(Item.get(Item.FIRE_CHARGE, 0)); - addCreativeItem(Item.get(Item.WOODEN_SWORD)); - addCreativeItem(Item.get(Item.WOODEN_HOE)); - addCreativeItem(Item.get(Item.WOODEN_SHOVEL)); - addCreativeItem(Item.get(Item.WOODEN_PICKAXE)); - addCreativeItem(Item.get(Item.WOODEN_AXE)); - addCreativeItem(Item.get(Item.STONE_SWORD)); - addCreativeItem(Item.get(Item.STONE_HOE)); - addCreativeItem(Item.get(Item.STONE_SHOVEL)); - addCreativeItem(Item.get(Item.STONE_PICKAXE)); - addCreativeItem(Item.get(Item.STONE_AXE)); - addCreativeItem(Item.get(Item.IRON_SWORD)); - addCreativeItem(Item.get(Item.IRON_HOE)); - addCreativeItem(Item.get(Item.IRON_SHOVEL)); - addCreativeItem(Item.get(Item.IRON_PICKAXE)); - addCreativeItem(Item.get(Item.IRON_AXE)); - addCreativeItem(Item.get(Item.DIAMOND_SWORD)); - addCreativeItem(Item.get(Item.DIAMOND_HOE)); - addCreativeItem(Item.get(Item.DIAMOND_SHOVEL)); - addCreativeItem(Item.get(Item.DIAMOND_PICKAXE)); - addCreativeItem(Item.get(Item.DIAMOND_AXE)); - addCreativeItem(Item.get(Item.GOLD_SWORD)); - addCreativeItem(Item.get(Item.GOLD_HOE)); - addCreativeItem(Item.get(Item.GOLD_SHOVEL)); - addCreativeItem(Item.get(Item.GOLD_PICKAXE)); - addCreativeItem(Item.get(Item.GOLD_AXE)); - addCreativeItem(Item.get(Item.LEATHER_CAP)); - addCreativeItem(Item.get(Item.LEATHER_TUNIC)); - addCreativeItem(Item.get(Item.LEATHER_PANTS)); - addCreativeItem(Item.get(Item.LEATHER_BOOTS)); - addCreativeItem(Item.get(Item.CHAIN_HELMET)); - addCreativeItem(Item.get(Item.CHAIN_CHESTPLATE)); - addCreativeItem(Item.get(Item.CHAIN_LEGGINGS)); - addCreativeItem(Item.get(Item.CHAIN_BOOTS)); - addCreativeItem(Item.get(Item.IRON_HELMET)); - addCreativeItem(Item.get(Item.IRON_CHESTPLATE)); - addCreativeItem(Item.get(Item.IRON_LEGGINGS)); - addCreativeItem(Item.get(Item.IRON_BOOTS)); - addCreativeItem(Item.get(Item.DIAMOND_HELMET)); - addCreativeItem(Item.get(Item.DIAMOND_CHESTPLATE)); - addCreativeItem(Item.get(Item.DIAMOND_LEGGINGS)); - addCreativeItem(Item.get(Item.DIAMOND_BOOTS)); - addCreativeItem(Item.get(Item.GOLD_HELMET)); - addCreativeItem(Item.get(Item.GOLD_CHESTPLATE)); - addCreativeItem(Item.get(Item.GOLD_LEGGINGS)); - addCreativeItem(Item.get(Item.GOLD_BOOTS)); - addCreativeItem(Item.get(Item.ELYTRA)); - addCreativeItem(Item.get(Item.LEVER)); - addCreativeItem(Item.get(Item.REDSTONE_LAMP)); - addCreativeItem(Item.get(Item.REDSTONE_TORCH)); - addCreativeItem(Item.get(Item.WOODEN_PRESSURE_PLATE)); - addCreativeItem(Item.get(Item.STONE_PRESSURE_PLATE)); - addCreativeItem(Item.get(Item.LIGHT_WEIGHTED_PRESSURE_PLATE)); - addCreativeItem(Item.get(Item.HEAVY_WEIGHTED_PRESSURE_PLATE)); - addCreativeItem(Item.get(Item.WOODEN_BUTTON, 5)); - addCreativeItem(Item.get(Item.STONE_BUTTON, 5)); - addCreativeItem(Item.get(Item.DAYLIGHT_DETECTOR)); - addCreativeItem(Item.get(Item.TRIPWIRE_HOOK)); - addCreativeItem(Item.get(Item.REPEATER)); - addCreativeItem(Item.get(Item.COMPARATOR)); - addCreativeItem(Item.get(Item.DISPENSER, 3)); - addCreativeItem(Item.get(Item.DROPPER)); - addCreativeItem(Item.get(Item.PISTON)); - addCreativeItem(Item.get(Item.STICKY_PISTON)); - addCreativeItem(Item.get(Item.OBSERVER)); - addCreativeItem(Item.get(Item.HOPPER)); - addCreativeItem(Item.get(Item.SNOWBALL)); - addCreativeItem(Item.get(Item.ENDER_PEARL)); - addCreativeItem(Item.get(Item.ENDER_EYE)); - - //Seeds - addCreativeItem(Item.get(Item.COAL, 0)); - addCreativeItem(Item.get(Item.COAL, 1)); - addCreativeItem(Item.get(Item.DIAMOND, 0)); - addCreativeItem(Item.get(Item.IRON_INGOT, 0)); - addCreativeItem(Item.get(Item.GOLD_INGOT, 0)); - addCreativeItem(Item.get(Item.EMERALD, 0)); - addCreativeItem(Item.get(Item.STICK, 0)); - addCreativeItem(Item.get(Item.BOWL, 0)); - addCreativeItem(Item.get(Item.STRING, 0)); - addCreativeItem(Item.get(Item.FEATHER, 0)); - addCreativeItem(Item.get(Item.FLINT, 0)); - addCreativeItem(Item.get(Item.LEATHER, 0)); - addCreativeItem(Item.get(Item.RABBIT_HIDE, 0)); - addCreativeItem(Item.get(Item.CLAY, 0)); - addCreativeItem(Item.get(Item.SUGAR, 0)); - addCreativeItem(Item.get(Item.BRICK, 0)); - addCreativeItem(Item.get(Item.NETHER_BRICK, 0)); - addCreativeItem(Item.get(Item.NETHER_QUARTZ, 0)); - addCreativeItem(Item.get(Item.PAPER, 0)); - addCreativeItem(Item.get(Item.BOOK, 0)); - addCreativeItem(Item.get(Item.ARROW, 0)); - addCreativeItem(Item.get(Item.BONE, 0)); - addCreativeItem(Item.get(Item.EMPTY_MAP, 0)); - addCreativeItem(Item.get(Item.SUGARCANE, 0)); - addCreativeItem(Item.get(Item.WHEAT, 0)); - addCreativeItem(Item.get(Item.SEEDS, 0)); - addCreativeItem(Item.get(Item.PUMPKIN_SEEDS, 0)); - addCreativeItem(Item.get(Item.MELON_SEEDS, 0)); - addCreativeItem(Item.get(Item.BEETROOT_SEEDS, 0)); - addCreativeItem(Item.get(Item.EGG, 0)); - addCreativeItem(Item.get(Item.APPLE, 0)); - addCreativeItem(Item.get(Item.GOLDEN_APPLE, 0)); - addCreativeItem(Item.get(Item.GOLDEN_APPLE_ENCHANTED, 0)); - addCreativeItem(Item.get(Item.RAW_FISH, 0)); - addCreativeItem(Item.get(Item.RAW_SALMON, 0)); - addCreativeItem(Item.get(Item.CLOWNFISH, 0)); - addCreativeItem(Item.get(Item.PUFFERFISH, 0)); - addCreativeItem(Item.get(Item.COOKED_FISH, 0)); - addCreativeItem(Item.get(Item.COOKED_SALMON, 0)); - addCreativeItem(Item.get(Item.ROTTEN_FLESH, 0)); - addCreativeItem(Item.get(Item.MUSHROOM_STEW, 0)); - addCreativeItem(Item.get(Item.BREAD, 0)); - addCreativeItem(Item.get(Item.RAW_PORKCHOP, 0)); - addCreativeItem(Item.get(Item.COOKED_PORKCHOP, 0)); - addCreativeItem(Item.get(Item.RAW_CHICKEN, 0)); - addCreativeItem(Item.get(Item.COOKED_CHICKEN, 0)); - addCreativeItem(Item.get(Item.RAW_MUTTON, 0)); - addCreativeItem(Item.get(Item.COOKED_MUTTON, 0)); - addCreativeItem(Item.get(Item.RAW_BEEF, 0)); - addCreativeItem(Item.get(Item.STEAK, 0)); - addCreativeItem(Item.get(Item.MELON, 0)); - addCreativeItem(Item.get(Item.CARROT, 0)); - addCreativeItem(Item.get(Item.POTATO, 0)); - addCreativeItem(Item.get(Item.BAKED_POTATO, 0)); - addCreativeItem(Item.get(Item.POISONOUS_POTATO, 0)); - addCreativeItem(Item.get(Item.BEETROOT, 0)); - addCreativeItem(Item.get(Item.COOKIE, 0)); - addCreativeItem(Item.get(Item.PUMPKIN_PIE, 0)); - addCreativeItem(Item.get(Item.RAW_RABBIT, 0)); - addCreativeItem(Item.get(Item.COOKED_RABBIT, 0)); - addCreativeItem(Item.get(Item.RABBIT_STEW, 0)); - addCreativeItem(Item.get(Item.CHORUS_FRUIT, 0)); - addCreativeItem(Item.get(Item.POPPED_CHORUS_FRUIT, 0)); - addCreativeItem(Item.get(Item.NETHER_STAR, 0)); - addCreativeItem(Item.get(Item.MAGMA_CREAM, 0)); - addCreativeItem(Item.get(Item.BLAZE_ROD, 0)); - addCreativeItem(Item.get(Item.GOLD_NUGGET, 0)); - addCreativeItem(Item.get(Item.GOLDEN_CARROT, 0)); - addCreativeItem(Item.get(Item.GLISTERING_MELON, 0)); - addCreativeItem(Item.get(Item.RABBIT_FOOT, 0)); - addCreativeItem(Item.get(Item.GHAST_TEAR, 0)); - addCreativeItem(Item.get(Item.SLIMEBALL, 0)); - addCreativeItem(Item.get(Item.BLAZE_POWDER, 0)); - addCreativeItem(Item.get(Item.NETHER_WART, 0)); - addCreativeItem(Item.get(Item.GUNPOWDER, 0)); - addCreativeItem(Item.get(Item.GLOWSTONE_DUST, 0)); - addCreativeItem(Item.get(Item.SPIDER_EYE, 0)); - addCreativeItem(Item.get(Item.FERMENTED_SPIDER_EYE, 0)); - addCreativeItem(Item.get(Item.DRAGON_BREATH)); - addCreativeItem(Item.get(Item.CARROT_ON_A_STICK)); - addCreativeItem(Item.get(Item.EXPERIENCE_BOTTLE)); - addCreativeItem(Item.get(Item.SHULKER_SHELL)); - addCreativeItem(Item.get(Item.PRISMARINE_SHARD, 0)); - addCreativeItem(Item.get(Item.PRISMARINE_CRYSTALS, 0)); - for (int color : dyeColors) { - addCreativeItem(Item.get(Item.DYE, color)); + for (Map map : list) { + try { + int id = (int) map.get("id"); + int damage = (int) map.getOrDefault("damage", 0); + String hex = (String) map.get("nbt_hex"); + byte[] nbt = hex != null ? DatatypeConverter.parseHexBinary(hex) : new byte[0]; + + addCreativeItem(Item.get(id, damage, 1, nbt)); + } catch (Exception e) { + MainLogger.getLogger().logException(e); + } } - - //Potion - addCreativeItem(Item.get(Item.GLASS_BOTTLE, 0)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.NO_EFFECTS)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.MUNDANE)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.MUNDANE_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.THICK)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.AWKWARD)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.NIGHT_VISION)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.NIGHT_VISION_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.INVISIBLE)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.INVISIBLE_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.LEAPING)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.LEAPING_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.LEAPING_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.FIRE_RESISTANCE)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.FIRE_RESISTANCE_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.SPEED)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.SPEED_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.SPEED_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.SLOWNESS)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.SLOWNESS_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.WATER_BREATHING)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.WATER_BREATHING_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.INSTANT_HEALTH)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.INSTANT_HEALTH_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.HARMING)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.HARMING_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.POISON)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.POISON_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.POISON_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.REGENERATION)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.REGENERATION_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.REGENERATION_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.STRENGTH)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.STRENGTH_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.STRENGTH_II)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.WEAKNESS)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.WEAKNESS_LONG)); - addCreativeItem(Item.get(Item.POTION, ItemPotion.DECAY)); - - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.NO_EFFECTS)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.MUNDANE)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.MUNDANE_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.THICK)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.AWKWARD)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.NIGHT_VISION)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.NIGHT_VISION_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.INVISIBLE)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.INVISIBLE_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.LEAPING)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.LEAPING_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.LEAPING_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.FIRE_RESISTANCE)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.FIRE_RESISTANCE_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.SPEED)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.SPEED_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.SPEED_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.SLOWNESS)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.SLOWNESS_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.WATER_BREATHING)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.WATER_BREATHING_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.INSTANT_HEALTH)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.INSTANT_HEALTH_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.HARMING)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.HARMING_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.POISON)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.POISON_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.POISON_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.REGENERATION)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.REGENERATION_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.REGENERATION_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.STRENGTH)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.STRENGTH_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.STRENGTH_II)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.WEAKNESS)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.WEAKNESS_LONG)); - addCreativeItem(Item.get(Item.SPLASH_POTION, ItemPotion.DECAY)); - - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.NO_EFFECTS)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.MUNDANE)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.MUNDANE_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.THICK)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.AWKWARD)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.NIGHT_VISION)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.NIGHT_VISION_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.INVISIBLE)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.INVISIBLE_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.LEAPING)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.LEAPING_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.LEAPING_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.FIRE_RESISTANCE)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.FIRE_RESISTANCE_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.SPEED)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.SPEED_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.SPEED_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.SLOWNESS)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.SLOWNESS_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.WATER_BREATHING)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.WATER_BREATHING_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.INSTANT_HEALTH)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.INSTANT_HEALTH_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.HARMING)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.HARMING_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.POISON)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.POISON_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.POISON_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.REGENERATION)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.REGENERATION_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.REGENERATION_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.STRENGTH)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.STRENGTH_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.STRENGTH_II)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.WEAKNESS)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.WEAKNESS_LONG)); - addCreativeItem(Item.get(Item.LINGERING_POTION, ItemPotion.DECAY)); } public static void clearCreativeItems() { @@ -1835,7 +1244,7 @@ public String[] getLore() { return lines.toArray(new String[0]); } - public void setLore(String... lines) { + public Item setLore(String... lines) { CompoundTag tag; if (!this.hasCompoundTag()) { tag = new CompoundTag(); @@ -1855,6 +1264,7 @@ public void setLore(String... lines) { } this.setNamedTag(tag); + return this; } public Tag getNamedTagEntry(String name) { @@ -1869,16 +1279,24 @@ public Tag getNamedTagEntry(String name) { public CompoundTag getNamedTag() { if (!this.hasCompoundTag()) { return null; - } else if (this.cachedNBT != null) { - return this.cachedNBT; } - return this.cachedNBT = parseCompoundTag(this.tags); + + if (this.cachedNBT == null) { + this.cachedNBT = parseCompoundTag(this.tags); + } + + if (this.cachedNBT != null) { + this.cachedNBT.setName(""); + } + + return this.cachedNBT; } public Item setNamedTag(CompoundTag tag) { if (tag.isEmpty()) { return this.clearNamedTag(); } + tag.setName(null); this.cachedNBT = tag; this.tags = writeCompoundTag(tag); @@ -1890,6 +1308,23 @@ public Item clearNamedTag() { return this.setCompoundTag(new byte[0]); } + public static CompoundTag parseCompoundTag(byte[] tag) { + try { + return NBTIO.read(tag, ByteOrder.LITTLE_ENDIAN); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public byte[] writeCompoundTag(CompoundTag tag) { + try { + tag.setName(""); + return NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public int getCount() { return count; } @@ -1898,6 +1333,10 @@ public void setCount(int count) { this.count = count; } + public boolean isNull() { + return this.count <= 0 || this.id == AIR; + } + final public String getName() { return this.hasCustomName() ? this.getCustomName() : this.name; } @@ -2041,6 +1480,22 @@ public boolean onActivate(Level level, Player player, Block block, Block target, return false; } + /** + * Called when a player uses the item on air, for example throwing a projectile. + * Returns whether the item was changed, for example count decrease or durability change. + */ + public boolean onClickAir(Player player, Vector3 directionVector) { + return false; + } + + /** + * Called when a player is using this item and releases it. Used to handle bow shoot actions. + * Returns whether the item was changed, for example count decrease or durability change. + */ + public boolean onReleaseUsing(Player player) { + return false; + } + @Override public final boolean equals(Object item) { return item instanceof Item && this.equals((Item) item, true); @@ -2051,27 +1506,41 @@ public final boolean equals(Item item, boolean checkDamage) { } public final boolean equals(Item item, boolean checkDamage, boolean checkCompound) { - return this.getId() == item.getId() && (!checkDamage || this.getDamage() == item.getDamage()) && (!checkCompound || Arrays.equals(this.getCompoundTag(), item.getCompoundTag())); + if (this.getId() == item.getId() && (!checkDamage || this.getDamage() == item.getDamage())) { + if (checkCompound) { + if (Arrays.equals(this.getCompoundTag(), item.getCompoundTag())) { + return true; + } else if (this.hasCompoundTag() && item.hasCompoundTag()) { + return this.getNamedTag().equals(item.getNamedTag()); + } + } else { + return true; + } + } + + return false; + } + + /** + * Returns whether the specified item stack has the same ID, damage, NBT and count as this item stack. + */ + public final boolean equalsExact(Item other) { + return this.equals(other, true, true) && this.count == other.count; } + @Deprecated public final boolean deepEquals(Item item) { - return deepEquals(item, true); + return equals(item, true); } + @Deprecated public final boolean deepEquals(Item item, boolean checkDamage) { - return deepEquals(item, checkDamage, true); + return equals(item, checkDamage, true); } + @Deprecated public final boolean deepEquals(Item item, boolean checkDamage, boolean checkCompound) { - if (this.equals(item, checkDamage, checkCompound)) { - return true; - } else if (item.hasCompoundTag()) { - return item.getNamedTag().equals(this.getNamedTag()); - } else if (this.hasCompoundTag()) { - return this.getNamedTag().equals(item.getNamedTag()); - } - - return false; + return equals(item, checkDamage, checkCompound); } @Override diff --git a/src/main/java/cn/nukkit/item/ItemArmor.java b/src/main/java/cn/nukkit/item/ItemArmor.java index 618bc70d60..a292eb2cdc 100644 --- a/src/main/java/cn/nukkit/item/ItemArmor.java +++ b/src/main/java/cn/nukkit/item/ItemArmor.java @@ -1,5 +1,8 @@ package cn.nukkit.item; +import cn.nukkit.Player; +import cn.nukkit.math.Vector3; + /** * author: MagicDroidX * Nukkit Project @@ -38,6 +41,25 @@ public boolean isArmor() { return true; } + @Override + public boolean onClickAir(Player player, Vector3 directionVector) { + if (this.isHelmet() && player.getInventory().getHelmet().isNull()) { + if (player.getInventory().setHelmet(this)) + player.getInventory().clear(player.getInventory().getHeldItemIndex()); + } else if (this.isChestplate() && player.getInventory().getChestplate().isNull()) { + if (player.getInventory().setChestplate(this)) + player.getInventory().clear(player.getInventory().getHeldItemIndex()); + } else if (this.isLeggings() && player.getInventory().getLeggings().isNull()) { + if (player.getInventory().setHelmet(this)) + player.getInventory().clear(player.getInventory().getHeldItemIndex()); + } else if (this.isBoots() && player.getInventory().getBoots().isNull()) { + if (player.getInventory().setBoots(this)) + player.getInventory().clear(player.getInventory().getHeldItemIndex()); + } + + return this.getCount() == 0; + } + @Override public int getEnchantAbility() { switch (this.getTier()) { diff --git a/src/main/java/cn/nukkit/item/ItemBookWritten.java b/src/main/java/cn/nukkit/item/ItemBookWritten.java new file mode 100644 index 0000000000..f4d699b393 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemBookWritten.java @@ -0,0 +1,71 @@ +package cn.nukkit.item; + +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.ListTag; + +import java.util.concurrent.ThreadLocalRandom; + +public class ItemBookWritten extends Item { + + protected boolean isWritten = false; + + public ItemBookWritten() { + this(0, 1); + } + + public ItemBookWritten(Integer meta, int count) { + super(Item.WRITTEN_BOOK, 0, count, "Book"); + } + + public Item writeBook(String author, String title, String[] pages) { + ListTag pageList = new ListTag<>("pages"); + for (String page : pages) { + pageList.add(new CompoundTag().putString("photoname", "").putString("text", page)); + } + return writeBook(author, title, pageList); + } + + public Item writeBook(String author, String title, ListTag pages) { + if (pages.size() > 50 || pages.size() <= 0) return this; //Minecraft does not support more than 50 pages + if (this.isWritten) return this; //Book content can only be updated once + CompoundTag tag; + if (!this.hasCompoundTag()) { + tag = new CompoundTag(); + } else { + tag = this.getNamedTag(); + } + + tag.putString("author", author); + tag.putString("title", title); + tag.putList(pages); + + tag.putInt("generation", 0); + long randomId = 1095216660480L + ThreadLocalRandom.current().nextLong(0L, 2147483647L); + tag.putLong("id", randomId); + + this.isWritten = true; + return this.setNamedTag(tag); + } + + public String getAuthor() { + if (!this.isWritten) return ""; + return this.getNamedTag().getString("author"); + } + + public String getTitle() { + if (!this.isWritten) return "Book"; + return this.getNamedTag().getString("title"); + } + + public String[] getPages() { + if (!this.isWritten) return new String[0]; + ListTag tag = (ListTag) this.getNamedTag().getList("pages"); + String[] pages = new String[tag.size()]; + int i = 0; + for (CompoundTag pageCompound : tag.getAll()) { + pages[i] = pageCompound.getString("text"); + i++; + } + return pages; + } +} \ No newline at end of file diff --git a/src/main/java/cn/nukkit/item/ItemBow.java b/src/main/java/cn/nukkit/item/ItemBow.java index ada68422ad..1a4e23dab1 100644 --- a/src/main/java/cn/nukkit/item/ItemBow.java +++ b/src/main/java/cn/nukkit/item/ItemBow.java @@ -1,5 +1,21 @@ package cn.nukkit.item; +import cn.nukkit.Player; +import cn.nukkit.Server; +import cn.nukkit.block.BlockAir; +import cn.nukkit.entity.projectile.EntityArrow; +import cn.nukkit.entity.projectile.EntityProjectile; +import cn.nukkit.event.entity.EntityShootBowEvent; +import cn.nukkit.event.entity.ProjectileLaunchEvent; +import cn.nukkit.item.enchantment.Enchantment; +import cn.nukkit.level.sound.LaunchSound; +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; + /** * author: MagicDroidX * Nukkit Project @@ -27,4 +43,91 @@ public int getMaxDurability() { public int getEnchantAbility() { return 1; } + + public boolean onReleaseUsing(Player player) { + Item itemArrow = Item.get(Item.ARROW, 0, 1); + + if (player.isSurvival() && !player.getInventory().contains(itemArrow)) { + player.getInventory().sendContents(player); + return false; + } + + double damage = 2; + boolean flame = false; + + if (this.hasEnchantments()) { + Enchantment bowDamage = this.getEnchantment(Enchantment.ID_BOW_POWER); + + if (bowDamage != null && bowDamage.getLevel() > 0) { + damage += 0.25 * (bowDamage.getLevel() + 1); + } + + Enchantment flameEnchant = this.getEnchantment(Enchantment.ID_BOW_FLAME); + flame = flameEnchant != null && flameEnchant.getLevel() > 0; + } + + CompoundTag nbt = new CompoundTag() + .putList(new ListTag("Pos") + .add(new DoubleTag("", player.x)) + .add(new DoubleTag("", player.y + player.getEyeHeight())) + .add(new DoubleTag("", player.z))) + .putList(new ListTag("Motion") + .add(new DoubleTag("", -Math.sin(player.yaw / 180 * Math.PI) * Math.cos(player.pitch / 180 * Math.PI))) + .add(new DoubleTag("", -Math.sin(player.pitch / 180 * Math.PI))) + .add(new DoubleTag("", Math.cos(player.yaw / 180 * Math.PI) * Math.cos(player.pitch / 180 * Math.PI)))) + .putList(new ListTag("Rotation") + .add(new FloatTag("", (player.yaw > 180 ? 360 : 0) - (float) player.yaw)) + .add(new FloatTag("", (float) -player.pitch))) + .putShort("Fire", player.isOnFire() || flame ? 45 * 60 : 0) + .putDouble("damage", damage); + + int diff = (Server.getInstance().getTick() - player.getStartActionTick()); + double p = (double) diff / 20; + + double f = Math.min((p * p + p * 2) / 3, 1) * 2; + EntityShootBowEvent entityShootBowEvent = new EntityShootBowEvent(player, this, new EntityArrow(player.chunk, nbt, player, f == 2), f); + + if (f < 0.1 || diff < 5) { + entityShootBowEvent.setCancelled(); + } + + Server.getInstance().getPluginManager().callEvent(entityShootBowEvent); + if (entityShootBowEvent.isCancelled()) { + entityShootBowEvent.getProjectile().kill(); + player.getInventory().sendContents(player); + } else { + entityShootBowEvent.getProjectile().setMotion(entityShootBowEvent.getProjectile().getMotion().multiply(entityShootBowEvent.getForce())); + if (player.isSurvival()) { + Enchantment infinity; + + if (!this.hasEnchantments() || (infinity = this.getEnchantment(Enchantment.ID_BOW_INFINITY)) == null || infinity.getLevel() <= 0) + player.getInventory().removeItem(itemArrow); + if (!this.isUnbreakable()) { + Enchantment durability = this.getEnchantment(Enchantment.ID_DURABILITY); + if (!(durability != null && durability.getLevel() > 0 && (100 / (durability.getLevel() + 1)) <= new Random().nextInt(100))) { + this.setDamage(this.getDamage() + 1); + if (this.getDamage() >= 385) { + player.getInventory().setItemInHand(new ItemBlock(new BlockAir(), 0, 0)); + } else { + player.getInventory().setItemInHand(this); + } + } + } + } + if (entityShootBowEvent.getProjectile() instanceof EntityProjectile) { + ProjectileLaunchEvent projectev = new ProjectileLaunchEvent(entityShootBowEvent.getProjectile()); + Server.getInstance().getPluginManager().callEvent(projectev); + if (projectev.isCancelled()) { + entityShootBowEvent.getProjectile().kill(); + } else { + entityShootBowEvent.getProjectile().spawnToAll(); + player.level.addSound(new LaunchSound(player), player.getViewers().values()); + } + } else { + entityShootBowEvent.getProjectile().spawnToAll(); + } + } + + return true; + } } diff --git a/src/main/java/cn/nukkit/item/ItemEgg.java b/src/main/java/cn/nukkit/item/ItemEgg.java index 139071cf4f..38eb3f062f 100644 --- a/src/main/java/cn/nukkit/item/ItemEgg.java +++ b/src/main/java/cn/nukkit/item/ItemEgg.java @@ -4,7 +4,7 @@ * author: MagicDroidX * Nukkit Project */ -public class ItemEgg extends Item { +public class ItemEgg extends ProjectileItem { public ItemEgg() { this(0, 1); @@ -17,4 +17,14 @@ public ItemEgg(Integer meta) { public ItemEgg(Integer meta, int count) { super(EGG, meta, count, "Egg"); } + + @Override + public String getProjectileEntityType() { + return "Egg"; + } + + @Override + public float getThrowForce() { + return 1.5f; + } } diff --git a/src/main/java/cn/nukkit/item/ItemEnderPearl.java b/src/main/java/cn/nukkit/item/ItemEnderPearl.java index b2895fdd3a..138de386ae 100644 --- a/src/main/java/cn/nukkit/item/ItemEnderPearl.java +++ b/src/main/java/cn/nukkit/item/ItemEnderPearl.java @@ -1,6 +1,6 @@ package cn.nukkit.item; -public class ItemEnderPearl extends Item { +public class ItemEnderPearl extends ProjectileItem { public ItemEnderPearl() { this(0, 1); @@ -18,4 +18,14 @@ public ItemEnderPearl(Integer meta, int count) { public int getMaxStackSize() { return 16; } + + @Override + public String getProjectileEntityType() { + return "EnderPearl"; + } + + @Override + public float getThrowForce() { + return 1.5f; + } } diff --git a/src/main/java/cn/nukkit/item/ItemExpBottle.java b/src/main/java/cn/nukkit/item/ItemExpBottle.java index 04c288b5f4..247e282431 100644 --- a/src/main/java/cn/nukkit/item/ItemExpBottle.java +++ b/src/main/java/cn/nukkit/item/ItemExpBottle.java @@ -4,7 +4,7 @@ * Created on 2015/12/25 by xtypr. * Package cn.nukkit.item in project Nukkit . */ -public class ItemExpBottle extends Item { +public class ItemExpBottle extends ProjectileItem { public ItemExpBottle() { this(0, 1); @@ -18,4 +18,14 @@ public ItemExpBottle(Integer meta, int count) { super(EXPERIENCE_BOTTLE, meta, count, "Bottle o' Enchanting"); } + @Override + public String getProjectileEntityType() { + return "ThrownExpBottle"; + } + + @Override + public float getThrowForce() { + return 1f; + } + } diff --git a/src/main/java/cn/nukkit/item/ItemMinecart.java b/src/main/java/cn/nukkit/item/ItemMinecart.java index a5c262d3b6..38466e3956 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecart.java +++ b/src/main/java/cn/nukkit/item/ItemMinecart.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.block.Block; +import cn.nukkit.block.BlockRail; import cn.nukkit.entity.item.EntityMinecartEmpty; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; @@ -9,6 +10,7 @@ import cn.nukkit.nbt.tag.DoubleTag; import cn.nukkit.nbt.tag.FloatTag; import cn.nukkit.nbt.tag.ListTag; +import cn.nukkit.utils.Rail; /** * author: MagicDroidX @@ -35,28 +37,31 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { - Block secret = level.getBlock(block.add(0, -1, 0)); - // TODO: 2016/1/30 check if blockId of secret is a rail - - EntityMinecartEmpty minecart = new EntityMinecartEmpty( - level.getChunk(block.getFloorX() >> 4, block.getFloorZ() >> 4), 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))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", 0)) - .add(new FloatTag("", 0))) - ); - minecart.spawnToAll(); - - // TODO: 2016/1/30 if player is survival, item in hand count-- - - return true; + if (Rail.isRailBlock(target)) { + Rail.Orientation type = ((BlockRail) target).getOrientation(); + double adjacent = 0.0D; + if (type.isAscending()) { + adjacent = 0.5D; + } + EntityMinecartEmpty minecart = new EntityMinecartEmpty( + level.getChunk(target.getFloorX() >> 4, target.getFloorZ() >> 4), new CompoundTag("") + .putList(new ListTag<>("Pos") + .add(new DoubleTag("", target.getX() + 0.5)) + .add(new DoubleTag("", target.getY() + 0.0625D + adjacent)) + .add(new DoubleTag("", target.getZ() + 0.5))) + .putList(new ListTag<>("Motion") + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0))) + .putList(new ListTag<>("Rotation") + .add(new FloatTag("", 0)) + .add(new FloatTag("", 0))) + ); + minecart.spawnToAll(); + count -= 1; + return true; + } + return false; } @Override diff --git a/src/main/java/cn/nukkit/item/ItemMinecartChest.java b/src/main/java/cn/nukkit/item/ItemMinecartChest.java index fecbf7792d..fdee794e64 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecartChest.java +++ b/src/main/java/cn/nukkit/item/ItemMinecartChest.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.block.Block; +import cn.nukkit.block.BlockRail; import cn.nukkit.entity.item.EntityMinecartChest; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; @@ -9,6 +10,7 @@ import cn.nukkit.nbt.tag.DoubleTag; import cn.nukkit.nbt.tag.FloatTag; import cn.nukkit.nbt.tag.ListTag; +import cn.nukkit.utils.Rail; public class ItemMinecartChest extends Item { @@ -21,7 +23,7 @@ public ItemMinecartChest(Integer meta) { } public ItemMinecartChest(Integer meta, int count) { - super(MINECART_WITH_CHEST, meta, count, "Minecart With Chest"); + super(MINECART_WITH_CHEST, meta, count, "Minecart with Chest"); } @Override @@ -31,27 +33,31 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { - Block secret = level.getBlock(block.add(0, -1, 0)); - // TODO: 2016/1/30 check if blockId of secret is a rail - EntityMinecartChest minecart = new EntityMinecartChest( - level.getChunk(block.getFloorX() >> 4, block.getFloorZ() >> 4), 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))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", 0)) - .add(new FloatTag("", 0))) - ); - minecart.spawnToAll(); - - // TODO: 2016/1/30 if player is survival, item in hand count-- - - return true; + if (Rail.isRailBlock(target)) { + Rail.Orientation type = ((BlockRail) target).getOrientation(); + double adjacent = 0.0D; + if (type.isAscending()) { + adjacent = 0.5D; + } + EntityMinecartChest minecart = new EntityMinecartChest( + level.getChunk(target.getFloorX() >> 4, target.getFloorZ() >> 4), new CompoundTag("") + .putList(new ListTag<>("Pos") + .add(new DoubleTag("", target.getX() + 0.5)) + .add(new DoubleTag("", target.getY() + 0.0625D + adjacent)) + .add(new DoubleTag("", target.getZ() + 0.5))) + .putList(new ListTag<>("Motion") + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0))) + .putList(new ListTag<>("Rotation") + .add(new FloatTag("", 0)) + .add(new FloatTag("", 0))) + ); + minecart.spawnToAll(); + count -= 1; + return true; + } + return false; } @Override diff --git a/src/main/java/cn/nukkit/item/ItemMinecartHopper.java b/src/main/java/cn/nukkit/item/ItemMinecartHopper.java index 6d5b34f83e..46b9d2865f 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecartHopper.java +++ b/src/main/java/cn/nukkit/item/ItemMinecartHopper.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.block.Block; +import cn.nukkit.block.BlockRail; import cn.nukkit.entity.item.EntityMinecartHopper; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; @@ -9,6 +10,7 @@ import cn.nukkit.nbt.tag.DoubleTag; import cn.nukkit.nbt.tag.FloatTag; import cn.nukkit.nbt.tag.ListTag; +import cn.nukkit.utils.Rail; public class ItemMinecartHopper extends Item { @@ -21,7 +23,7 @@ public ItemMinecartHopper(Integer meta) { } public ItemMinecartHopper(Integer meta, int count) { - super(MINECART_WITH_HOPPER, meta, count, "Minecart With Hopper"); + super(MINECART_WITH_HOPPER, meta, count, "Minecart with Hopper"); } @Override @@ -31,27 +33,31 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { - Block secret = level.getBlock(block.add(0, -1, 0)); - // TODO: 2016/1/30 check if blockId of secret is a rail - EntityMinecartHopper minecart = new EntityMinecartHopper( - level.getChunk(block.getFloorX() >> 4, block.getFloorZ() >> 4), 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))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", 0)) - .add(new FloatTag("", 0))) - ); - minecart.spawnToAll(); - - // TODO: 2016/1/30 if player is survival, item in hand count-- - - return true; + if (Rail.isRailBlock(target)) { + Rail.Orientation type = ((BlockRail) target).getOrientation(); + double adjacent = 0.0D; + if (type.isAscending()) { + adjacent = 0.5D; + } + EntityMinecartHopper minecart = new EntityMinecartHopper( + level.getChunk(target.getFloorX() >> 4, target.getFloorZ() >> 4), new CompoundTag("") + .putList(new ListTag<>("Pos") + .add(new DoubleTag("", target.getX() + 0.5)) + .add(new DoubleTag("", target.getY() + 0.0625D + adjacent)) + .add(new DoubleTag("", target.getZ() + 0.5))) + .putList(new ListTag<>("Motion") + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0))) + .putList(new ListTag<>("Rotation") + .add(new FloatTag("", 0)) + .add(new FloatTag("", 0))) + ); + minecart.spawnToAll(); + count -= 1; + return true; + } + return false; } @Override diff --git a/src/main/java/cn/nukkit/item/ItemMinecartTNT.java b/src/main/java/cn/nukkit/item/ItemMinecartTNT.java index 27fa125dca..51a9d1c93d 100644 --- a/src/main/java/cn/nukkit/item/ItemMinecartTNT.java +++ b/src/main/java/cn/nukkit/item/ItemMinecartTNT.java @@ -2,6 +2,7 @@ import cn.nukkit.Player; import cn.nukkit.block.Block; +import cn.nukkit.block.BlockRail; import cn.nukkit.entity.item.EntityMinecartTNT; import cn.nukkit.level.Level; import cn.nukkit.math.BlockFace; @@ -9,6 +10,7 @@ import cn.nukkit.nbt.tag.DoubleTag; import cn.nukkit.nbt.tag.FloatTag; import cn.nukkit.nbt.tag.ListTag; +import cn.nukkit.utils.Rail; public class ItemMinecartTNT extends Item { @@ -21,7 +23,7 @@ public ItemMinecartTNT(Integer meta) { } public ItemMinecartTNT(Integer meta, int count) { - super(MINECART_WITH_TNT, meta, count, "Minecart With TNT"); + super(MINECART_WITH_TNT, meta, count, "Minecart with TNT"); } @Override @@ -31,27 +33,31 @@ public boolean canBeActivated() { @Override public boolean onActivate(Level level, Player player, Block block, Block target, BlockFace face, double fx, double fy, double fz) { - Block secret = level.getBlock(block.add(0, -1, 0)); - // TODO: 2016/1/30 check if blockId of secret is a rail - EntityMinecartTNT minecart = new EntityMinecartTNT( - level.getChunk(block.getFloorX() >> 4, block.getFloorZ() >> 4), 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))) - .putList(new ListTag("Motion") - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0)) - .add(new DoubleTag("", 0))) - .putList(new ListTag("Rotation") - .add(new FloatTag("", 0)) - .add(new FloatTag("", 0))) - ); - minecart.spawnToAll(); - - // TODO: 2016/1/30 if player is survival, item in hand count-- - - return true; + if (Rail.isRailBlock(target)) { + Rail.Orientation type = ((BlockRail) target).getOrientation(); + double adjacent = 0.0D; + if (type.isAscending()) { + adjacent = 0.5D; + } + EntityMinecartTNT minecart = new EntityMinecartTNT( + level.getChunk(target.getFloorX() >> 4, target.getFloorZ() >> 4), new CompoundTag("") + .putList(new ListTag<>("Pos") + .add(new DoubleTag("", target.getX() + 0.5)) + .add(new DoubleTag("", target.getY() + 0.0625D + adjacent)) + .add(new DoubleTag("", target.getZ() + 0.5))) + .putList(new ListTag<>("Motion") + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0)) + .add(new DoubleTag("", 0))) + .putList(new ListTag<>("Rotation") + .add(new FloatTag("", 0)) + .add(new FloatTag("", 0))) + ); + minecart.spawnToAll(); + count -= 1; + return true; + } + return false; } @Override diff --git a/src/main/java/cn/nukkit/item/ItemPotionSplash.java b/src/main/java/cn/nukkit/item/ItemPotionSplash.java index d337540f0c..fa4bcc38f6 100644 --- a/src/main/java/cn/nukkit/item/ItemPotionSplash.java +++ b/src/main/java/cn/nukkit/item/ItemPotionSplash.java @@ -1,10 +1,12 @@ package cn.nukkit.item; +import cn.nukkit.nbt.tag.CompoundTag; + /** * Created on 2015/12/27 by xtypr. * Package cn.nukkit.item in project Nukkit . */ -public class ItemPotionSplash extends Item { +public class ItemPotionSplash extends ProjectileItem { public ItemPotionSplash(Integer meta) { this(meta, 1); @@ -23,4 +25,19 @@ public int getMaxStackSize() { public boolean canBeActivated() { return true; } + + @Override + public String getProjectileEntityType() { + return "ThrownPotion"; + } + + @Override + public float getThrowForce() { + return 1f; + } + + @Override + protected void correctNBT(CompoundTag nbt) { + nbt.putInt("PotionId", this.meta); + } } diff --git a/src/main/java/cn/nukkit/item/ItemRecord.java b/src/main/java/cn/nukkit/item/ItemRecord.java new file mode 100644 index 0000000000..84f5c0fec1 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecord.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +/** + * @author CreeperFace + */ +public abstract class ItemRecord extends Item { + + public ItemRecord() { + this(0, 1); + } + + public ItemRecord(Integer meta) { + this(meta, 1); + } + + public ItemRecord(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getMaxStackSize() { + return 1; + } + + public abstract int getSoundId(); +} diff --git a/src/main/java/cn/nukkit/item/ItemRecord11.java b/src/main/java/cn/nukkit/item/ItemRecord11.java new file mode 100644 index 0000000000..a5117a0b2b --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecord11.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecord11 extends ItemRecord { + + public ItemRecord11() { + this(0, 1); + } + + public ItemRecord11(Integer meta) { + this(meta, 1); + } + + public ItemRecord11(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_11; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecord13.java b/src/main/java/cn/nukkit/item/ItemRecord13.java new file mode 100644 index 0000000000..5537802709 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecord13.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecord13 extends ItemRecord { + + public ItemRecord13() { + this(0, 1); + } + + public ItemRecord13(Integer meta) { + this(meta, 1); + } + + public ItemRecord13(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_13; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordBlocks.java b/src/main/java/cn/nukkit/item/ItemRecordBlocks.java new file mode 100644 index 0000000000..21aabbf583 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordBlocks.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordBlocks extends ItemRecord { + + public ItemRecordBlocks() { + this(0, 1); + } + + public ItemRecordBlocks(Integer meta) { + this(meta, 1); + } + + public ItemRecordBlocks(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_BLOCKS; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordCat.java b/src/main/java/cn/nukkit/item/ItemRecordCat.java new file mode 100644 index 0000000000..ac826f6853 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordCat.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordCat extends ItemRecord { + + public ItemRecordCat() { + this(0, 1); + } + + public ItemRecordCat(Integer meta) { + this(meta, 1); + } + + public ItemRecordCat(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_CAT; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordChirp.java b/src/main/java/cn/nukkit/item/ItemRecordChirp.java new file mode 100644 index 0000000000..3e8cc21ae0 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordChirp.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordChirp extends ItemRecord { + + public ItemRecordChirp() { + this(0, 1); + } + + public ItemRecordChirp(Integer meta) { + this(meta, 1); + } + + public ItemRecordChirp(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_CHIRP; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordFar.java b/src/main/java/cn/nukkit/item/ItemRecordFar.java new file mode 100644 index 0000000000..cd84d1def5 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordFar.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordFar extends ItemRecord { + + public ItemRecordFar() { + this(0, 1); + } + + public ItemRecordFar(Integer meta) { + this(meta, 1); + } + + public ItemRecordFar(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_FAR; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordMall.java b/src/main/java/cn/nukkit/item/ItemRecordMall.java new file mode 100644 index 0000000000..5c2da3936b --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordMall.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordMall extends ItemRecord { + + public ItemRecordMall() { + this(0, 1); + } + + public ItemRecordMall(Integer meta) { + this(meta, 1); + } + + public ItemRecordMall(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_MALL; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordMellohi.java b/src/main/java/cn/nukkit/item/ItemRecordMellohi.java new file mode 100644 index 0000000000..13fb5c2d11 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordMellohi.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordMellohi extends ItemRecord { + + public ItemRecordMellohi() { + this(0, 1); + } + + public ItemRecordMellohi(Integer meta) { + this(meta, 1); + } + + public ItemRecordMellohi(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_MELLOHI; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordStal.java b/src/main/java/cn/nukkit/item/ItemRecordStal.java new file mode 100644 index 0000000000..3a4824de7c --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordStal.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordStal extends ItemRecord { + + public ItemRecordStal() { + this(0, 1); + } + + public ItemRecordStal(Integer meta) { + this(meta, 1); + } + + public ItemRecordStal(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_STAL; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordStrad.java b/src/main/java/cn/nukkit/item/ItemRecordStrad.java new file mode 100644 index 0000000000..1c7e42dd8c --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordStrad.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordStrad extends ItemRecord { + + public ItemRecordStrad() { + this(0, 1); + } + + public ItemRecordStrad(Integer meta) { + this(meta, 1); + } + + public ItemRecordStrad(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_STRAD; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordWait.java b/src/main/java/cn/nukkit/item/ItemRecordWait.java new file mode 100644 index 0000000000..738f26f088 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordWait.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordWait extends ItemRecord { + + public ItemRecordWait() { + this(0, 1); + } + + public ItemRecordWait(Integer meta) { + this(meta, 1); + } + + public ItemRecordWait(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_WAIT; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemRecordWard.java b/src/main/java/cn/nukkit/item/ItemRecordWard.java new file mode 100644 index 0000000000..b8f2f79154 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ItemRecordWard.java @@ -0,0 +1,26 @@ +package cn.nukkit.item; + +import cn.nukkit.network.protocol.LevelSoundEventPacket; + +/** + * @author CreeperFace + */ +public class ItemRecordWard extends ItemRecord { + + public ItemRecordWard() { + this(0, 1); + } + + public ItemRecordWard(Integer meta) { + this(meta, 1); + } + + public ItemRecordWard(Integer meta, int count) { + super(meta, count); + } + + @Override + public int getSoundId() { + return LevelSoundEventPacket.SOUND_RECORD_WARD; + } +} diff --git a/src/main/java/cn/nukkit/item/ItemSnowball.java b/src/main/java/cn/nukkit/item/ItemSnowball.java index 47bf2dc6e5..ae6a5a2740 100644 --- a/src/main/java/cn/nukkit/item/ItemSnowball.java +++ b/src/main/java/cn/nukkit/item/ItemSnowball.java @@ -4,7 +4,7 @@ * author: MagicDroidX * Nukkit Project */ -public class ItemSnowball extends Item { +public class ItemSnowball extends ProjectileItem { public ItemSnowball() { this(0, 1); @@ -22,4 +22,14 @@ public ItemSnowball(Integer meta, int count) { public int getMaxStackSize() { return 16; } + + @Override + public String getProjectileEntityType() { + return "Snowball"; + } + + @Override + public float getThrowForce() { + return 1.5f; + } } diff --git a/src/main/java/cn/nukkit/item/ProjectileItem.java b/src/main/java/cn/nukkit/item/ProjectileItem.java new file mode 100644 index 0000000000..0ae17e87a0 --- /dev/null +++ b/src/main/java/cn/nukkit/item/ProjectileItem.java @@ -0,0 +1,70 @@ +package cn.nukkit.item; + +import cn.nukkit.Player; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.projectile.EntityProjectile; +import cn.nukkit.event.entity.ProjectileLaunchEvent; +import cn.nukkit.level.sound.LaunchSound; +import cn.nukkit.math.Vector3; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.DoubleTag; +import cn.nukkit.nbt.tag.FloatTag; +import cn.nukkit.nbt.tag.ListTag; + +/** + * @author CreeperFace + */ +public abstract class ProjectileItem extends Item { + + public ProjectileItem(int id, Integer meta, int count, String name) { + super(id, meta, count, name); + } + + abstract public String getProjectileEntityType(); + + abstract public float getThrowForce(); + + public boolean onClickAir(Player player, Vector3 directionVector) { + CompoundTag nbt = new CompoundTag() + .putList(new ListTag("Pos") + .add(new DoubleTag("", player.x)) + .add(new DoubleTag("", player.y + player.getEyeHeight())) + .add(new DoubleTag("", player.z))) + .putList(new ListTag("Motion") + .add(new DoubleTag("", directionVector.x)) + .add(new DoubleTag("", directionVector.y)) + .add(new DoubleTag("", directionVector.z))) + .putList(new ListTag("Rotation") + .add(new FloatTag("", (float) player.yaw)) + .add(new FloatTag("", (float) player.pitch))); + + this.correctNBT(nbt); + + Entity projectile = Entity.createEntity(this.getProjectileEntityType(), player.getLevel().getChunk(player.getFloorX() >> 4, player.getFloorZ() >> 4), nbt, player); + if (projectile != null) { + projectile.setMotion(projectile.getMotion().multiply(this.getThrowForce())); + this.count--; + + if (projectile instanceof EntityProjectile) { + ProjectileLaunchEvent ev = new ProjectileLaunchEvent((EntityProjectile) projectile); + + player.getServer().getPluginManager().callEvent(ev); + if (ev.isCancelled()) { + projectile.kill(); + } else { + projectile.spawnToAll(); + player.getLevel().addSound(new LaunchSound(player), player.getViewers().values()); + } + } else { + projectile.spawnToAll(); + } + } else { + return false; + } + return true; + } + + protected void correctNBT(CompoundTag nbt) { + + } +} diff --git a/src/main/java/cn/nukkit/item/enchantment/Enchantment.java b/src/main/java/cn/nukkit/item/enchantment/Enchantment.java index 9e1f9c08e3..78ae5c1e5d 100644 --- a/src/main/java/cn/nukkit/item/enchantment/Enchantment.java +++ b/src/main/java/cn/nukkit/item/enchantment/Enchantment.java @@ -90,7 +90,7 @@ public static Enchantment get(int id) { } public static Enchantment getEnchantment(int id) { - return get(id) == null ? null : get(id).clone(); + return get(id).clone(); } public static Enchantment[] getEnchantments() { @@ -126,14 +126,14 @@ public int getLevel() { return level; } - public void setLevel(int level) { - this.setLevel(level, true); + public Enchantment setLevel(int level) { + return this.setLevel(level, true); } - public void setLevel(int level, boolean safe) { + public Enchantment setLevel(int level, boolean safe) { if (!safe) { this.level = level; - return; + return this; } if (level > this.getMaxLevel()) { @@ -143,6 +143,8 @@ public void setLevel(int level, boolean safe) { } else { this.level = level; } + + return this; } public int getId() { diff --git a/src/main/java/cn/nukkit/level/ChunkPosition.java b/src/main/java/cn/nukkit/level/ChunkPosition.java new file mode 100644 index 0000000000..4a8282dc54 --- /dev/null +++ b/src/main/java/cn/nukkit/level/ChunkPosition.java @@ -0,0 +1,42 @@ +package cn.nukkit.level; + +import cn.nukkit.math.MathHelper; +import cn.nukkit.math.Vector3; + +/** + * Author: Adam Matthew + *

+ * Nukkit Project + */ +public class ChunkPosition { + + public final int x; + public final int y; + public final int z; + + public ChunkPosition(int i, int j, int k) { + this.x = i; + this.y = j; + this.z = k; + } + + public ChunkPosition(Vector3 vec3d) { + this(MathHelper.floor(vec3d.x), MathHelper.floor(vec3d.y), MathHelper.floor(vec3d.z)); + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof ChunkPosition)) { + return false; + } else { + ChunkPosition chunkposition = (ChunkPosition) object; + + return chunkposition.x == this.x && chunkposition.y == this.y && chunkposition.z == this.z; + } + } + + @Override + public int hashCode() { + return this.x * 8976890 + this.y * 981131 + this.z; + } +} diff --git a/src/main/java/cn/nukkit/level/GameRules.java b/src/main/java/cn/nukkit/level/GameRules.java index 5a6da6fb1a..466440a003 100644 --- a/src/main/java/cn/nukkit/level/GameRules.java +++ b/src/main/java/cn/nukkit/level/GameRules.java @@ -31,6 +31,7 @@ public GameRules() { this.addGameRule("spectatorsGenerateChunks", "true", ValueType.BOOLEAN_VALUE); this.addGameRule("spawnRadius", "10", ValueType.NUMERICAL_VALUE); this.addGameRule("disableElytraMovementCheck", "false", ValueType.BOOLEAN_VALUE); + this.addGameRule("pvp", "true", ValueType.BOOLEAN_VALUE); } public void addGameRule(String key, String value, ValueType type) { diff --git a/src/main/java/cn/nukkit/level/Level.java b/src/main/java/cn/nukkit/level/Level.java index a9e3ab2f89..19ac2d6885 100644 --- a/src/main/java/cn/nukkit/level/Level.java +++ b/src/main/java/cn/nukkit/level/Level.java @@ -489,11 +489,11 @@ public void addSound(Sound sound, Collection players) { this.addSound(sound, players.stream().toArray(Player[]::new)); } - public void addLevelSoundEvent(byte type, int pitch, int data, Vector3 pos, boolean unknown, boolean disableRelativeVolume) { - this.addLevelSoundEvent(type, pitch, data, pos, this.players.values(), unknown, disableRelativeVolume); + public void addLevelSoundEvent(int type, int pitch, int data, Vector3 pos) { + this.addLevelSoundEvent(type, pitch, data, pos, false); } - public void addLevelSoundEvent(byte type, int pitch, int data, Vector3 pos, Collection players, boolean unknown, boolean disableRelativeVolume) { + public void addLevelSoundEvent(int type, int pitch, int data, Vector3 pos, boolean isGlobal) { LevelSoundEventPacket pk = new LevelSoundEventPacket(); pk.sound = type; pk.pitch = pitch; @@ -501,14 +501,9 @@ public void addLevelSoundEvent(byte type, int pitch, int data, Vector3 pos, Coll pk.x = (float) pos.x; pk.y = (float) pos.y; pk.z = (float) pos.z; - pk.unknownBool = unknown; - pk.disableRelativeVolume = disableRelativeVolume; + pk.isGlobal = isGlobal; - if (players == null) { - this.addChunkPacket(pos.getFloorX(), pos.getFloorZ(), pk); - } else { - Server.broadcastPacket(players, pk); - } + this.addChunkPacket(pos.getFloorX() >> 4, pos.getFloorZ() >> 4, pk); } public void addParticle(Particle particle) { @@ -676,30 +671,21 @@ public void checkTime() { } } - public void sendTime(Player player) { - if (this.stopTime) { + public void sendTime(Player... players) { + /*if (this.stopTime) { //TODO SetTimePacket pk0 = new SetTimePacket(); pk0.time = (int) this.time; player.dataPacket(pk0); - } + }*/ SetTimePacket pk = new SetTimePacket(); pk.time = (int) this.time; - player.dataPacket(pk); + Server.broadcastPacket(players, pk); } public void sendTime() { - if (this.stopTime) { - SetTimePacket pk0 = new SetTimePacket(); - pk0.time = (int) this.time; - Server.broadcastPacket(this.players.values().stream().toArray(Player[]::new), pk0); - } - - SetTimePacket pk = new SetTimePacket(); - pk.time = (int) this.time; - - Server.broadcastPacket(this.players.values().stream().toArray(Player[]::new), pk); + sendTime(this.players.values().stream().toArray(Player[]::new)); } public GameRules getGameRules() { @@ -767,8 +753,8 @@ public void doTick(int currentTick) { bolt.setEffect(false); } - this.addLevelSoundEvent(LevelSoundEventPacket.SOUND_THUNDER, 93, -1, vector, false, false); - this.addLevelSoundEvent(LevelSoundEventPacket.SOUND_EXPLODE, 93, -1, vector, false, false); + this.addLevelSoundEvent(LevelSoundEventPacket.SOUND_THUNDER, 93, -1, vector, false); + this.addLevelSoundEvent(LevelSoundEventPacket.SOUND_EXPLODE, 93, -1, vector, false); } } @@ -1755,7 +1741,6 @@ public void dropItem(Vector3 source, Item item, Vector3 motion, boolean dropArou .putShort("Health", 5).putCompound("Item", itemTag).putShort("PickupDelay", delay)); - itemEntity.setPickupDelay(delay); //TODO: fix this itemEntity.spawnToAll(); } } @@ -1806,11 +1791,11 @@ public Item useBreakOn(Vector3 vector, Item item, Player player, boolean createP breakTime *= 1 - (0.3 * eff.getLevel()); } - //breakTime -= 0.1; - //TODO: Check if it's necessary to minus breakTime with 0.1. + breakTime -= 0.15; BlockBreakEvent ev = new BlockBreakEvent(player, target, item, player.isCreative(), (player.lastBreak + breakTime * 1000) > System.currentTimeMillis()); + double distance; if (player.isSurvival() && !target.isBreakable(item)) { ev.setCancelled(); @@ -1957,6 +1942,7 @@ public Item useItemOn(Vector3 vector, Item item, BlockFace face, float fx, float return this.useItemOn(vector, item, face, fx, fy, fz, player, false); } + public Item useItemOn(Vector3 vector, Item item, BlockFace face, float fx, float fy, float fz, Player player, boolean playSound) { Block target = this.getBlock(vector); Block block = target.getSide(face); @@ -2003,6 +1989,7 @@ public Item useItemOn(Vector3 vector, Item item, BlockFace face, float fx, float } else { return null; } + } else if (target.canBeActivated() && target.onActivate(item, null)) { return item; } @@ -2098,7 +2085,7 @@ public Item useItemOn(Vector3 vector, Item item, BlockFace face, float fx, float } if (playSound) { - this.addSound(new BlockPlaceSound(hand, hand.getId())); + this.addLevelSoundEvent(LevelSoundEventPacket.SOUND_PLACE, 1, item.getId(), hand, false); } if (item.getCount() <= 0) { @@ -2369,14 +2356,18 @@ public void setChunk(int chunkX, int chunkZ, BaseFullChunk chunk, boolean unload for (Entity entity : oldEntities.values()) { chunk.addEntity(entity); - oldChunk.removeEntity(entity); - entity.chunk = chunk; + if (oldChunk != null) { + oldChunk.removeEntity(entity); + entity.chunk = chunk; + } } for (BlockEntity blockEntity : oldBlockEntities.values()) { chunk.addBlockEntity(blockEntity); - oldChunk.removeBlockEntity(blockEntity); - blockEntity.chunk = chunk; + if (oldChunk != null) { + oldChunk.removeBlockEntity(blockEntity); + blockEntity.chunk = chunk; + } } this.provider.setChunk(chunkX, chunkZ, chunk); diff --git a/src/main/java/cn/nukkit/level/format/anvil/ChunkRequestTask.java b/src/main/java/cn/nukkit/level/format/anvil/ChunkRequestTask.java index 299d12db02..b4f9c55d9f 100644 --- a/src/main/java/cn/nukkit/level/format/anvil/ChunkRequestTask.java +++ b/src/main/java/cn/nukkit/level/format/anvil/ChunkRequestTask.java @@ -72,8 +72,6 @@ public void onRun() { for (int z = 0; z < 16; ++z) { orderedIds.put(this.getColumn(ids, x, z)); orderedData.put(this.getHalfColumn(meta, x, z)); - orderedSkyLight.put(this.getHalfColumn(skyLight, x, z)); - orderedLight.put(this.getHalfColumn(blockLight, x, z)); } } @@ -90,8 +88,6 @@ public void onRun() { buffer .put(orderedIds) .put(orderedData) - .put(orderedSkyLight) - .put(orderedLight) .put(orderedHeightMap) .put(orderedBiomeColors) .put(this.blockEntities) diff --git a/src/main/java/cn/nukkit/level/format/anvil/ChunkSection.java b/src/main/java/cn/nukkit/level/format/anvil/ChunkSection.java index 01df616960..5c290cad7e 100644 --- a/src/main/java/cn/nukkit/level/format/anvil/ChunkSection.java +++ b/src/main/java/cn/nukkit/level/format/anvil/ChunkSection.java @@ -251,11 +251,9 @@ public boolean isEmpty() { @Override public byte[] getBytes() { - ByteBuffer buffer = ByteBuffer.allocate(10240); + ByteBuffer buffer = ByteBuffer.allocate(6144); byte[] blocks = new byte[4096]; byte[] data = new byte[2048]; - byte[] skyLight = new byte[2048]; - byte[] blockLight = new byte[2048]; for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { int i = (x << 7) | (z << 3); @@ -265,20 +263,12 @@ public byte[] getBytes() { int b1 = this.getBlockData(x, y, z); int b2 = this.getBlockData(x, y + 1, z); data[i | (y >> 1)] = (byte) ((b2 << 4) | b1); - b1 = this.getBlockSkyLight(x, y, z); - b2 = this.getBlockSkyLight(x, y + 1, z); - skyLight[i | (y >> 1)] = (byte) ((b2 << 4) | b1); - b1 = this.getBlockLight(x, y, z); - b2 = this.getBlockLight(x, y + 1, z); - blockLight[i | (y >> 1)] = (byte) ((b2 << 4) | b1); } } } return buffer .put(blocks) .put(data) - .put(skyLight) - .put(blockLight) .array(); } diff --git a/src/main/java/cn/nukkit/level/format/generic/EmptyChunkSection.java b/src/main/java/cn/nukkit/level/format/generic/EmptyChunkSection.java index b60a808572..c8be638548 100644 --- a/src/main/java/cn/nukkit/level/format/generic/EmptyChunkSection.java +++ b/src/main/java/cn/nukkit/level/format/generic/EmptyChunkSection.java @@ -3,7 +3,6 @@ import cn.nukkit.level.format.ChunkSection; import cn.nukkit.utils.ChunkException; -import java.nio.ByteBuffer; import java.util.Arrays; /** @@ -131,13 +130,7 @@ public boolean isEmpty() { @Override public byte[] getBytes() { - ByteBuffer buffer = ByteBuffer.allocate(10240); - byte[] skyLight = new byte[2048]; - Arrays.fill(skyLight, (byte) 0xff); - buffer.position(6144); - return buffer - .put(skyLight) - .array(); + return new byte[6144]; } @Override diff --git a/src/main/java/cn/nukkit/level/format/leveldb/LevelDB.java b/src/main/java/cn/nukkit/level/format/leveldb/LevelDB.java index a3e0c50e53..dce975c0fe 100644 --- a/src/main/java/cn/nukkit/level/format/leveldb/LevelDB.java +++ b/src/main/java/cn/nukkit/level/format/leveldb/LevelDB.java @@ -3,8 +3,8 @@ import cn.nukkit.Server; import cn.nukkit.blockentity.BlockEntity; import cn.nukkit.blockentity.BlockEntitySpawnable; -import cn.nukkit.level.Level; import cn.nukkit.level.GameRules; +import cn.nukkit.level.Level; import cn.nukkit.level.format.ChunkSection; import cn.nukkit.level.format.FullChunk; import cn.nukkit.level.format.LevelProvider; @@ -507,7 +507,7 @@ public void setSpawn(Vector3 pos) { this.levelData.putInt("SpawnY", (int) pos.y); this.levelData.putInt("SpawnZ", (int) pos.z); } - + @Override public GameRules getGamerules() { GameRules rules = new GameRules(); diff --git a/src/main/java/cn/nukkit/level/particle/FloatingTextParticle.java b/src/main/java/cn/nukkit/level/particle/FloatingTextParticle.java index dc1688b019..9e5566f414 100644 --- a/src/main/java/cn/nukkit/level/particle/FloatingTextParticle.java +++ b/src/main/java/cn/nukkit/level/particle/FloatingTextParticle.java @@ -2,13 +2,14 @@ import cn.nukkit.entity.Entity; import cn.nukkit.entity.data.EntityMetadata; -import cn.nukkit.entity.item.EntityItem; +import cn.nukkit.item.Item; import cn.nukkit.math.Vector3; -import cn.nukkit.network.protocol.AddEntityPacket; +import cn.nukkit.network.protocol.AddPlayerPacket; import cn.nukkit.network.protocol.DataPacket; import cn.nukkit.network.protocol.RemoveEntityPacket; import java.util.ArrayList; +import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; /** @@ -16,6 +17,8 @@ * Package cn.nukkit.level.particle in project Nukkit . */ public class FloatingTextParticle extends Particle { + + protected String text; protected String title; protected long entityId = -1; @@ -32,10 +35,18 @@ public FloatingTextParticle(Vector3 pos, String text, String title) { this.title = title; } + public String getText() { + return text; + } + public void setText(String text) { this.text = text; } + public String getTitle() { + return title; + } + public void setTitle(String title) { this.title = title; } @@ -66,10 +77,11 @@ public DataPacket[] encode() { } if (!this.invisible) { - AddEntityPacket pk = new AddEntityPacket(); + AddPlayerPacket pk = new AddPlayerPacket(); + pk.uuid = UUID.randomUUID(); + pk.username = ""; pk.entityUniqueId = this.entityId; pk.entityRuntimeId = this.entityId; - pk.type = EntityItem.NETWORK_ID; pk.x = (float) this.x; pk.y = (float) (this.y - 0.75); pk.z = (float) this.z; @@ -80,15 +92,18 @@ public DataPacket[] encode() { pk.pitch = 0; long flags = ( (1L << Entity.DATA_FLAG_CAN_SHOW_NAMETAG) | - (1L << Entity.DATA_FLAG_ALWAYS_SHOW_NAMETAG) | - (1L << Entity.DATA_FLAG_IMMOBILE) + (1L << Entity.DATA_FLAG_ALWAYS_SHOW_NAMETAG) | + (1L << Entity.DATA_FLAG_IMMOBILE) ); pk.metadata = new EntityMetadata() .putLong(Entity.DATA_FLAGS, flags) - .putString(Entity.DATA_NAMETAG, this.title + (!this.text.isEmpty() ? "\n" + this.text : "")); + .putString(Entity.DATA_NAMETAG, this.title + (!this.text.isEmpty() ? "\n" + this.text : "")) + .putLong(Entity.DATA_LEAD_HOLDER_EID,-1) + .putFloat(Entity.DATA_SCALE, 0.01f); //zero causes problems on debug builds? + pk.item = Item.get(Item.AIR); packets.add(pk); } return packets.stream().toArray(DataPacket[]::new); } -} +} \ No newline at end of file diff --git a/src/main/java/cn/nukkit/level/sound/LevelSoundEventSound.java b/src/main/java/cn/nukkit/level/sound/LevelSoundEventSound.java index 8d89a8c3b5..380c32b517 100644 --- a/src/main/java/cn/nukkit/level/sound/LevelSoundEventSound.java +++ b/src/main/java/cn/nukkit/level/sound/LevelSoundEventSound.java @@ -8,19 +8,19 @@ * @author Tee7even */ public class LevelSoundEventSound extends Sound { - protected final byte type; + protected final int type; protected int extraData; protected int pitch; - public LevelSoundEventSound(Vector3 pos, byte type) { + public LevelSoundEventSound(Vector3 pos, int type) { this(pos, type, -1, -1); } - public LevelSoundEventSound(Vector3 pos, byte type, int extraData) { + public LevelSoundEventSound(Vector3 pos, int type, int extraData) { this(pos, type, extraData, -1); } - public LevelSoundEventSound(Vector3 pos, byte type, int extraData, int pitch) { + public LevelSoundEventSound(Vector3 pos, int type, int extraData, int pitch) { super(pos.x, pos.y, pos.z); this.type = type; this.extraData = extraData; diff --git a/src/main/java/cn/nukkit/math/Angle.java b/src/main/java/cn/nukkit/math/Angle.java index 33a7fd61f9..549284a74a 100644 --- a/src/main/java/cn/nukkit/math/Angle.java +++ b/src/main/java/cn/nukkit/math/Angle.java @@ -1,6 +1,8 @@ package cn.nukkit.math; -import static java.lang.Math.*; +import java.util.Locale; + +import static java.lang.Math.PI; /** * Copyright 2017 lmlstarqaq @@ -8,142 +10,143 @@ */ public final class Angle implements Comparable { - public static Angle fromDegree(float floatDegree) { - return new Angle(floatDegree, true); - } - - public static Angle fromDegree(double doubleDegree) { - return new Angle(doubleDegree, true); - } - - public static Angle fromRadian(float floatRadian) { - return new Angle(floatRadian, false); - } - - public static Angle fromRadian(double doubleRadian) { - return new Angle(doubleRadian, false); - } - - public static Angle asin(double v) { - return fromRadian(Math.asin(v)); - } - - public static Angle acos(double v) { - return fromRadian(Math.acos(v)); - } - - public static Angle atan(double v) { - return fromRadian(Math.atan(v)); - } - - public double sin() { - return Math.sin(asDoubleRadian()); - } - - public double cos() { - return Math.cos(asDoubleRadian()); - } - - public double tan() { - return Math.tan(asDoubleRadian()); - } - - public float asFloatRadian() { - if (isOriginDouble) { - if (isDegree) return (float) (doubleValue * PI / 180.0); - else return (float) doubleValue; - } else { - if (isDegree) return floatValue * (float) PI / 180.0f; - else return floatValue; - } - } - - public double asDoubleRadian() { - if (isOriginDouble) { - if (isDegree) return doubleValue * PI / 180.0; - else return doubleValue; - } else { - if (isDegree) return floatValue * PI / 180.0; - else return floatValue; - } - } - - public float asFloatDegree() { - if (isOriginDouble) { - if (isDegree) return (float) doubleValue; - else return (float) (doubleValue * 180.0 / PI); - } else { - if (isDegree) return floatValue; - else return floatValue * 180.0f / (float) PI; - } - } - - public double asDoubleDegree() { - if (isOriginDouble) { - if (isDegree) return doubleValue; - else return doubleValue * 180.0 / PI; - } else { - if (isDegree) return floatValue ; - else return floatValue * 180.0 / PI; - } - } - - public static int compare(Angle a, Angle b) { - return a.compareTo(b); - } + public static Angle fromDegree(float floatDegree) { + return new Angle(floatDegree, true); + } + + public static Angle fromDegree(double doubleDegree) { + return new Angle(doubleDegree, true); + } + + public static Angle fromRadian(float floatRadian) { + return new Angle(floatRadian, false); + } + + public static Angle fromRadian(double doubleRadian) { + return new Angle(doubleRadian, false); + } + + public static Angle asin(double v) { + return fromRadian(Math.asin(v)); + } + + public static Angle acos(double v) { + return fromRadian(Math.acos(v)); + } + + public static Angle atan(double v) { + return fromRadian(Math.atan(v)); + } + + public double sin() { + return Math.sin(asDoubleRadian()); + } + + public double cos() { + return Math.cos(asDoubleRadian()); + } + + public double tan() { + return Math.tan(asDoubleRadian()); + } + + public float asFloatRadian() { + if (isOriginDouble) { + if (isDegree) return (float) (doubleValue * PI / 180.0); + else return (float) doubleValue; + } else { + if (isDegree) return floatValue * (float) PI / 180.0f; + else return floatValue; + } + } + + public double asDoubleRadian() { + if (isOriginDouble) { + if (isDegree) return doubleValue * PI / 180.0; + else return doubleValue; + } else { + if (isDegree) return floatValue * PI / 180.0; + else return floatValue; + } + } + + public float asFloatDegree() { + if (isOriginDouble) { + if (isDegree) return (float) doubleValue; + else return (float) (doubleValue * 180.0 / PI); + } else { + if (isDegree) return floatValue; + else return floatValue * 180.0f / (float) PI; + } + } + + public double asDoubleDegree() { + if (isOriginDouble) { + if (isDegree) return doubleValue; + else return doubleValue * 180.0 / PI; + } else { + if (isDegree) return floatValue; + else return floatValue * 180.0 / PI; + } + } + + public static int compare(Angle a, Angle b) { + return a.compareTo(b); + } /* -- Override -- */ - @Override - public String toString() { - return String.format("Angle[%s, %f%s = %f%s] [%d]", - isOriginDouble ? "Double" : "Float", - isOriginDouble ? doubleValue : floatValue, - isDegree ? "deg" : "rad", - isDegree ? (isOriginDouble ? asDoubleRadian() : asFloatRadian()) : - (isOriginDouble ? asDoubleDegree() : asFloatDegree()) , - isDegree ? "rad" : "deg", - hashCode() - ); - } - - @Override - public int compareTo(Angle o) { - return Double.compare(asDoubleRadian(), o.asDoubleRadian()); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Angle && this.compareTo((Angle) obj) == 0; - } - - @Override - public int hashCode() { - int hash; - if (isOriginDouble) hash = Double.hashCode(doubleValue); - else hash = Float.hashCode(floatValue); - if (isDegree) hash = hash ^ 0xABCD1234; - return hash; - } + @Override + public String toString() { + return String.format(Locale.ROOT, + "Angle[%s, %f%s = %f%s] [%d]", + isOriginDouble ? "Double" : "Float", + isOriginDouble ? doubleValue : floatValue, + isDegree ? "deg" : "rad", + isDegree ? (isOriginDouble ? asDoubleRadian() : asFloatRadian()) : + (isOriginDouble ? asDoubleDegree() : asFloatDegree()), + isDegree ? "rad" : "deg", + hashCode() + ); + } + + @Override + public int compareTo(Angle o) { + return Double.compare(asDoubleRadian(), o.asDoubleRadian()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Angle && this.compareTo((Angle) obj) == 0; + } + + @Override + public int hashCode() { + int hash; + if (isOriginDouble) hash = Double.hashCode(doubleValue); + else hash = Float.hashCode(floatValue); + if (isDegree) hash = hash ^ 0xABCD1234; + return hash; + } /* -- Internal Part -- */ - private final float floatValue; - private final double doubleValue; - private final boolean isDegree, isOriginDouble; - - private Angle(float floatValue, boolean isDegree) { - this.isOriginDouble = false; - this.floatValue = floatValue; - this.doubleValue = 0.0; - this.isDegree = isDegree; - } - - private Angle(double doubleValue, boolean isDegree) { - this.isOriginDouble = true; - this.floatValue = 0.0f; - this.doubleValue = doubleValue; - this.isDegree = isDegree; - } + private final float floatValue; + private final double doubleValue; + private final boolean isDegree, isOriginDouble; + + private Angle(float floatValue, boolean isDegree) { + this.isOriginDouble = false; + this.floatValue = floatValue; + this.doubleValue = 0.0; + this.isDegree = isDegree; + } + + private Angle(double doubleValue, boolean isDegree) { + this.isOriginDouble = true; + this.floatValue = 0.0f; + this.doubleValue = doubleValue; + this.isDegree = isDegree; + } } diff --git a/src/main/java/cn/nukkit/math/BlockVector3.java b/src/main/java/cn/nukkit/math/BlockVector3.java index 379dd35c29..98d2d4544b 100644 --- a/src/main/java/cn/nukkit/math/BlockVector3.java +++ b/src/main/java/cn/nukkit/math/BlockVector3.java @@ -216,4 +216,12 @@ public BlockVector3 clone() { return null; } } + + public Vector3 asVector3() { + return new Vector3(this.x, this.y, this.z); + } + + public Vector3f asVector3f() { + return new Vector3f(this.x, this.y, this.z); + } } diff --git a/src/main/java/cn/nukkit/math/NukkitMath.java b/src/main/java/cn/nukkit/math/NukkitMath.java index cd92c76e3b..1858b57937 100644 --- a/src/main/java/cn/nukkit/math/NukkitMath.java +++ b/src/main/java/cn/nukkit/math/NukkitMath.java @@ -46,4 +46,20 @@ public static double round(double d, int precision) { return ((double) Math.round(d * Math.pow(10, precision))) / Math.pow(10, precision); } + public static double clamp(double check, double min, double max) { + return check > max ? max : (check < min ? min : check); + } + + public static double getDirection(double d0, double d1) { + if (d0 < 0.0D) { + d0 = -d0; + } + + if (d1 < 0.0D) { + d1 = -d1; + } + + return d0 > d1 ? d0 : d1; + } + } diff --git a/src/main/java/cn/nukkit/math/Vector3.java b/src/main/java/cn/nukkit/math/Vector3.java index e80a3ddbcd..75c7695e6f 100644 --- a/src/main/java/cn/nukkit/math/Vector3.java +++ b/src/main/java/cn/nukkit/math/Vector3.java @@ -346,4 +346,12 @@ public Vector3 clone() { return null; } } + + public Vector3f asVector3f() { + return new Vector3f((float) this.x, (float) this.y, (float) this.z); + } + + public BlockVector3 asBlockVector3() { + return new BlockVector3(this.getFloorX(), this.getFloorY(), this.getFloorZ()); + } } diff --git a/src/main/java/cn/nukkit/math/Vector3f.java b/src/main/java/cn/nukkit/math/Vector3f.java index 3f1eba2461..d77200a278 100644 --- a/src/main/java/cn/nukkit/math/Vector3f.java +++ b/src/main/java/cn/nukkit/math/Vector3f.java @@ -324,4 +324,12 @@ public Vector3f clone() { return null; } } + + public Vector3 asVector3() { + return new Vector3(this.x, this.y, this.z); + } + + public BlockVector3 asBlockVector3() { + return new BlockVector3(getFloorX(), getFloorY(), getFloorZ()); + } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java b/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java index 9203a4fc46..6902856b74 100644 --- a/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java +++ b/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java @@ -10,6 +10,8 @@ public interface AdvancedSourceInterface extends SourceInterface { void blockAddress(String address, int timeout); + void unblockAddress(String address); + void setNetwork(Network network); void sendRawPacket(String address, int port, byte[] payload); diff --git a/src/main/java/cn/nukkit/network/Network.java b/src/main/java/cn/nukkit/network/Network.java index 1c937272c7..f76f31530d 100644 --- a/src/main/java/cn/nukkit/network/Network.java +++ b/src/main/java/cn/nukkit/network/Network.java @@ -41,6 +41,7 @@ public class Network { private double download = 0; private String name; + private String subName; public Network(Server server) { this.registerPackets(); @@ -91,7 +92,7 @@ public void registerInterface(SourceInterface interfaz) { this.advancedInterfaces.add((AdvancedSourceInterface) interfaz); ((AdvancedSourceInterface) interfaz).setNetwork(this); } - interfaz.setName(this.name); + interfaz.setName(this.name + "!@#" + this.subName); } public void unregisterInterface(SourceInterface interfaz) { @@ -108,9 +109,17 @@ public String getName() { return name; } + public String getSubName() { + return subName; + } + + public void setSubName(String subName) { + this.subName = subName; + } + public void updateName() { for (SourceInterface interfaz : this.interfaces) { - interfaz.setName(this.name); + interfaz.setName(this.name + "!@#" + this.subName); } } @@ -140,10 +149,16 @@ public void processBatch(BatchPacket packet, Player player) { DataPacket pk; if ((pk = this.getPacket(buf[0])) != null) { - pk.setBuffer(buf, 1); - - pk.decode(); - + pk.setBuffer(buf, 3); //skip 2 more bytes + try { + pk.decode(); + } catch (Exception e) { + //TODO: Remove it in future! Compatible with 1.1.x, then we can do something like kicking... + if (pk.pid() == ProtocolInfo.LOGIN_PACKET) { + pk.setBuffer(buf, 1); + pk.decode(); + } else throw e; + } packets.add(pk); } } @@ -166,20 +181,7 @@ public void processBatch(BatchPacket packet, Player player) { */ public void processPackets(Player player, List packets) { if (packets.isEmpty()) return; - List filter = new ArrayList<>(); - for (DataPacket packet : packets) { - switch (packet.pid()) { - case ProtocolInfo.USE_ITEM_PACKET: - // Prevent double fire of PlayerInteractEvent - if (!filter.contains(ProtocolInfo.USE_ITEM_PACKET)) { - player.handleDataPacket(packet); - filter.add(ProtocolInfo.USE_ITEM_PACKET); - } - break; - default: - player.handleDataPacket(packet); - } - } + packets.forEach(player::handleDataPacket); } @@ -211,13 +213,17 @@ public void blockAddress(String address, int timeout) { } } + public void unblockAddress(String address) { + for (AdvancedSourceInterface interfaz : this.advancedInterfaces) { + interfaz.unblockAddress(address); + } + } + private void registerPackets() { this.packetPool = new Class[256]; this.registerPacket(ProtocolInfo.ADD_ENTITY_PACKET, AddEntityPacket.class); - this.registerPacket(ProtocolInfo.ADD_HANGING_ENTITY_PACKET, AddHangingEntityPacket.class); this.registerPacket(ProtocolInfo.ADD_ITEM_ENTITY_PACKET, AddItemEntityPacket.class); - this.registerPacket(ProtocolInfo.ADD_ITEM_PACKET, AddItemPacket.class); this.registerPacket(ProtocolInfo.ADD_PAINTING_PACKET, AddPaintingPacket.class); this.registerPacket(ProtocolInfo.ADD_PLAYER_PACKET, AddPlayerPacket.class); this.registerPacket(ProtocolInfo.ADVENTURE_SETTINGS_PACKET, AdventureSettingsPacket.class); @@ -231,16 +237,13 @@ private void registerPackets() { this.registerPacket(ProtocolInfo.CHANGE_DIMENSION_PACKET, ChangeDimensionPacket.class); this.registerPacket(ProtocolInfo.CHUNK_RADIUS_UPDATED_PACKET, ChunkRadiusUpdatedPacket.class); this.registerPacket(ProtocolInfo.CLIENTBOUND_MAP_ITEM_DATA_PACKET, ClientboundMapItemDataPacket.class); - this.registerPacket(ProtocolInfo.COMMAND_STEP_PACKET, CommandStepPacket.class); + this.registerPacket(ProtocolInfo.COMMAND_REQUEST_PACKET, CommandRequestPacket.class); this.registerPacket(ProtocolInfo.CONTAINER_CLOSE_PACKET, ContainerClosePacket.class); this.registerPacket(ProtocolInfo.CONTAINER_OPEN_PACKET, ContainerOpenPacket.class); - this.registerPacket(ProtocolInfo.CONTAINER_SET_CONTENT_PACKET, ContainerSetContentPacket.class); this.registerPacket(ProtocolInfo.CONTAINER_SET_DATA_PACKET, ContainerSetDataPacket.class); - this.registerPacket(ProtocolInfo.CONTAINER_SET_SLOT_PACKET, ContainerSetSlotPacket.class); this.registerPacket(ProtocolInfo.CRAFTING_DATA_PACKET, CraftingDataPacket.class); this.registerPacket(ProtocolInfo.CRAFTING_EVENT_PACKET, CraftingEventPacket.class); this.registerPacket(ProtocolInfo.DISCONNECT_PACKET, DisconnectPacket.class); - this.registerPacket(ProtocolInfo.DROP_ITEM_PACKET, DropItemPacket.class); this.registerPacket(ProtocolInfo.ENTITY_EVENT_PACKET, EntityEventPacket.class); this.registerPacket(ProtocolInfo.ENTITY_FALL_PACKET, EntityFallPacket.class); this.registerPacket(ProtocolInfo.EXPLODE_PACKET, ExplodePacket.class); @@ -248,7 +251,9 @@ private void registerPackets() { this.registerPacket(ProtocolInfo.GAME_RULES_CHANGED_PACKET, GameRulesChangedPacket.class); this.registerPacket(ProtocolInfo.HURT_ARMOR_PACKET, HurtArmorPacket.class); this.registerPacket(ProtocolInfo.INTERACT_PACKET, InteractPacket.class); - this.registerPacket(ProtocolInfo.INVENTORY_ACTION_PACKET, InventoryActionPacket.class); + this.registerPacket(ProtocolInfo.INVENTORY_CONTENT_PACKET, InventoryContentPacket.class); + this.registerPacket(ProtocolInfo.INVENTORY_SLOT_PACKET, InventorySlotPacket.class); + this.registerPacket(ProtocolInfo.INVENTORY_TRANSACTION_PACKET, InventoryTransactionPacket.class); this.registerPacket(ProtocolInfo.ITEM_FRAME_DROP_ITEM_PACKET, ItemFrameDropItemPacket.class); this.registerPacket(ProtocolInfo.LEVEL_EVENT_PACKET, LevelEventPacket.class); this.registerPacket(ProtocolInfo.LEVEL_SOUND_EVENT_PACKET, LevelSoundEventPacket.class); @@ -256,16 +261,17 @@ private void registerPackets() { this.registerPacket(ProtocolInfo.MAP_INFO_REQUEST_PACKET, MapInfoRequestPacket.class); this.registerPacket(ProtocolInfo.MOB_ARMOR_EQUIPMENT_PACKET, MobArmorEquipmentPacket.class); this.registerPacket(ProtocolInfo.MOB_EQUIPMENT_PACKET, MobEquipmentPacket.class); + this.registerPacket(ProtocolInfo.MODAL_FORM_REQUEST_PACKET, ModalFormRequestPacket.class); + this.registerPacket(ProtocolInfo.MODAL_FORM_RESPONSE_PACKET, ModalFormResponsePacket.class); this.registerPacket(ProtocolInfo.MOVE_ENTITY_PACKET, MoveEntityPacket.class); this.registerPacket(ProtocolInfo.MOVE_PLAYER_PACKET, MovePlayerPacket.class); this.registerPacket(ProtocolInfo.PLAYER_ACTION_PACKET, PlayerActionPacket.class); this.registerPacket(ProtocolInfo.PLAYER_INPUT_PACKET, PlayerInputPacket.class); this.registerPacket(ProtocolInfo.PLAYER_LIST_PACKET, PlayerListPacket.class); + this.registerPacket(ProtocolInfo.PLAYER_HOTBAR_PACKET, PlayerListPacket.class); this.registerPacket(ProtocolInfo.PLAY_SOUND_PACKET, PlaySoundPacket.class); this.registerPacket(ProtocolInfo.PLAY_STATUS_PACKET, PlayStatusPacket.class); - this.registerPacket(ProtocolInfo.REMOVE_BLOCK_PACKET, RemoveBlockPacket.class); this.registerPacket(ProtocolInfo.REMOVE_ENTITY_PACKET, RemoveEntityPacket.class); - this.registerPacket(ProtocolInfo.REPLACE_ITEM_IN_SLOT_PACKET, ReplaceItemInSlotPacket.class); this.registerPacket(ProtocolInfo.REQUEST_CHUNK_RADIUS_PACKET, RequestChunkRadiusPacket.class); this.registerPacket(ProtocolInfo.RESOURCE_PACKS_INFO_PACKET, ResourcePacksInfoPacket.class); this.registerPacket(ProtocolInfo.RESOURCE_PACK_STACK_PACKET, ResourcePackStackPacket.class); @@ -285,13 +291,14 @@ private void registerPackets() { this.registerPacket(ProtocolInfo.SET_SPAWN_POSITION_PACKET, SetSpawnPositionPacket.class); this.registerPacket(ProtocolInfo.SET_TITLE_PACKET, SetTitlePacket.class); this.registerPacket(ProtocolInfo.SET_TIME_PACKET, SetTimePacket.class); + this.registerPacket(ProtocolInfo.SERVER_SETTINGS_REQUEST_PACKET, ServerSettingsRequestPacket.class); + this.registerPacket(ProtocolInfo.SERVER_SETTINGS_RESPONSE_PACKET, ServerSettingsResponsePacket.class); this.registerPacket(ProtocolInfo.SHOW_CREDITS_PACKET, ShowCreditsPacket.class); this.registerPacket(ProtocolInfo.SPAWN_EXPERIENCE_ORB_PACKET, SpawnExperienceOrbPacket.class); this.registerPacket(ProtocolInfo.START_GAME_PACKET, StartGamePacket.class); this.registerPacket(ProtocolInfo.TAKE_ITEM_ENTITY_PACKET, TakeItemEntityPacket.class); this.registerPacket(ProtocolInfo.TEXT_PACKET, TextPacket.class); this.registerPacket(ProtocolInfo.UPDATE_BLOCK_PACKET, UpdateBlockPacket.class); - this.registerPacket(ProtocolInfo.USE_ITEM_PACKET, UseItemPacket.class); this.registerPacket(ProtocolInfo.UPDATE_TRADE_PACKET, UpdateTradePacket.class); } } diff --git a/src/main/java/cn/nukkit/network/RakNetInterface.java b/src/main/java/cn/nukkit/network/RakNetInterface.java index 745d019763..e5df71d4d0 100644 --- a/src/main/java/cn/nukkit/network/RakNetInterface.java +++ b/src/main/java/cn/nukkit/network/RakNetInterface.java @@ -184,6 +184,11 @@ public void blockAddress(String address, int timeout) { this.handler.blockAddress(address, timeout); } + @Override + public void unblockAddress(String address) { + this.handler.unblockAddress(address); + } + @Override public void handleRaw(String address, int port, byte[] payload) { this.server.handlePacket(address, port, payload); @@ -205,13 +210,16 @@ public void notifyACK(String identifier, int identifierACK) { @Override public void setName(String name) { QueryRegenerateEvent info = this.server.getQueryInformation(); - + String[] names = name.split("!@#"); //Split double names within the program this.handler.sendOption("name", - "MCPE;" + Utils.rtrim(name.replace(";", "\\;"), '\\') + ";" + + "MCPE;" + Utils.rtrim(names[0].replace(";", "\\;"), '\\') + ";" + ProtocolInfo.CURRENT_PROTOCOL + ";" + ProtocolInfo.MINECRAFT_VERSION_NETWORK + ";" + info.getPlayerCount() + ";" + - info.getMaxPlayerCount()); + info.getMaxPlayerCount() + ";" + + this.server.getServerUniqueId().toString() + ";" + + (names.length > 1 ? Utils.rtrim(names[1].replace(";", "\\;"), '\\') : "") + ";" + + Server.getGamemodeString(this.server.getDefaultGamemode(), true) + ";"); } public void setPortCheck(boolean value) { diff --git a/src/main/java/cn/nukkit/network/protocol/AddBehaviorTreePacket.java b/src/main/java/cn/nukkit/network/protocol/AddBehaviorTreePacket.java new file mode 100644 index 0000000000..ce3fa07196 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/AddBehaviorTreePacket.java @@ -0,0 +1,22 @@ +package cn.nukkit.network.protocol; + +public class AddBehaviorTreePacket extends DataPacket { + + public String unknown; + + @Override + public byte pid() { + return ProtocolInfo.ADD_BEHAVIOR_TREE_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + this.reset(); + this.putString(unknown); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java b/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java index 98ec3b0f3e..c0df2caf39 100644 --- a/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AddEntityPacket.java @@ -39,8 +39,8 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.entityUniqueId); - this.putVarLong(this.entityRuntimeId); + this.putEntityUniqueId(this.entityUniqueId); + this.putEntityRuntimeId(this.entityRuntimeId); this.putUnsignedVarInt(this.type); this.putVector3f(this.x, this.y, this.z); this.putVector3f(this.speedX, this.speedY, this.speedZ); diff --git a/src/main/java/cn/nukkit/network/protocol/AddHangingEntityPacket.java b/src/main/java/cn/nukkit/network/protocol/AddHangingEntityPacket.java deleted file mode 100644 index 2c0c838540..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/AddHangingEntityPacket.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.math.BlockVector3; - -public class AddHangingEntityPacket extends DataPacket { - public static final byte NETWORK_ID = ProtocolInfo.ADD_HANGING_ENTITY_PACKET; - - @Override - public byte pid() { - return NETWORK_ID; - } - - public long entityUniqueId; - public long entityRuntimeId; - public int x; - public int y; - public int z; - public int unknown; - - @Override - public void decode() { - this.entityUniqueId = this.getVarLong(); - this.entityRuntimeId = this.getVarLong(); - BlockVector3 v3 = this.getBlockCoords(); - this.x = v3.x; - this.y = v3.y; - this.z = v3.z; - this.unknown = this.getVarInt(); - } - - @Override - public void encode() { - this.reset(); - this.putVarLong(this.entityUniqueId); - this.putVarLong(this.entityRuntimeId); - this.putBlockCoords(this.x, this.y, this.z); - this.putVarInt(this.unknown); - } -} diff --git a/src/main/java/cn/nukkit/network/protocol/AddItemEntityPacket.java b/src/main/java/cn/nukkit/network/protocol/AddItemEntityPacket.java index c22085541c..9a6904e7f5 100644 --- a/src/main/java/cn/nukkit/network/protocol/AddItemEntityPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AddItemEntityPacket.java @@ -35,8 +35,8 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.entityUniqueId); - this.putVarLong(this.entityRuntimeId); + this.putEntityUniqueId(this.entityUniqueId); + this.putEntityRuntimeId(this.entityRuntimeId); this.putSlot(this.item); this.putVector3f(this.x, this.y, this.z); this.putVector3f(this.speedX, this.speedY, this.speedZ); diff --git a/src/main/java/cn/nukkit/network/protocol/AddItemPacket.java b/src/main/java/cn/nukkit/network/protocol/AddItemPacket.java deleted file mode 100644 index 413b6b84f2..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/AddItemPacket.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.item.Item; - -public class AddItemPacket extends DataPacket { - public static final byte NETWORK_ID = ProtocolInfo.ADD_ITEM_PACKET; - - @Override - public byte pid() { - return NETWORK_ID; - } - - public Item item; - - @Override - public void decode() { - - } - - @Override - public void encode() { - this.reset(); - this.putSlot(item); - } -} diff --git a/src/main/java/cn/nukkit/network/protocol/AddPaintingPacket.java b/src/main/java/cn/nukkit/network/protocol/AddPaintingPacket.java index ae7e86c4fb..3c07331314 100644 --- a/src/main/java/cn/nukkit/network/protocol/AddPaintingPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AddPaintingPacket.java @@ -23,9 +23,9 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.entityUniqueId); - this.putVarLong(this.entityRuntimeId); - this.putBlockCoords(this.x, this.y, this.z); + this.putEntityUniqueId(this.entityUniqueId); + this.putEntityRuntimeId(this.entityRuntimeId); + this.putBlockVector3(this.x, this.y, this.z); this.putVarInt(this.direction); this.putString(this.title); } diff --git a/src/main/java/cn/nukkit/network/protocol/AddPlayerPacket.java b/src/main/java/cn/nukkit/network/protocol/AddPlayerPacket.java index 3accf53984..6c211d3db6 100644 --- a/src/main/java/cn/nukkit/network/protocol/AddPlayerPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AddPlayerPacket.java @@ -43,8 +43,8 @@ public void encode() { this.reset(); this.putUUID(this.uuid); this.putString(this.username); - this.putVarLong(this.entityUniqueId); - this.putVarLong(this.entityRuntimeId); + this.putEntityUniqueId(this.entityUniqueId); + this.putEntityRuntimeId(this.entityRuntimeId); this.putVector3f(this.x, this.y, this.z); this.putVector3f(this.speedX, this.speedY, this.speedZ); this.putLFloat(this.pitch); diff --git a/src/main/java/cn/nukkit/network/protocol/AdventureSettingsPacket.java b/src/main/java/cn/nukkit/network/protocol/AdventureSettingsPacket.java index 5d68d7f1a2..16c3ff6802 100644 --- a/src/main/java/cn/nukkit/network/protocol/AdventureSettingsPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AdventureSettingsPacket.java @@ -1,73 +1,97 @@ package cn.nukkit.network.protocol; +import cn.nukkit.Player; + /** * @author Nukkit Project Team */ public class AdventureSettingsPacket extends DataPacket { + public static final byte NETWORK_ID = ProtocolInfo.ADVENTURE_SETTINGS_PACKET; - public boolean worldImmutable; - public boolean noPvp; - public boolean noPvm; - public boolean noMvp; - - public boolean autoJump; - public boolean allowFlight; - public boolean noClip; - public boolean worldBuilder; - public boolean isFlying; - public boolean muted; - - /* - bit mask | flag name - 0x00000001 world_immutable - 0x00000002 no_pvp - 0x00000004 no_pvm - 0x00000008 no_mvp - 0x00000010 ? - 0x00000020 auto_jump - 0x00000040 allow_fly - 0x00000080 noclip - 0x00000100 ? - 0x00000200 is_flying - */ - - public int flags = 0; - public int userPermission; + public static final int PERMISSION_NORMAL = 0; + public static final int PERMISSION_OPERATOR = 1; + public static final int PERMISSION_HOST = 2; + public static final int PERMISSION_AUTOMATION = 3; + public static final int PERMISSION_ADMIN = 4; + //TODO: check level 3 + /** + * This constant is used to identify flags that should be set on the second field. In a sensible world, these + * flags would all be set on the same packet field, but as of MCPE 1.2, the new abilities flags have for some + * reason been assigned a separate field. + */ + public static final int BITFLAG_SECOND_SET = 1 << 16; + + public static final int WORLD_IMMUTABLE = 0x01; + public static final int NO_PVP = 0x02; + public static final int AUTO_JUMP = 0x20; + public static final int ALLOW_FLIGHT = 0x40; + public static final int NO_CLIP = 0x80; + public static final int WORLD_BUILDER = 0x100; + public static final int FLYING = 0x200; + public static final int MUTED = 0x400; + public static final int BUILD_AND_MINE = 0x01 | BITFLAG_SECOND_SET; + public static final int DOORS_AND_SWITCHES = 0x02 | BITFLAG_SECOND_SET; + public static final int OPEN_CONTAINERS = 0x04 | BITFLAG_SECOND_SET; + public static final int ATTACK_PLAYERS = 0x08 | BITFLAG_SECOND_SET; + public static final int ATTACK_MOBS = 0x10 | BITFLAG_SECOND_SET; + public static final int OPERATOR = 0x20 | BITFLAG_SECOND_SET; + public static final int TELEPORT = 0x80 | BITFLAG_SECOND_SET; + + public long flags = 0; + + public long commandPermission = PERMISSION_NORMAL; + + public long flags2 = -1; + + public long playerPermission = Player.PERMISSION_MEMBER; + + public long customFlags; //... + + public long entityUniqueId; //This is a little-endian long, NOT a var-long. (WTF Mojang) - @Override public void decode() { - this.flags = (int) this.getUnsignedVarInt(); - this.userPermission = (int) this.getUnsignedVarInt(); - this.worldImmutable = (this.flags & 1) != 0; - this.noPvp = (this.flags & (1 << 1)) != 0; - this.noPvm = (this.flags & (1 << 2)) != 0; - this.noMvp = (this.flags & (1 << 3)) != 0; - - this.autoJump = (this.flags & (1 << 5)) != 0; - this.allowFlight = (this.flags & (1 << 6)) != 0; - this.noClip = (this.flags & (1 << 7)) != 0; - this.worldBuilder = (this.flags & (1 << 8)) != 0; - this.isFlying = (this.flags & (1 << 9)) != 0; - this.muted = (this.flags & (1 << 10)) != 0; + this.flags = getUnsignedVarInt(); + this.commandPermission = getUnsignedVarInt(); + this.flags2 = getUnsignedVarInt(); + this.playerPermission = getUnsignedVarInt(); + this.customFlags = getUnsignedVarInt(); + this.entityUniqueId = getLLong(); } - @Override public void encode() { this.reset(); - if (this.worldImmutable) this.flags |= 1; - if (this.noPvp) this.flags |= 1 << 1; - if (this.noPvm) this.flags |= 1 << 2; - if (this.noMvp) this.flags |= 1 << 3; - - if (this.autoJump) this.flags |= 1 << 5; - if (this.allowFlight) this.flags |= 1 << 6; - if (this.noClip) this.flags |= 1 << 7; - if (this.worldBuilder) this.flags |= 1 << 8; - if (this.isFlying) this.flags |= 1 << 9; - if (this.muted) this.flags |= 1 << 10; this.putUnsignedVarInt(this.flags); - this.putUnsignedVarInt(this.userPermission); + this.putUnsignedVarInt(this.commandPermission); + this.putUnsignedVarInt(this.flags2); + this.putUnsignedVarInt(this.playerPermission); + this.putUnsignedVarInt(this.customFlags); + this.putLLong(this.entityUniqueId); + } + + public boolean getFlag(int flag) { + if ((flag & BITFLAG_SECOND_SET) != 0) { + return (this.flags2 & flag) != 0; + } + return (this.flags & flag) != 0; + } + + public void setFlag(int flag, boolean value) { + boolean flags = (flag & BITFLAG_SECOND_SET) != 0; + + if (value) { + if (flags) { + this.flags2 |= flag; + } else { + this.flags |= flag; + } + } else { + if (flags) { + this.flags2 &= ~flag; + } else { + this.flags &= ~flag; + } + } } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/AnimatePacket.java b/src/main/java/cn/nukkit/network/protocol/AnimatePacket.java index 8a7efec1f3..87abc99036 100644 --- a/src/main/java/cn/nukkit/network/protocol/AnimatePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AnimatePacket.java @@ -13,8 +13,8 @@ public class AnimatePacket extends DataPacket { @Override public void decode() { - this.action = (int) this.getUnsignedVarInt(); - this.eid = getVarLong(); + this.action = this.getVarInt(); + this.eid = getEntityRuntimeId(); if ((this.action & 0x80) != 0) { this.unknown = this.getLFloat(); } @@ -23,8 +23,8 @@ public void decode() { @Override public void encode() { this.reset(); - this.putUnsignedVarInt(this.action); - this.putVarLong(this.eid); + this.putVarInt(this.action); + this.putEntityRuntimeId(this.eid); if ((this.action & 0x80) != 0) { this.putLFloat(this.unknown); } diff --git a/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java b/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java index 4719a2ff05..02db5e03bc 100644 --- a/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/AvailableCommandsPacket.java @@ -1,5 +1,14 @@ package cn.nukkit.network.protocol; +import cn.nukkit.command.data.CommandDataVersions; +import cn.nukkit.command.data.CommandOverload; +import cn.nukkit.command.data.CommandParameter; +import cn.nukkit.utils.BinaryStream; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Map; + /** * author: MagicDroidX * Nukkit Project @@ -7,8 +16,25 @@ public class AvailableCommandsPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.AVAILABLE_COMMANDS_PACKET; - public String commands; //JSON-encoded command data - public String unknown = ""; + public Map commands; + + public static final int ARG_FLAG_VALID = 0x100000; + public static final int ARG_TYPE_INT = 0x01; + public static final int ARG_TYPE_FLOAT = 0x02; + public static final int ARG_TYPE_VALUE = 0x03; + public static final int ARG_TYPE_TARGET = 0x04; + + public static final int ARG_TYPE_STRING = 0x0d; + public static final int ARG_TYPE_POSITION = 0x0e; + + public static final int ARG_TYPE_RAWTEXT = 0x11; + public static final int ARG_TYPE_TEXT = 0x13; + + public static final int ARG_TYPE_JSON = 0x16; + public static final int ARG_TYPE_COMMAND = 0x1d; + + public static final int ARG_FLAG_ENUM = 0x200000; + public static final int ARG_FLAG_TEMPLATE = 0x01000000; @Override public byte pid() { @@ -19,11 +45,58 @@ public byte pid() { public void decode() { } + int aliasCommands = 0; + @Override public void encode() { this.reset(); - this.putString(this.commands); - this.putString(this.unknown); + BinaryStream commandsStream = new BinaryStream(); + this.commands.forEach((name, versions) -> { + if (name.equals("help")) return; + ArrayList aliases = new ArrayList<>(); + aliases.addAll(Arrays.asList(versions.versions.get(0).aliases)); + aliases.add(name); + for (String alias : aliases) { + commandsStream.putString(alias); + commandsStream.putString(versions.versions.get(0).description); + commandsStream.putByte((byte) 0); + commandsStream.putByte((byte) 0); + commandsStream.putLInt(-1); + commandsStream.putUnsignedVarInt(versions.versions.get(0).overloads.size()); + for (CommandOverload overload : versions.versions.get(0).overloads.values()) { + commandsStream.putUnsignedVarInt(overload.input.parameters.length); + for (CommandParameter parameter : overload.input.parameters) { + commandsStream.putString(parameter.name); + commandsStream.putLInt(ARG_FLAG_VALID | getFlag(parameter.type)); + commandsStream.putBoolean(parameter.optional); + } + } + } + aliasCommands += (aliases.size() - 1); + }); + + this.putUnsignedVarInt(0); + this.putUnsignedVarInt(0); + this.putUnsignedVarInt(0); + this.putUnsignedVarInt(this.commands.size() + aliasCommands); + this.put(commandsStream.getBuffer()); } + int getFlag(String type) { + switch (type) { + case CommandParameter.ARG_TYPE_BLOCK_POS: + return ARG_TYPE_POSITION; + case CommandParameter.ARG_TYPE_INT: + return ARG_TYPE_INT; + case CommandParameter.ARG_TYPE_PLAYER: + return ARG_TYPE_TARGET; + case CommandParameter.ARG_TYPE_RAW_TEXT: + return ARG_TYPE_RAWTEXT; + case CommandParameter.ARG_TYPE_STRING: + case CommandParameter.ARG_TYPE_STRING_ENUM: + return ARG_TYPE_STRING; + default: + return 0; + } + } } diff --git a/src/main/java/cn/nukkit/network/protocol/BlockEntityDataPacket.java b/src/main/java/cn/nukkit/network/protocol/BlockEntityDataPacket.java index 92a69abb21..84908c1e4e 100644 --- a/src/main/java/cn/nukkit/network/protocol/BlockEntityDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/BlockEntityDataPacket.java @@ -21,7 +21,7 @@ public byte pid() { @Override public void decode() { - BlockVector3 v = this.getBlockCoords(); + BlockVector3 v = this.getBlockVector3(); this.x = v.x; this.y = v.y; this.z = v.z; @@ -31,7 +31,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putBlockCoords(this.x, this.y, this.z); + this.putBlockVector3(this.x, this.y, this.z); this.put(this.namedTag); } } \ No newline at end of file diff --git a/src/main/java/cn/nukkit/network/protocol/BlockEventPacket.java b/src/main/java/cn/nukkit/network/protocol/BlockEventPacket.java index 747cf939c3..411464c220 100644 --- a/src/main/java/cn/nukkit/network/protocol/BlockEventPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/BlockEventPacket.java @@ -26,7 +26,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putBlockCoords(this.x, this.y, this.z); + this.putBlockVector3(this.x, this.y, this.z); this.putVarInt(this.case1); this.putVarInt(this.case2); } diff --git a/src/main/java/cn/nukkit/network/protocol/BlockPickRequestPacket.java b/src/main/java/cn/nukkit/network/protocol/BlockPickRequestPacket.java index 8df148f834..a2cbc823f1 100644 --- a/src/main/java/cn/nukkit/network/protocol/BlockPickRequestPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/BlockPickRequestPacket.java @@ -9,6 +9,7 @@ public class BlockPickRequestPacket extends DataPacket { public int x; public int y; public int z; + public boolean addUserData; public int selectedSlot; @Override @@ -18,10 +19,11 @@ public byte pid() { @Override public void decode() { - BlockVector3 v = this.getBlockCoords(); + BlockVector3 v = this.getSignedBlockPosition(); this.x = v.x; this.y = v.y; this.z = v.z; + this.putBoolean(this.addUserData); this.selectedSlot = this.getByte(); } diff --git a/src/main/java/cn/nukkit/network/protocol/BookEditPacket.java b/src/main/java/cn/nukkit/network/protocol/BookEditPacket.java new file mode 100644 index 0000000000..a52a97a98e --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/BookEditPacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class BookEditPacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.BOOK_EDIT_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/BossEventPacket.java b/src/main/java/cn/nukkit/network/protocol/BossEventPacket.java index 6ab6cf58e5..8bca568b8e 100644 --- a/src/main/java/cn/nukkit/network/protocol/BossEventPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/BossEventPacket.java @@ -7,12 +7,32 @@ public class BossEventPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.BOSS_EVENT_PACKET; - public long eid; - public int type; + /* S2C: Shows the bossbar to the player. */ + public static final int TYPE_SHOW = 0; + /* C2S: Registers a player to a boss fight. */ + public static final int TYPE_REGISTER_PLAYER = 1; + public static final int TYPE_UPDATE = 1; + /* S2C: Removes the bossbar from the client. */ + public static final int TYPE_HIDE = 2; + /* C2S: Unregisters a player from a boss fight. */ + public static final int TYPE_UNREGISTER_PLAYER = 3; + /* S2C: Appears not to be implemented. Currently bar percentage only appears to change in response to the target entity's health. */ + public static final int TYPE_HEALTH_PERCENT = 4; + /* S2C: Also appears to not be implemented. Title clientside sticks as the target entity's nametag, or their entity type name if not set. */ + public static final int TYPE_TITLE = 5; + /* S2C: Not sure on this. Includes color and overlay fields, plus an unknown short. TODO: check this */ + public static final int TYPE_UNKNOWN_6 = 6; + /* S2C: Not implemented :( Intended to alter bar appearance, but these currently produce no effect on clientside whatsoever. */ + public static final int TYPE_TEXTURE = 7; - public static final int ADD = 0; - public static final int UPDATE = 1; - public static final int REMOVE = 2; + public long bossEid; + public int type; + public long playerEid; + public float healthPercent; + public String title = ""; + public short unknown; + public int color; + public int overlay; @Override public byte pid() { @@ -21,13 +41,56 @@ public byte pid() { @Override public void decode() { - + this.bossEid = this.getEntityUniqueId(); + this.type = (int) this.getUnsignedVarInt(); + switch (this.type) { + case TYPE_REGISTER_PLAYER: + case TYPE_UNREGISTER_PLAYER: + this.playerEid = this.getEntityUniqueId(); + break; + case TYPE_SHOW: + this.title = this.getString(); + this.healthPercent = this.getLFloat(); + case TYPE_UNKNOWN_6: + this.unknown = (short) this.getShort(); + case TYPE_TEXTURE: + this.color = (int) this.getUnsignedVarInt(); + this.overlay = (int) this.getUnsignedVarInt(); + break; + case TYPE_HEALTH_PERCENT: + this.healthPercent = this.getLFloat(); + break; + case TYPE_TITLE: + this.title = this.getString(); + break; + } } @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityUniqueId(this.bossEid); this.putUnsignedVarInt(this.type); + switch (this.type) { + case TYPE_REGISTER_PLAYER: + case TYPE_UNREGISTER_PLAYER: + this.putEntityUniqueId(this.playerEid); + break; + case TYPE_SHOW: + this.putString(this.title); + this.putLFloat(this.healthPercent); + case TYPE_UNKNOWN_6: + this.putShort(this.unknown); + case TYPE_TEXTURE: + this.putUnsignedVarInt(this.color); + this.putUnsignedVarInt(this.overlay); + break; + case TYPE_HEALTH_PERCENT: + this.putLFloat(this.healthPercent); + break; + case TYPE_TITLE: + this.putString(this.title); + break; + } } } diff --git a/src/main/java/cn/nukkit/network/protocol/CameraPacket.java b/src/main/java/cn/nukkit/network/protocol/CameraPacket.java new file mode 100644 index 0000000000..7f0c092dc3 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/CameraPacket.java @@ -0,0 +1,25 @@ +package cn.nukkit.network.protocol; + +public class CameraPacket extends DataPacket { + + public long cameraUniqueId; + public long playerUniqueId; + + @Override + public byte pid() { + return ProtocolInfo.CAMERA_PACKET; + } + + @Override + public void decode() { + this.cameraUniqueId = this.getVarLong(); + this.playerUniqueId = this.getVarLong(); + } + + @Override + public void encode() { + this.reset(); + this.putEntityUniqueId(this.cameraUniqueId); + this.putEntityUniqueId(this.playerUniqueId); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ChangeDimensionPacket.java b/src/main/java/cn/nukkit/network/protocol/ChangeDimensionPacket.java index 87a91c2e8e..5341227779 100644 --- a/src/main/java/cn/nukkit/network/protocol/ChangeDimensionPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ChangeDimensionPacket.java @@ -14,7 +14,7 @@ public class ChangeDimensionPacket extends DataPacket { public float y; public float z; - public boolean unknown; + public boolean respawn; @Override public void decode() { @@ -26,7 +26,7 @@ public void encode() { this.reset(); this.putVarInt(this.dimension); this.putVector3f(this.x, this.y, this.z); - this.putBoolean(this.unknown); + this.putBoolean(this.respawn); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/ClientToServerHandshakePacket.java b/src/main/java/cn/nukkit/network/protocol/ClientToServerHandshakePacket.java new file mode 100644 index 0000000000..d38d0b2b33 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/ClientToServerHandshakePacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class ClientToServerHandshakePacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.CLIENT_TO_SERVER_HANDSHAKE_PACKET; + } + + @Override + public void decode() { + //no content + } + + @Override + public void encode() { + + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ClientboundMapItemDataPacket.java b/src/main/java/cn/nukkit/network/protocol/ClientboundMapItemDataPacket.java index a5ed8ab43d..5e8b531477 100644 --- a/src/main/java/cn/nukkit/network/protocol/ClientboundMapItemDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ClientboundMapItemDataPacket.java @@ -1,6 +1,5 @@ package cn.nukkit.network.protocol; -import cn.nukkit.utils.BinaryStream; import cn.nukkit.utils.Utils; import java.awt.*; @@ -9,7 +8,7 @@ /** * Created by CreeperFace on 5.3.2017. */ -public class ClientboundMapItemDataPacket extends DataPacket { +public class ClientboundMapItemDataPacket extends DataPacket { //TODO: update to 1.2 public int[] eids = new int[0]; @@ -21,6 +20,8 @@ public class ClientboundMapItemDataPacket extends DataPacket { public int offsetX; public int offsetZ; + public byte dimensionId; + public MapDecorator[] decorators = new MapDecorator[0]; public int[] colors = new int[0]; public BufferedImage image = null; @@ -43,7 +44,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(mapId); + this.putEntityUniqueId(mapId); int update = 0; if (eids.length > 0) { @@ -52,15 +53,18 @@ public void encode() { if (decorators.length > 0) { update |= DECORATIONS_UPDATE; } + if (image != null || colors.length > 0) { update |= TEXTURE_UPDATE; } + this.putUnsignedVarInt(update); + this.putByte(this.dimensionId); if ((update & 0x08) != 0) { //TODO: find out what these are for this.putUnsignedVarInt(eids.length); for (int eid : eids) { - this.putVarInt(eid); + this.putEntityUniqueId(eid); } } if ((update & (TEXTURE_UPDATE | DECORATIONS_UPDATE)) != 0) { @@ -71,11 +75,12 @@ public void encode() { this.putUnsignedVarInt(decorators.length); for (MapDecorator decorator : decorators) { - this.putVarInt((decorator.rotation & 0x0f) | (decorator.icon << 4)); + this.putByte(decorator.rotation); + this.putByte(decorator.icon); this.putByte(decorator.offsetX); this.putByte(decorator.offsetZ); this.putString(decorator.label); - this.putLInt(decorator.color.getRGB()); + this.putVarInt(decorator.color.getRGB()); } } @@ -85,6 +90,8 @@ public void encode() { this.putVarInt(offsetX); this.putVarInt(offsetZ); + this.putUnsignedVarInt(width * height); + if (image != null) { for (int y = 0; y < width; y++) { for (int x = 0; x < height; x++) { diff --git a/src/main/java/cn/nukkit/network/protocol/CommandBlockUpdatePacket.java b/src/main/java/cn/nukkit/network/protocol/CommandBlockUpdatePacket.java new file mode 100644 index 0000000000..e87893e35c --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/CommandBlockUpdatePacket.java @@ -0,0 +1,62 @@ +package cn.nukkit.network.protocol; + +import cn.nukkit.math.BlockVector3; + +public class CommandBlockUpdatePacket extends DataPacket { + + public boolean isBlock; + public int x; + public int y; + public int z; + public int commandBlockMode; + public boolean isRedstoneMode; + public boolean isConditional; + public long minecartEid; + public String command; + public String lastOutput; + public String name; + public boolean shouldTrackOutput; + + @Override + public byte pid() { + return ProtocolInfo.COMMAND_BLOCK_UPDATE_PACKET; + } + + @Override + public void decode() { + this.isBlock = this.getBoolean(); + if (this.isBlock) { + BlockVector3 v = this.getBlockVector3(); + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.commandBlockMode = (int) this.getUnsignedVarInt(); + this.isRedstoneMode = this.getBoolean(); + this.isConditional = this.getBoolean(); + } else { + this.minecartEid = this.getEntityRuntimeId(); + } + this.command = this.getString(); + this.lastOutput = this.getString(); + this.name = this.getString(); + this.shouldTrackOutput = this.getBoolean(); + } + + @Override + public void encode() { + this.reset(); + this.putBoolean(this.isBlock); + if (this.isBlock) { + this.putBlockVector3(this.x, this.y, this.z); + this.putUnsignedVarInt(this.commandBlockMode); + this.putBoolean(this.isRedstoneMode); + this.putBoolean(this.isConditional); + } else { + this.putEntityRuntimeId(this.minecartEid); + } + this.putString(this.command); + this.putString(this.lastOutput); + this.putString(this.name); + this.putBoolean(this.shouldTrackOutput); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/CommandRequestPacket.java b/src/main/java/cn/nukkit/network/protocol/CommandRequestPacket.java new file mode 100644 index 0000000000..28673d2ac1 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/CommandRequestPacket.java @@ -0,0 +1,45 @@ +package cn.nukkit.network.protocol; + +/** + * author: MagicDroidX + * Nukkit Project + */ +public class CommandRequestPacket extends DataPacket { + + public static final byte NETWORK_ID = ProtocolInfo.COMMAND_REQUEST_PACKET; + + public static final int TYPE_PLAYER = 0; + public static final int TYPE_COMMAND_BLOCK = 1; + public static final int TYPE_MINECART_COMMAND_BLOCK = 2; + public static final int TYPE_DEV_CONSOLE = 3; + public static final int TYPE_AUTOMATION_PLAYER = 4; + public static final int TYPE_CLIENT_AUTOMATION = 5; + public static final int TYPE_DEDICATED_SERVER = 6; + public static final int TYPE_ENTITY = 7; + public static final int TYPE_VIRTUAL = 8; + public static final int TYPE_GAME_ARGUMENT = 9; + public static final int TYPE_INTERNAL = 10; + + public String command; + public int type; + public String requestId; + public long playerUniqueId; + + @Override + public byte pid() { + return NETWORK_ID; + } + + @Override + public void decode() { + this.command = this.getString(); + this.type = this.getVarInt(); + this.requestId = this.getString(); + this.playerUniqueId = this.getVarLong(); + } + + @Override + public void encode() { + } + +} diff --git a/src/main/java/cn/nukkit/network/protocol/CommandStepPacket.java b/src/main/java/cn/nukkit/network/protocol/CommandStepPacket.java deleted file mode 100644 index d85d26104d..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/CommandStepPacket.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.command.data.CommandArgs; -import com.google.gson.Gson; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public class CommandStepPacket extends DataPacket { - - public static final byte NETWORK_ID = ProtocolInfo.COMMAND_STEP_PACKET; - - /** - * unknown (string) - * unknown (string) - * unknown (uvarint) - * unknown (uvarint) - * unknown (bool) - * unknown (uvarint64) - * unknown (string) - * unknown (string) - * https://gist.github.com/dktapps/8285b93af4ca38e0104bfeb9a6c87afd - */ - - - public String command; - public String overload; - public long uvarint1; - public long currentStep; - public boolean done; - public long clientId; - public CommandArgs args = new CommandArgs(); //JSON formatted command arguments - public String outputJson; - - @Override - public byte pid() { - return NETWORK_ID; - } - - @Override - public void decode() { - this.command = this.getString(); - this.overload = this.getString(); - this.uvarint1 = this.getUnsignedVarInt(); - this.currentStep = this.getUnsignedVarInt(); - this.done = this.getBoolean(); - this.clientId = this.getVarLong(); - String argsString = this.getString(); - this.args = new Gson().fromJson(argsString, CommandArgs.class); - this.outputJson = this.getString(); - while (!this.feof()) { - this.getByte(); //prevent assertion errors. TODO: find out why there are always 3 extra bytes at the end of this packet. - } - - } - - @Override - public void encode() { - } - -} diff --git a/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java b/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java index 2c648e4199..d9646cd20c 100644 --- a/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ContainerClosePacket.java @@ -12,16 +12,16 @@ public byte pid() { return NETWORK_ID; } - public int windowid; + public int windowId; @Override public void decode() { - this.windowid = this.getByte(); + this.windowId = this.getByte(); } @Override public void encode() { this.reset(); - this.putByte((byte) this.windowid); + this.putByte((byte) this.windowId); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ContainerOpenPacket.java b/src/main/java/cn/nukkit/network/protocol/ContainerOpenPacket.java index 9d64bf2124..9ba871c569 100644 --- a/src/main/java/cn/nukkit/network/protocol/ContainerOpenPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ContainerOpenPacket.java @@ -1,5 +1,7 @@ package cn.nukkit.network.protocol; +import cn.nukkit.math.BlockVector3; + /** * author: MagicDroidX * Nukkit Project @@ -12,24 +14,30 @@ public byte pid() { return NETWORK_ID; } - public byte windowid; - public byte type; + public int windowId; + public int type; public int x; public int y; public int z; - public final long entityId = -1; + public long entityId = -1; @Override public void decode() { - + this.windowId = this.getByte(); + this.type = this.getByte(); + BlockVector3 v = this.getBlockVector3(); + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.entityId = this.getEntityUniqueId(); } @Override public void encode() { this.reset(); - this.putByte(this.windowid); - this.putByte(this.type); - this.putBlockCoords(this.x, this.y, this.z); - this.putVarLong(this.entityId); + this.putByte((byte) this.windowId); + this.putByte((byte) this.type); + this.putBlockVector3(this.x, this.y, this.z); + this.putEntityUniqueId(this.entityId); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ContainerSetContentPacket.java b/src/main/java/cn/nukkit/network/protocol/ContainerSetContentPacket.java deleted file mode 100644 index 73d92944af..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/ContainerSetContentPacket.java +++ /dev/null @@ -1,79 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.item.Item; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public class ContainerSetContentPacket extends DataPacket { - public static final byte NETWORK_ID = ProtocolInfo.CONTAINER_SET_CONTENT_PACKET; - - @Override - public byte pid() { - return NETWORK_ID; - } - - public static final byte SPECIAL_INVENTORY = 0; - public static final byte SPECIAL_ARMOR = 0x78; - public static final byte SPECIAL_CREATIVE = 0x79; - public static final byte SPECIAL_HOTBAR = 0x7a; - public static final byte SPECIAL_FIXED_INVENTORY = 0x7b; - - public long windowid; - public long eid; - public Item[] slots = new Item[0]; - public int[] hotbar = new int[0]; - - @Override - public DataPacket clean() { - this.slots = new Item[0]; - this.hotbar = new int[0]; - return super.clean(); - } - - @Override - public void decode() { - this.windowid = (int) this.getUnsignedVarInt(); - this.eid = this.getVarLong(); - int count = (int) this.getUnsignedVarInt(); - this.slots = new Item[count]; - - for (int s = 0; s < count && !this.feof(); ++s) { - this.slots[s] = this.getSlot(); - } - - count = (int) this.getUnsignedVarInt(); - this.hotbar = new int[count]; - for (int s = 0; s < count && !this.feof(); ++s) { - this.hotbar[s] = this.getVarInt(); - } - } - - @Override - public void encode() { - this.reset(); - this.putUnsignedVarInt(this.windowid); - this.putVarLong(this.eid); - this.putUnsignedVarInt(this.slots.length); - for (Item slot : this.slots) { - this.putSlot(slot); - } - - if (this.windowid == SPECIAL_INVENTORY && this.hotbar.length > 0) { - this.putUnsignedVarInt(this.hotbar.length); - for (int slot : this.hotbar) { - this.putVarInt(slot); - } - } else { - this.putUnsignedVarInt(0); - } - } - - @Override - public ContainerSetContentPacket clone() { - ContainerSetContentPacket pk = (ContainerSetContentPacket) super.clone(); - pk.slots = this.slots.clone(); - return pk; - } -} diff --git a/src/main/java/cn/nukkit/network/protocol/ContainerSetDataPacket.java b/src/main/java/cn/nukkit/network/protocol/ContainerSetDataPacket.java index 0af0d573a4..97458e3e82 100644 --- a/src/main/java/cn/nukkit/network/protocol/ContainerSetDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ContainerSetDataPacket.java @@ -7,12 +7,22 @@ public class ContainerSetDataPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.CONTAINER_SET_DATA_PACKET; + public static final int PROPERTY_FURNACE_TICK_COUNT = 0; + public static final int PROPERTY_FURNACE_LIT_TIME = 1; + public static final int PROPERTY_FURNACE_LIT_DURATION = 2; + //TODO: check property 3 + public static final int PROPERTY_FURNACE_FUEL_AUX = 4; + + public static final int PROPERTY_BREWING_STAND_BREW_TIME = 0; + public static final int PROPERTY_BREWING_STAND_FUEL_AMOUNT = 1; + public static final int PROPERTY_BREWING_STAND_FUEL_TOTAL = 2; + @Override public byte pid() { return NETWORK_ID; } - public byte windowid; + public int windowId; public int property; public int value; @@ -24,7 +34,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putByte(this.windowid); + this.putByte((byte) this.windowId); this.putVarInt(this.property); this.putVarInt(this.value); } diff --git a/src/main/java/cn/nukkit/network/protocol/ContainerSetSlotPacket.java b/src/main/java/cn/nukkit/network/protocol/ContainerSetSlotPacket.java deleted file mode 100644 index b78648c5bb..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/ContainerSetSlotPacket.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.item.Item; - -/** - * author: MagicDroidX - * Nukkit Project - */ -public class ContainerSetSlotPacket extends DataPacket { - public static final byte NETWORK_ID = ProtocolInfo.CONTAINER_SET_SLOT_PACKET; - - @Override - public byte pid() { - return NETWORK_ID; - } - - public int windowid; - public int slot; - public int hotbarSlot; - public Item item; - public int selectedSlot; - - @Override - public void decode() { - this.windowid = this.getByte(); - this.slot = this.getVarInt(); - this.hotbarSlot = this.getVarInt(); - this.item = this.getSlot(); - this.selectedSlot = this.getByte(); - } - - @Override - public void encode() { - this.reset(); - this.putByte((byte) this.windowid); - this.putVarInt(this.slot); - this.putVarInt(this.hotbarSlot); - this.putSlot(this.item); - this.putByte((byte) this.selectedSlot); - } -} diff --git a/src/main/java/cn/nukkit/network/protocol/CraftingEventPacket.java b/src/main/java/cn/nukkit/network/protocol/CraftingEventPacket.java index a500542d61..e9530edd56 100644 --- a/src/main/java/cn/nukkit/network/protocol/CraftingEventPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/CraftingEventPacket.java @@ -12,6 +12,13 @@ public class CraftingEventPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.CRAFTING_EVENT_PACKET; + public static final int TYPE_SHAPELESS = 0; + public static final int TYPE_SHAPED = 1; + public static final int TYPE_FURNACE = 2; + public static final int TYPE_FURNACE_DATA = 3; + public static final int TYPE_MULTI = 4; + public static final int TYPE_SHULKER_BOX = 5; + public int windowId; public int type; public UUID id; @@ -22,7 +29,7 @@ public class CraftingEventPacket extends DataPacket { @Override public void decode() { this.windowId = this.getByte(); - this.type = (int) this.getUnsignedVarInt(); + this.type = this.getVarInt(); this.id = this.getUUID(); int inputSize = (int) this.getUnsignedVarInt(); diff --git a/src/main/java/cn/nukkit/network/protocol/DataPacket.java b/src/main/java/cn/nukkit/network/protocol/DataPacket.java index da40136b85..f2570e5573 100644 --- a/src/main/java/cn/nukkit/network/protocol/DataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/DataPacket.java @@ -27,6 +27,7 @@ public abstract class DataPacket extends BinaryStream implements Cloneable { public void reset() { super.reset(); this.putByte(this.pid()); + this.putShort(0); } public void setChannel(int channel) { diff --git a/src/main/java/cn/nukkit/network/protocol/DropItemPacket.java b/src/main/java/cn/nukkit/network/protocol/DropItemPacket.java deleted file mode 100644 index 9cd730ad79..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/DropItemPacket.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.item.Item; - -/** - * @author Nukkit Project Team - */ -public class DropItemPacket extends DataPacket { - - public static final byte NETWORK_ID = ProtocolInfo.DROP_ITEM_PACKET; - - public int type; - public Item item; - - @Override - public void decode() { - this.type = this.getByte(); - this.item = this.getSlot(); - } - - @Override - public void encode() { - - } - - @Override - public byte pid() { - return NETWORK_ID; - } - -} diff --git a/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java b/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java index 074338725c..2ad19b9e17 100644 --- a/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/EntityEventPacket.java @@ -5,24 +5,31 @@ * Nukkit Project */ public class EntityEventPacket extends DataPacket { - public static final byte NETWORK_ID = ProtocolInfo.ENTITY_EVENT_PACKET; + public static final int NETWORK_ID = ProtocolInfo.ENTITY_EVENT_PACKET; - public static final byte HURT_ANIMATION = 2; - public static final byte DEATH_ANIMATION = 3; + public static final int HURT_ANIMATION = 2; + public static final int DEATH_ANIMATION = 3; - public static final byte TAME_FAIL = 6; - public static final byte TAME_SUCCESS = 7; - public static final byte SHAKE_WET = 8; - public static final byte USE_ITEM = 9; - public static final byte EAT_GRASS_ANIMATION = 10; - public static final byte FISH_HOOK_BUBBLE = 11; - public static final byte FISH_HOOK_POSITION = 12; - public static final byte FISH_HOOK_HOOK = 13; - public static final byte FISH_HOOK_TEASE = 14; - public static final byte SQUID_INK_CLOUD = 15; - public static final byte AMBIENT_SOUND = 16; - public static final byte RESPAWN = 17; + public static final int TAME_FAIL = 6; + public static final int TAME_SUCCESS = 7; + public static final int SHAKE_WET = 8; + public static final int USE_ITEM = 9; + public static final int EAT_GRASS_ANIMATION = 10; + public static final int FISH_HOOK_BUBBLE = 11; + public static final int FISH_HOOK_POSITION = 12; + public static final int FISH_HOOK_HOOK = 13; + public static final int FISH_HOOK_TEASE = 14; + public static final int SQUID_INK_CLOUD = 15; + + public static final int AMBIENT_SOUND = 17; + public static final int RESPAWN = 18; + + public static final int ENCHANT = 34; + + public static final byte EATING_ITEM = 57; + + public static final byte UNKNOWN1 = 66; @Override public byte pid() { @@ -30,21 +37,21 @@ public byte pid() { } public long eid; - public byte event; - public long unknown; + public int event; + public int data; @Override public void decode() { - this.eid = this.getVarLong(); - this.event = (byte) this.getByte(); - this.unknown = this.getUnsignedVarInt(); + this.eid = this.getEntityRuntimeId(); + this.event = this.getByte(); + this.data = this.getVarInt(); } @Override public void encode() { this.reset(); - this.putVarLong(this.eid); - this.putByte(this.event); - this.putUnsignedVarInt(this.unknown); + this.putEntityRuntimeId(this.eid); + this.putByte((byte) this.event); + this.putVarInt((byte) this.data); } } diff --git a/src/main/java/cn/nukkit/network/protocol/EntityFallPacket.java b/src/main/java/cn/nukkit/network/protocol/EntityFallPacket.java index ae876b029d..086fc56a6b 100644 --- a/src/main/java/cn/nukkit/network/protocol/EntityFallPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/EntityFallPacket.java @@ -9,7 +9,7 @@ public class EntityFallPacket extends DataPacket { @Override public void decode() { - this.eid = this.getVarLong(); + this.eid = this.getEntityRuntimeId(); this.fallDistance = this.getLFloat(); this.unknown = this.getBoolean(); } diff --git a/src/main/java/cn/nukkit/network/protocol/EntityPickRequestPacket.java b/src/main/java/cn/nukkit/network/protocol/EntityPickRequestPacket.java new file mode 100644 index 0000000000..d4abec9571 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/EntityPickRequestPacket.java @@ -0,0 +1,21 @@ +package cn.nukkit.network.protocol; + +public class EntityPickRequestPacket extends DataPacket { + + public static final byte NETWORK_ID = ProtocolInfo.ENTITY_PICK_REQUEST_PACKET; + + @Override + public byte pid() { + return NETWORK_ID; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ExplodePacket.java b/src/main/java/cn/nukkit/network/protocol/ExplodePacket.java index a55e33ef89..922aaff2bd 100644 --- a/src/main/java/cn/nukkit/network/protocol/ExplodePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ExplodePacket.java @@ -40,7 +40,7 @@ public void encode() { this.putUnsignedVarInt(this.records.length); if (this.records.length > 0) { for (Vector3 record : records) { - this.putBlockCoords((int) record.x, (int) record.y, (int) record.z); + this.putBlockVector3((int) record.x, (int) record.y, (int) record.z); } } } diff --git a/src/main/java/cn/nukkit/network/protocol/GUIDataPickItemPacket.java b/src/main/java/cn/nukkit/network/protocol/GUIDataPickItemPacket.java new file mode 100644 index 0000000000..6cbc80ca1f --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/GUIDataPickItemPacket.java @@ -0,0 +1,22 @@ +package cn.nukkit.network.protocol; + +public class GUIDataPickItemPacket extends DataPacket { + + public int hotbarSlot; + + @Override + public byte pid() { + return ProtocolInfo.GUI_DATA_PICK_ITEM_PACKET; + } + + @Override + public void encode() { + this.reset(); + this.putLInt(this.hotbarSlot); + } + + @Override + public void decode() { + this.hotbarSlot = this.getLInt(); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/InitiateWebSocketConnectionPacket.java b/src/main/java/cn/nukkit/network/protocol/InitiateWebSocketConnectionPacket.java new file mode 100644 index 0000000000..4cb9bd6333 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/InitiateWebSocketConnectionPacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class InitiateWebSocketConnectionPacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.INITIATE_WEB_SOCKET_CONNECTION_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/InteractPacket.java b/src/main/java/cn/nukkit/network/protocol/InteractPacket.java index 6d49409e24..e909160c28 100644 --- a/src/main/java/cn/nukkit/network/protocol/InteractPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/InteractPacket.java @@ -7,27 +7,25 @@ public class InteractPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.INTERACT_PACKET; - public static final byte ACTION_RIGHT_CLICK = 1; - public static final byte ACTION_LEFT_CLICK = 2; - public static final byte ACTION_VEHICLE_EXIT = 3; - public static final byte ACTION_MOUSEOVER = 4; + public static final int ACTION_VEHICLE_EXIT = 3; + public static final int ACTION_MOUSEOVER = 4; - public static final byte ACTION_OPEN_INVENTORY = 6; + public static final int ACTION_OPEN_INVENTORY = 6; - public byte action; + public int action; public long target; @Override public void decode() { - this.action = (byte) this.getByte(); - this.target = this.getVarLong(); + this.action = this.getByte(); + this.target = this.getEntityRuntimeId(); } @Override public void encode() { this.reset(); - this.putByte(this.action); - this.putVarLong(this.target); + this.putByte((byte) this.action); + this.putEntityRuntimeId(this.target); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/InventoryActionPacket.java b/src/main/java/cn/nukkit/network/protocol/InventoryActionPacket.java deleted file mode 100644 index e7492223ee..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/InventoryActionPacket.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.item.Item; - -public class InventoryActionPacket extends DataPacket { - - public static final byte NETWORK_ID = ProtocolInfo.INVENTORY_ACTION_PACKET; - - public int actionId; - public Item item; - public int enchantmentId; - public int enchantmentLevel; - - @Override - public void decode() { - - } - - @Override - public void encode() { - this.reset(); - this.putUnsignedVarInt(this.actionId); - this.putSlot(this.item); - this.putVarInt(this.enchantmentId); - this.putVarInt(this.enchantmentLevel); - } - - @Override - public byte pid() { - return NETWORK_ID; - } -} diff --git a/src/main/java/cn/nukkit/network/protocol/InventoryContentPacket.java b/src/main/java/cn/nukkit/network/protocol/InventoryContentPacket.java new file mode 100644 index 0000000000..18b831866e --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/InventoryContentPacket.java @@ -0,0 +1,60 @@ +package cn.nukkit.network.protocol; + +import cn.nukkit.item.Item; + +/** + * author: MagicDroidX + * Nukkit Project + */ +public class InventoryContentPacket extends DataPacket { + public static final byte NETWORK_ID = ProtocolInfo.INVENTORY_CONTENT_PACKET; + + @Override + public byte pid() { + return NETWORK_ID; + } + + public static final int SPECIAL_INVENTORY = 0; + public static final int SPECIAL_OFFHAND = 0x77; + public static final int SPECIAL_ARMOR = 0x78; + public static final int SPECIAL_CREATIVE = 0x79; + public static final int SPECIAL_HOTBAR = 0x7a; + public static final int SPECIAL_FIXED_INVENTORY = 0x7b; + + public int inventoryId; + public Item[] slots = new Item[0]; + + @Override + public DataPacket clean() { + this.slots = new Item[0]; + return super.clean(); + } + + @Override + public void decode() { + this.inventoryId = (int) this.getUnsignedVarInt(); + int count = (int) this.getUnsignedVarInt(); + this.slots = new Item[count]; + + for (int s = 0; s < count && !this.feof(); ++s) { + this.slots[s] = this.getSlot(); + } + } + + @Override + public void encode() { + this.reset(); + this.putUnsignedVarInt(this.inventoryId); + this.putUnsignedVarInt(this.slots.length); + for (Item slot : this.slots) { + this.putSlot(slot); + } + } + + @Override + public InventoryContentPacket clone() { + InventoryContentPacket pk = (InventoryContentPacket) super.clone(); + pk.slots = this.slots.clone(); + return pk; + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/InventorySlotPacket.java b/src/main/java/cn/nukkit/network/protocol/InventorySlotPacket.java new file mode 100644 index 0000000000..ae8a9d6e0a --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/InventorySlotPacket.java @@ -0,0 +1,35 @@ +package cn.nukkit.network.protocol; + +import cn.nukkit.item.Item; + +/** + * author: MagicDroidX + * Nukkit Project + */ +public class InventorySlotPacket extends DataPacket { + public static final byte NETWORK_ID = ProtocolInfo.INVENTORY_SLOT_PACKET; + + @Override + public byte pid() { + return NETWORK_ID; + } + + public int inventoryId; + public int slot; + public Item item; + + @Override + public void decode() { + this.inventoryId = (int) this.getUnsignedVarInt(); + this.slot = (int) this.getUnsignedVarInt(); + this.item = this.getSlot(); + } + + @Override + public void encode() { + this.reset(); + this.putUnsignedVarInt((byte) this.inventoryId); + this.putUnsignedVarInt(this.slot); + 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 new file mode 100644 index 0000000000..88ae0646de --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/InventoryTransactionPacket.java @@ -0,0 +1,144 @@ +package cn.nukkit.network.protocol; + +import cn.nukkit.inventory.transaction.data.ReleaseItemData; +import cn.nukkit.inventory.transaction.data.TransactionData; +import cn.nukkit.inventory.transaction.data.UseItemData; +import cn.nukkit.inventory.transaction.data.UseItemOnEntityData; +import cn.nukkit.network.protocol.types.NetworkInventoryAction; + +public class InventoryTransactionPacket extends DataPacket { + + public static final int TYPE_NORMAL = 0; + public static final int TYPE_MISMATCH = 1; + public static final int TYPE_USE_ITEM = 2; + public static final int TYPE_USE_ITEM_ON_ENTITY = 3; + public static final int TYPE_RELEASE_ITEM = 4; + + public static final int USE_ITEM_ACTION_CLICK_BLOCK = 0; + public static final int USE_ITEM_ACTION_CLICK_AIR = 1; + public static final int USE_ITEM_ACTION_BREAK_BLOCK = 2; + + public static final int RELEASE_ITEM_ACTION_RELEASE = 0; //bow shoot + public static final int RELEASE_ITEM_ACTION_CONSUME = 1; //eat food, drink potion + + public static final int USE_ITEM_ON_ENTITY_ACTION_INTERACT = 0; + public static final int USE_ITEM_ON_ENTITY_ACTION_ATTACK = 1; + + + public static final int ACTION_MAGIC_SLOT_DROP_ITEM = 0; + public static final int ACTION_MAGIC_SLOT_PICKUP_ITEM = 1; + + public static final int ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM = 0; + public static final int ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM = 1; + + public int transactionType; + public NetworkInventoryAction[] actions; + public TransactionData transactionData; + + @Override + public byte pid() { + return ProtocolInfo.INVENTORY_TRANSACTION_PACKET; + } + + @Override + public void encode() { + this.reset(); + this.putUnsignedVarInt(this.transactionType); + + this.putUnsignedVarInt(this.actions.length); + for (NetworkInventoryAction action : this.actions) { + action.write(this); + } + + switch (this.transactionType) { + case TYPE_NORMAL: + case TYPE_MISMATCH: + break; + case TYPE_USE_ITEM: + UseItemData useItemData = (UseItemData) this.transactionData; + + this.putUnsignedVarInt(useItemData.actionType); + this.putBlockVector3(useItemData.blockPos); + this.putBlockFace(useItemData.face); + this.putVarInt(useItemData.hotbarSlot); + this.putSlot(useItemData.itemInHand); + this.putVector3f(useItemData.playerPos.asVector3f()); + this.putVector3f(useItemData.clickPos); + break; + case TYPE_USE_ITEM_ON_ENTITY: + UseItemOnEntityData useItemOnEntityData = (UseItemOnEntityData) this.transactionData; + + this.putEntityRuntimeId(useItemOnEntityData.entityRuntimeId); + this.putUnsignedVarInt(useItemOnEntityData.actionType); + this.putVarInt(useItemOnEntityData.hotbarSlot); + this.putSlot(useItemOnEntityData.itemInHand); + this.putVector3f(useItemOnEntityData.vector1.asVector3f()); + this.putVector3f(useItemOnEntityData.vector2.asVector3f()); + break; + case TYPE_RELEASE_ITEM: + ReleaseItemData releaseItemData = (ReleaseItemData) this.transactionData; + + this.putUnsignedVarInt(releaseItemData.actionType); + this.putVarInt(releaseItemData.hotbarSlot); + this.putSlot(releaseItemData.itemInHand); + this.putVector3f(releaseItemData.headRot.asVector3f()); + break; + default: + throw new RuntimeException("Unknown transaction type " + this.transactionType); + } + } + + @Override + public void decode() { + this.transactionType = (int) this.getUnsignedVarInt(); + + this.actions = new NetworkInventoryAction[(int) this.getUnsignedVarInt()]; + for (int i = 0; i < this.actions.length; i++) { + this.actions[i] = new NetworkInventoryAction().read(this); + } + + switch (this.transactionType) { + case TYPE_NORMAL: + case TYPE_MISMATCH: + //Regular ComplexInventoryTransaction doesn't read any extra data + break; + case TYPE_USE_ITEM: + UseItemData itemData = new UseItemData(); + + itemData.actionType = (int) this.getUnsignedVarInt(); + itemData.blockPos = this.getBlockVector3(); + itemData.face = this.getBlockFace(); + itemData.hotbarSlot = this.getVarInt(); + itemData.itemInHand = this.getSlot(); + itemData.playerPos = this.getVector3f().asVector3(); + itemData.clickPos = this.getVector3f(); + + this.transactionData = itemData; + break; + case TYPE_USE_ITEM_ON_ENTITY: + UseItemOnEntityData useItemOnEntityData = new UseItemOnEntityData(); + + useItemOnEntityData.entityRuntimeId = this.getEntityRuntimeId(); + useItemOnEntityData.actionType = (int) this.getUnsignedVarInt(); + useItemOnEntityData.hotbarSlot = this.getVarInt(); + useItemOnEntityData.itemInHand = this.getSlot(); + useItemOnEntityData.vector1 = this.getVector3f().asVector3(); + useItemOnEntityData.vector2 = this.getVector3f().asVector3(); + + this.transactionData = useItemOnEntityData; + break; + case TYPE_RELEASE_ITEM: + ReleaseItemData releaseItemData = new ReleaseItemData(); + + releaseItemData.actionType = (int) getUnsignedVarInt(); + releaseItemData.hotbarSlot = getVarInt(); + releaseItemData.itemInHand = getSlot(); + releaseItemData.headRot = this.getVector3f().asVector3(); + + this.transactionData = releaseItemData; + break; + default: + throw new RuntimeException("Unknown transaction type " + this.transactionType); + } + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ItemFrameDropItemPacket.java b/src/main/java/cn/nukkit/network/protocol/ItemFrameDropItemPacket.java index ce0ca5c807..1aa0b9458e 100644 --- a/src/main/java/cn/nukkit/network/protocol/ItemFrameDropItemPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ItemFrameDropItemPacket.java @@ -15,7 +15,7 @@ public class ItemFrameDropItemPacket extends DataPacket { @Override public void decode() { - BlockVector3 v = this.getBlockCoords(); + BlockVector3 v = this.getBlockVector3(); this.z = v.z; this.y = v.y; this.x = v.x; diff --git a/src/main/java/cn/nukkit/network/protocol/LevelEventPacket.java b/src/main/java/cn/nukkit/network/protocol/LevelEventPacket.java index 882233b3e3..421958dcd1 100644 --- a/src/main/java/cn/nukkit/network/protocol/LevelEventPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/LevelEventPacket.java @@ -32,7 +32,7 @@ public class LevelEventPacket extends DataPacket { public static final int EVENT_SOUND_ITEM_THROWN = 1031; public static final int EVENT_SOUND_PORTAL = 1032; - + public static final int EVENT_SOUND_ITEM_FRAME_ITEM_ADDED = 1040; public static final int EVENT_SOUND_ITEM_FRAME_PLACED = 1041; public static final int EVENT_SOUND_ITEM_FRAME_REMOVED = 1042; @@ -41,14 +41,19 @@ public class LevelEventPacket extends DataPacket { public static final int EVENT_SOUND_CAMERA_TAKE_PICTURE = 1050; public static final int EVENT_SOUND_EXPERIENCE_ORB = 1051; - public static final int EVENT_SOUND_BLOCK_PLACE = 1052; + public static final int EVENT_SOUND_TOTEM = 1052; + + public static final int EVENT_SOUND_ARMOR_STAND_BREAK = 1060; + public static final int EVENT_SOUND_ARMOR_STAND_HIT = 1061; + public static final int EVENT_SOUND_ARMOR_STAND_FALL = 1062; + public static final int EVENT_SOUND_ARMOR_STAND_PLACE = 1063; public static final int EVENT_GUARDIAN_CURSE = 2006; - + public static final int EVENT_PARTICLE_BLOCK_FORCE_FIELD = 2008; public static final int EVENT_PARTICLE_PUNCH_BLOCK = 2014; - + public static final int EVENT_SOUND_BUTTON_CLICK = 3500; public static final int EVENT_SOUND_EXPLODE = 3501; public static final int EVENT_CAULDRON_DYE_ARMOR = 3502; @@ -58,7 +63,8 @@ public class LevelEventPacket extends DataPacket { public static final int EVENT_SOUND_SPLASH = 3506; public static final int EVENT_CAULDRON_TAKE_WATER = 3507; public static final int EVENT_CAULDRON_ADD_DYE = 3508; - + public static final int EVENT_CAULDRON_CLEAN_BANNER = 3509; + public static final int EVENT_PARTICLE_SHOOT = 2000; public static final int EVENT_PARTICLE_DESTROY = 2001; public static final int EVENT_PARTICLE_SPLASH = 2002; @@ -89,7 +95,7 @@ public class LevelEventPacket extends DataPacket { public float x = 0; public float y = 0; public float z = 0; - public int data; + public int data = 0; @Override public byte pid() { diff --git a/src/main/java/cn/nukkit/network/protocol/LevelSoundEventPacket.java b/src/main/java/cn/nukkit/network/protocol/LevelSoundEventPacket.java index 5d6661f293..9952f18e46 100644 --- a/src/main/java/cn/nukkit/network/protocol/LevelSoundEventPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/LevelSoundEventPacket.java @@ -6,123 +6,176 @@ public class LevelSoundEventPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.LEVEL_SOUND_EVENT_PACKET; - public static final byte SOUND_ITEM_USE_ON = 0; - public static final byte SOUND_HIT = 1; - public static final byte SOUND_STEP = 2; - public static final byte SOUND_JUMP = 3; - public static final byte SOUND_BREAK = 4; - public static final byte SOUND_PLACE = 5; - public static final byte SOUND_HEAVY_STEP = 6; - public static final byte SOUND_GALLOP = 7; - public static final byte SOUND_FALL = 8; - public static final byte SOUND_AMBIENT = 9; - public static final byte SOUND_AMBIENT_BABY = 10; - public static final byte SOUND_AMBIENT_IN_WATER = 11; - public static final byte SOUND_BREATHE = 12; - public static final byte SOUND_DEATH = 13; - public static final byte SOUND_DEATH_IN_WATER = 14; - public static final byte SOUND_DEATH_TO_ZOMBIE = 15; - public static final byte SOUND_HURT = 16; - public static final byte SOUND_HURT_IN_WATER = 17; - public static final byte SOUND_MAD = 18; - public static final byte SOUND_BOOST = 19; - public static final byte SOUND_BOW = 20; - public static final byte SOUND_SQUISH_BIG = 21; - public static final byte SOUND_SQUISH_SMALL = 22; - public static final byte SOUND_FALL_BIG = 23; - public static final byte SOUND_FALL_SMALL = 24; - public static final byte SOUND_SPLASH = 25; - public static final byte SOUND_FIZZ = 26; - public static final byte SOUND_FLAP = 27; - public static final byte SOUND_SWIM = 28; - public static final byte SOUND_DRINK = 29; - public static final byte SOUND_EAT = 30; - public static final byte SOUND_TAKEOFF = 31; - public static final byte SOUND_SHAKE = 32; - public static final byte SOUND_PLOP = 33; - public static final byte SOUND_LAND = 34; - public static final byte SOUND_SADDLE = 35; - public static final byte SOUND_ARMOR = 36; - public static final byte SOUND_ADD_CHEST = 37; - public static final byte SOUND_THROW = 38; - public static final byte SOUND_ATTACK = 39; - public static final byte SOUND_ATTACK_NODAMAGE = 40; - public static final byte SOUND_WARN = 41; - public static final byte SOUND_SHEAR = 42; - public static final byte SOUND_MILK = 43; - public static final byte SOUND_THUNDER = 44; - public static final byte SOUND_EXPLODE = 45; - public static final byte SOUND_FIRE = 46; - public static final byte SOUND_IGNITE = 47; - public static final byte SOUND_FUSE = 48; - public static final byte SOUND_STARE = 49; - public static final byte SOUND_SPAWN = 50; - public static final byte SOUND_SHOOT = 51; - public static final byte SOUND_BREAK_BLOCK = 52; - public static final byte SOUND_REMEDY = 53; - public static final byte SOUND_UNFECT = 54; - public static final byte SOUND_LEVELUP = 55; - public static final byte SOUND_BOW_HIT = 56; - public static final byte SOUND_BULLET_HIT = 57; - public static final byte SOUND_EXTINGUISH_FIRE = 58; - public static final byte SOUND_ITEM_FIZZ = 59; - public static final byte SOUND_CHEST_OPEN = 60; - public static final byte SOUND_CHEST_CLOSED = 61; - public static final byte SOUND_SHULKERBOX_OPEN = 62; - public static final byte SOUND_SHULKERBOX_CLOSED = 63; - public static final byte SOUND_POWER_ON = 64; - public static final byte SOUND_POWER_OFF = 65; - public static final byte SOUND_ATTACH = 66; - public static final byte SOUND_DETACH = 67; - public static final byte SOUND_DENY = 68; - public static final byte SOUND_TRIPOD = 69; - public static final byte SOUND_POP = 70; - public static final byte SOUND_DROP_SLOT = 71; - public static final byte SOUND_NOTE = 72; - public static final byte SOUND_THORNS = 73; - public static final byte SOUND_PISTON_IN = 74; - public static final byte SOUND_PISTON_OUT = 75; - public static final byte SOUND_PORTAL = 76; - public static final byte SOUND_WATER = 77; - public static final byte SOUND_LAVA_POP = 78; - public static final byte SOUND_LAVA = 79; - public static final byte SOUND_BURP = 80; - public static final byte SOUND_BUCKET_FILL_WATER = 81; - public static final byte SOUND_BUCKET_FILL_LAVA = 82; - public static final byte SOUND_BUCKET_EMPTY_WATER = 83; - public static final byte SOUND_BUCKET_EMPTY_LAVA = 84; - public static final byte SOUND_GUARDIAN_FLOP = 85; - public static final byte SOUND_ELDERGUARDIAN_CURSE = 86; - public static final byte SOUND_MOB_WARNING = 87; - public static final byte SOUND_MOB_WARNING_BABY = 88; - public static final byte SOUND_TELEPORT = 89; - public static final byte SOUND_SHULKER_OPEN = 90; - public static final byte SOUND_SHULKER_CLOSE = 91; - public static final byte SOUND_HAGGLE = 92; - public static final byte SOUND_HAGGLE_YES = 93; - public static final byte SOUND_HAGGLE_NO = 94; - public static final byte SOUND_HAGGLE_IDLE = 95; - public static final byte SOUND_CHORUSGROW = 96; - public static final byte SOUND_CHORUSDEATH = 97; - public static final byte SOUND_GLASS = 98; - public static final byte SOUND_CAST_SPELL = 99; - public static final byte SOUND_PREPARE_ATTACK = 100; - public static final byte SOUND_PREPARE_SUMMON = 101; - public static final byte SOUND_PREPARE_WOLOLO = 102; - public static final byte SOUND_FANG = 103; - public static final byte SOUND_CHARGE = 104; - public static final byte SOUND_CAMERA_TAKE_PICTURE = 105; - public static final byte SOUND_DEFAULT = 106; - public static final byte SOUND_UNDEFINED = 107; + public static final int SOUND_ITEM_USE_ON = 0; + public static final int SOUND_HIT = 1; + public static final int SOUND_STEP = 2; + public static final int SOUND_FLY = 3; + public static final int SOUND_JUMP = 4; + public static final int SOUND_BREAK = 5; + public static final int SOUND_PLACE = 6; + public static final int SOUND_HEAVY_STEP = 7; + public static final int SOUND_GALLOP = 8; + public static final int SOUND_FALL = 9; + public static final int SOUND_AMBIENT = 10; + public static final int SOUND_AMBIENT_BABY = 11; + public static final int SOUND_AMBIENT_IN_WATER = 12; + public static final int SOUND_BREATHE = 13; + public static final int SOUND_DEATH = 14; + public static final int SOUND_DEATH_IN_WATER = 15; + public static final int SOUND_DEATH_TO_ZOMBIE = 16; + public static final int SOUND_HURT = 17; + public static final int SOUND_HURT_IN_WATER = 18; + public static final int SOUND_MAD = 19; + public static final int SOUND_BOOST = 20; + public static final int SOUND_BOW = 21; + public static final int SOUND_SQUISH_BIG = 22; + public static final int SOUND_SQUISH_SMALL = 23; + public static final int SOUND_FALL_BIG = 24; + public static final int SOUND_FALL_SMALL = 25; + public static final int SOUND_SPLASH = 26; + public static final int SOUND_FIZZ = 27; + public static final int SOUND_FLAP = 28; + public static final int SOUND_SWIM = 29; + public static final int SOUND_DRINK = 30; + public static final int SOUND_EAT = 31; + public static final int SOUND_TAKEOFF = 32; + public static final int SOUND_SHAKE = 33; + public static final int SOUND_PLOP = 34; + public static final int SOUND_LAND = 35; + public static final int SOUND_SADDLE = 36; + public static final int SOUND_ARMOR = 37; + public static final int SOUND_ADD_CHEST = 38; + public static final int SOUND_THROW = 39; + public static final int SOUND_ATTACK = 40; + public static final int SOUND_ATTACK_NODAMAGE = 41; + public static final int SOUND_WARN = 42; + public static final int SOUND_SHEAR = 43; + public static final int SOUND_MILK = 44; + public static final int SOUND_THUNDER = 45; + public static final int SOUND_EXPLODE = 46; + public static final int SOUND_FIRE = 47; + public static final int SOUND_IGNITE = 48; + public static final int SOUND_FUSE = 49; + public static final int SOUND_STARE = 50; + public static final int SOUND_SPAWN = 51; + public static final int SOUND_SHOOT = 52; + public static final int SOUND_BREAK_BLOCK = 53; + public static final int SOUND_LAUNCH = 54; + public static final int SOUND_BLAST = 55; + public static final int SOUND_LARGE_BLAST = 56; + public static final int SOUND_TWINKLE = 57; + public static final int SOUND_REMEDY = 58; + public static final int SOUND_UNFECT = 59; + public static final int SOUND_LEVELUP = 60; + public static final int SOUND_BOW_HIT = 61; + public static final int SOUND_BULLET_HIT = 62; + public static final int SOUND_EXTINGUISH_FIRE = 63; + public static final int SOUND_ITEM_FIZZ = 64; + public static final int SOUND_CHEST_OPEN = 65; + public static final int SOUND_CHEST_CLOSED = 66; + public static final int SOUND_SHULKERBOX_OPEN = 67; + public static final int SOUND_SHULKERBOX_CLOSED = 68; + public static final int SOUND_POWER_ON = 69; + public static final int SOUND_POWER_OFF = 70; + public static final int SOUND_ATTACH = 71; + public static final int SOUND_DETACH = 72; + public static final int SOUND_DENY = 73; + public static final int SOUND_TRIPOD = 74; + public static final int SOUND_POP = 75; + public static final int SOUND_DROP_SLOT = 76; + public static final int SOUND_NOTE = 77; + public static final int SOUND_THORNS = 78; + public static final int SOUND_PISTON_IN = 79; + public static final int SOUND_PISTON_OUT = 80; + public static final int SOUND_PORTAL = 81; + public static final int SOUND_WATER = 82; + public static final int SOUND_LAVA_POP = 83; + public static final int SOUND_LAVA = 84; + public static final int SOUND_BURP = 85; + public static final int SOUND_BUCKET_FILL_WATER = 86; + public static final int SOUND_BUCKET_FILL_LAVA = 87; + public static final int SOUND_BUCKET_EMPTY_WATER = 88; + public static final int SOUND_BUCKET_EMPTY_LAVA = 89; + public static final int SOUND_RECORD_13 = 90; + public static final int SOUND_RECORD_CAT = 91; + public static final int SOUND_RECORD_BLOCKS = 92; + public static final int SOUND_RECORD_CHIRP = 93; + public static final int SOUND_RECORD_FAR = 94; + public static final int SOUND_RECORD_MALL = 95; + public static final int SOUND_RECORD_MELLOHI = 96; + public static final int SOUND_RECORD_STAL = 97; + public static final int SOUND_RECORD_STRAD = 98; + public static final int SOUND_RECORD_WARD = 99; + public static final int SOUND_RECORD_11 = 100; + public static final int SOUND_RECORD_WAIT = 101; + public static final int SOUND_GUARDIAN_FLOP = 103; + public static final int SOUND_ELDERGUARDIAN_CURSE = 104; + public static final int SOUND_MOB_WARNING = 105; + public static final int SOUND_MOB_WARNING_BABY = 106; + public static final int SOUND_TELEPORT = 107; + public static final int SOUND_SHULKER_OPEN = 108; + public static final int SOUND_SHULKER_CLOSE = 109; + public static final int SOUND_HAGGLE = 110; + public static final int SOUND_HAGGLE_YES = 111; + public static final int SOUND_HAGGLE_NO = 112; + public static final int SOUND_HAGGLE_IDLE = 113; + public static final int SOUND_CHORUSGROW = 114; + public static final int SOUND_CHORUSDEATH = 115; + public static final int SOUND_GLASS = 116; + public static final int SOUND_CAST_SPELL = 117; + public static final int SOUND_PREPARE_ATTACK = 118; + public static final int SOUND_PREPARE_SUMMON = 119; + public static final int SOUND_PREPARE_WOLOLO = 120; + public static final int SOUND_FANG = 121; + public static final int SOUND_CHARGE = 122; + public static final int SOUND_CAMERA_TAKE_PICTURE = 123; + public static final int SOUND_LEASHKNOT_PLACE = 124; + public static final int SOUND_LEASHKNOT_BREAK = 125; + public static final int SOUND_GROWL = 126; + public static final int SOUND_WHINE = 127; + public static final int SOUND_PANT = 128; + public static final int SOUND_PURR = 129; + public static final int SOUND_PURREOW = 130; + public static final int SOUND_DEATH_MIN_VOLUME = 131; + public static final int SOUND_DEATH_MID_VOLUME = 132; + public static final int SOUND_IMITATE_BLAZE = 133; + public static final int SOUND_IMITATE_CAVE_SPIDER = 134; + public static final int SOUND_IMITATE_CREEPER = 135; + public static final int SOUND_IMITATE_ELDER_GUARDIAN = 136; + public static final int SOUND_IMITATE_ENDER_DRAGON = 137; + public static final int SOUND_IMITATE_ENDERMAN = 138; + public static final int SOUND_IMITATE_EVOCATION_ILLAGER = 140; + public static final int SOUND_IMITATE_GHAST = 141; + public static final int SOUND_IMITATE_HUSK = 142; + public static final int SOUND_IMITATE_ILLUSION_ILLAGER = 143; + public static final int SOUND_IMITATE_MAGMA_CUBE = 144; + public static final int SOUND_IMITATE_POLAR_BEAR = 145; + public static final int SOUND_IMITATE_SHULKER = 146; + public static final int SOUND_IMITATE_SILVERFISH = 147; + public static final int SOUND_IMITATE_SKELETON = 148; + public static final int SOUND_IMITATE_SLIME = 149; + public static final int SOUND_IMITATE_SPIDER = 150; + public static final int SOUND_IMITATE_STRAY = 151; + public static final int SOUND_IMITATE_VEX = 152; + public static final int SOUND_IMITATE_VINDICATION_ILLAGER = 153; + public static final int SOUND_IMITATE_WITCH = 154; + public static final int SOUND_IMITATE_WITHER = 155; + public static final int SOUND_IMITATE_WITHER_SKELETON = 156; + public static final int SOUND_IMITATE_WOLF = 157; + public static final int SOUND_IMITATE_ZOMBIE = 158; + public static final int SOUND_IMITATE_ZOMBIE_PIGMAN = 159; + public static final int SOUND_IMITATE_ZOMBIE_VILLAGER = 160; + public static final int SOUND_DEFAULT = 161; + public static final int SOUND_UNDEFINED = 162; public int sound; public float x; public float y; public float z; - public int extraData = -1; - public int pitch = 1; - public boolean unknownBool; - public boolean disableRelativeVolume; + public int extraData = -1; //TODO: Check name + public int pitch = 1; //TODO: Check name + public boolean isBabyMob; + public boolean isGlobal; @Override public void decode() { @@ -133,8 +186,8 @@ public void decode() { this.z = v.z; this.extraData = this.getVarInt(); this.pitch = this.getVarInt(); - this.unknownBool = this.getBoolean(); - this.disableRelativeVolume = this.getBoolean(); + this.isBabyMob = this.getBoolean(); + this.isGlobal = this.getBoolean(); } @Override @@ -144,8 +197,8 @@ public void encode() { this.putVector3f(this.x, this.y, this.z); this.putVarInt(this.extraData); this.putVarInt(this.pitch); - this.putBoolean(this.unknownBool); - this.putBoolean(this.disableRelativeVolume); + this.putBoolean(this.isBabyMob); + this.putBoolean(this.isGlobal); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/LoginPacket.java b/src/main/java/cn/nukkit/network/protocol/LoginPacket.java index b05665487a..c4127a4790 100644 --- a/src/main/java/cn/nukkit/network/protocol/LoginPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/LoginPacket.java @@ -1,7 +1,6 @@ package cn.nukkit.network.protocol; import cn.nukkit.entity.data.Skin; -import cn.nukkit.utils.Zlib; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; @@ -22,11 +21,14 @@ public class LoginPacket extends DataPacket { public String username; public int protocol; - public byte gameEdition; public UUID clientUUID; public long clientId; public Skin skin; + public String skinGeometryName; + public byte[] skinGeometry; + + public byte[] capeData; @Override public byte pid() { @@ -36,7 +38,8 @@ public byte pid() { @Override public void decode() { this.protocol = this.getInt(); - this.gameEdition = (byte) this.getByte(); + //TODO: Remove it in future! Compatible with 1.1.x, then we can do something like kicking... + if (this.protocol <= 113) this.getByte(); this.setBuffer(this.getByteArray(), 0); decodeChainData(); decodeSkinData(); @@ -73,7 +76,16 @@ private void decodeSkinData() { String skinId = null; if (skinToken.has("ClientRandomId")) this.clientId = skinToken.get("ClientRandomId").getAsLong(); if (skinToken.has("SkinId")) skinId = skinToken.get("SkinId").getAsString(); - if (skinToken.has("SkinData")) this.skin = new Skin(skinToken.get("SkinData").getAsString(), skinId); + if (skinToken.has("SkinData")) { + this.skin = new Skin(skinToken.get("SkinData").getAsString(), skinId); + + if (skinToken.has("CapeData")) + this.skin.setCape(this.skin.new Cape(Base64.getDecoder().decode(skinToken.get("CapeData").getAsString()))); + } + + if (skinToken.has("SkinGeometryName")) this.skinGeometryName = skinToken.get("SkinGeometryName").getAsString(); + if (skinToken.has("SkinGeometry")) + this.skinGeometry = Base64.getDecoder().decode(skinToken.get("SkinGeometry").getAsString()); } private JsonObject decodeToken(String token) { diff --git a/src/main/java/cn/nukkit/network/protocol/MapInfoRequestPacket.java b/src/main/java/cn/nukkit/network/protocol/MapInfoRequestPacket.java index 0edf1e43f8..f28d1a46c6 100644 --- a/src/main/java/cn/nukkit/network/protocol/MapInfoRequestPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MapInfoRequestPacket.java @@ -13,7 +13,7 @@ public byte pid() { @Override public void decode() { - mapId = this.getVarLong(); + mapId = this.getEntityUniqueId(); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/MobArmorEquipmentPacket.java b/src/main/java/cn/nukkit/network/protocol/MobArmorEquipmentPacket.java index fa26b06c87..b34c6fc013 100644 --- a/src/main/java/cn/nukkit/network/protocol/MobArmorEquipmentPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MobArmorEquipmentPacket.java @@ -19,7 +19,7 @@ public byte pid() { @Override public void decode() { - this.eid = this.getVarLong(); + this.eid = this.getEntityRuntimeId(); this.slots = new Item[4]; this.slots[0] = this.getSlot(); this.slots[1] = this.getSlot(); @@ -30,7 +30,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityRuntimeId(this.eid); this.putSlot(this.slots[0]); this.putSlot(this.slots[1]); this.putSlot(this.slots[2]); diff --git a/src/main/java/cn/nukkit/network/protocol/MobEffectPacket.java b/src/main/java/cn/nukkit/network/protocol/MobEffectPacket.java index afa89f0f5d..c04a3f23ef 100644 --- a/src/main/java/cn/nukkit/network/protocol/MobEffectPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MobEffectPacket.java @@ -32,7 +32,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityRuntimeId(this.eid); this.putByte((byte) this.eventId); this.putVarInt(this.effectId); this.putVarInt(this.amplifier); diff --git a/src/main/java/cn/nukkit/network/protocol/MobEquipmentPacket.java b/src/main/java/cn/nukkit/network/protocol/MobEquipmentPacket.java index f1ea1ebd50..22c0033d1e 100644 --- a/src/main/java/cn/nukkit/network/protocol/MobEquipmentPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MobEquipmentPacket.java @@ -16,26 +16,26 @@ public byte pid() { public long eid; public Item item; - public int slot; - public int selectedSlot; + public int inventorySlot; + public int hotbarSlot; public int windowId; @Override public void decode() { - this.eid = this.getVarLong(); //EntityRuntimeID + this.eid = this.getEntityRuntimeId(); //EntityRuntimeID this.item = this.getSlot(); - this.slot = this.getByte(); - this.selectedSlot = this.getByte(); + this.inventorySlot = this.getByte(); + this.hotbarSlot = this.getByte(); this.windowId = this.getByte(); } @Override public void encode() { this.reset(); - this.putVarLong(this.eid); //EntityRuntimeID + this.putEntityRuntimeId(this.eid); //EntityRuntimeID this.putSlot(this.item); - this.putByte((byte) this.slot); - this.putByte((byte) this.selectedSlot); + this.putByte((byte) this.inventorySlot); + this.putByte((byte) this.hotbarSlot); this.putByte((byte) this.windowId); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ModalFormRequestPacket.java b/src/main/java/cn/nukkit/network/protocol/ModalFormRequestPacket.java new file mode 100644 index 0000000000..20418c820d --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/ModalFormRequestPacket.java @@ -0,0 +1,24 @@ +package cn.nukkit.network.protocol; + +public class ModalFormRequestPacket extends DataPacket { + + public int formId; + public String data; + + @Override + public byte pid() { + return ProtocolInfo.MODAL_FORM_REQUEST_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + this.reset(); + this.putVarInt(this.formId); + this.putString(this.data); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ModalFormResponsePacket.java b/src/main/java/cn/nukkit/network/protocol/ModalFormResponsePacket.java new file mode 100644 index 0000000000..a17743b136 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/ModalFormResponsePacket.java @@ -0,0 +1,23 @@ +package cn.nukkit.network.protocol; + +public class ModalFormResponsePacket extends DataPacket { + + public int formId; + public String data; + + @Override + public byte pid() { + return ProtocolInfo.MODAL_FORM_RESPONSE_PACKET; + } + + @Override + public void decode() { + this.formId = this.getVarInt(); + this.data = this.getString(); //Data will be null if player close form without submit (by cross button or ESC) + } + + @Override + public void encode() { + + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/MoveEntityPacket.java b/src/main/java/cn/nukkit/network/protocol/MoveEntityPacket.java index 5ff8ba1f0d..9fef170c0f 100644 --- a/src/main/java/cn/nukkit/network/protocol/MoveEntityPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MoveEntityPacket.java @@ -26,7 +26,7 @@ public byte pid() { @Override public void decode() { - this.eid = this.getVarLong(); + this.eid = this.getEntityRuntimeId(); Vector3f v = this.getVector3f(); this.x = v.x; this.y = v.y; @@ -41,7 +41,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityRuntimeId(this.eid); this.putVector3f((float) this.x, (float) this.y, (float) this.z); this.putByte((byte) (this.pitch / (360d / 256d))); this.putByte((byte) (this.headYaw / (360d / 256d))); diff --git a/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java b/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java index 1745370746..41e11ec247 100644 --- a/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/MovePlayerPacket.java @@ -9,10 +9,10 @@ public class MovePlayerPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.MOVE_PLAYER_PACKET; - public static final byte MODE_NORMAL = 0; - public static final byte MODE_RESET = 1; - public static final byte MODE_TELEPORT = 2; - public static final byte MODE_PITCH = 3; //facepalm Mojang + public static final int MODE_NORMAL = 0; + public static final int MODE_RESET = 1; + public static final int MODE_TELEPORT = 2; + public static final int MODE_PITCH = 3; //facepalm Mojang public long eid; public float x; @@ -21,7 +21,7 @@ public class MovePlayerPacket extends DataPacket { public float yaw; public float headYaw; public float pitch; - public byte mode = MODE_NORMAL; + public int mode = MODE_NORMAL; public boolean onGround; public long ridingEid; public int int1 = 0; @@ -29,7 +29,7 @@ public class MovePlayerPacket extends DataPacket { @Override public void decode() { - this.eid = this.getVarLong(); + this.eid = this.getEntityRuntimeId(); Vector3f v = this.getVector3f(); this.x = v.x; this.y = v.y; @@ -37,10 +37,10 @@ public void decode() { this.pitch = this.getLFloat(); this.headYaw = this.getLFloat(); this.yaw = this.getLFloat(); - this.mode = (byte) this.getByte(); + this.mode = this.getByte(); this.onGround = this.getBoolean(); - this.ridingEid = this.getVarLong(); - if (this.mode == MODE_TELEPORT){ + this.ridingEid = this.getEntityRuntimeId(); + if (this.mode == MODE_TELEPORT) { this.int1 = this.getLInt(); this.int2 = this.getLInt(); } @@ -49,15 +49,15 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityRuntimeId(this.eid); this.putVector3f(this.x, this.y, this.z); this.putLFloat(this.pitch); this.putLFloat(this.yaw); this.putLFloat(this.headYaw); - this.putByte(this.mode); + this.putByte((byte) this.mode); this.putBoolean(this.onGround); - this.putVarLong(this.ridingEid); - if (this.mode == MODE_TELEPORT){ + this.putEntityRuntimeId(this.ridingEid); + if (this.mode == MODE_TELEPORT) { this.putLInt(this.int1); this.putLInt(this.int2); } diff --git a/src/main/java/cn/nukkit/network/protocol/NPCRequestPacket.java b/src/main/java/cn/nukkit/network/protocol/NPCRequestPacket.java new file mode 100644 index 0000000000..99e79e9a10 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/NPCRequestPacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class NPCRequestPacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.NPC_REQUEST_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/PlaySoundPacket.java b/src/main/java/cn/nukkit/network/protocol/PlaySoundPacket.java index aa0106e6e4..f8b131f453 100644 --- a/src/main/java/cn/nukkit/network/protocol/PlaySoundPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/PlaySoundPacket.java @@ -23,8 +23,9 @@ public void decode() { @Override public void encode() { + this.reset(); this.putString(this.name); - this.putBlockCoords(this.x, this.y, this.z); + this.putBlockVector3(this.x * 8, this.y * 8, this.z * 8); this.putLFloat(this.volume); this.putLFloat(this.pitch); } diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerActionPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerActionPacket.java index bcc6de31d8..cab869e93b 100644 --- a/src/main/java/cn/nukkit/network/protocol/PlayerActionPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/PlayerActionPacket.java @@ -9,24 +9,25 @@ public class PlayerActionPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.PLAYER_ACTION_PACKET; - public static final byte ACTION_START_BREAK = 0; - public static final byte ACTION_ABORT_BREAK = 1; - public static final byte ACTION_STOP_BREAK = 2; - - public static final byte ACTION_RELEASE_ITEM = 5; - public static final byte ACTION_STOP_SLEEPING = 6; - public static final byte ACTION_RESPAWN = 7; - public static final byte ACTION_JUMP = 8; - public static final byte ACTION_START_SPRINT = 9; - public static final byte ACTION_STOP_SPRINT = 10; - public static final byte ACTION_START_SNEAK = 11; - public static final byte ACTION_STOP_SNEAK = 12; - public static final byte ACTION_DIMENSION_CHANGE = 13; - public static final byte ACTION_ABORT_DIMENSION_CHANGE = 14; - public static final byte ACTION_START_GLIDE = 15; - public static final byte ACTION_STOP_GLIDE = 16; - public static final byte ACTION_WORLD_IMMUTABLE = 17; - public static final byte ACTION_CONTINUE_BREAK = 18; + public static final int ACTION_START_BREAK = 0; + public static final int ACTION_ABORT_BREAK = 1; + public static final int ACTION_STOP_BREAK = 2; + public static final int ACTION_GET_UPDATED_BLOCK = 3; + public static final int ACTION_DROP_ITEM = 4; + public static final int ACTION_START_SLEEPING = 5; + public static final int ACTION_STOP_SLEEPING = 6; + public static final int ACTION_RESPAWN = 7; + public static final int ACTION_JUMP = 8; + public static final int ACTION_START_SPRINT = 9; + public static final int ACTION_STOP_SPRINT = 10; + public static final int ACTION_START_SNEAK = 11; + public static final int ACTION_STOP_SNEAK = 12; + public static final int ACTION_DIMENSION_CHANGE_REQUEST = 13; //sent when dying in different dimension + public static final int ACTION_DIMENSION_CHANGE_ACK = 14; //sent when spawning in a different dimension to tell the server we spawned + public static final int ACTION_START_GLIDE = 15; + public static final int ACTION_STOP_GLIDE = 16; + public static final int ACTION_BUILD_DENIED = 17; + public static final int ACTION_CONTINUE_BREAK = 18; public long entityId; public int action; @@ -38,9 +39,9 @@ public class PlayerActionPacket extends DataPacket { @Override public void decode() { - this.entityId = this.getVarLong(); + this.entityId = this.getEntityRuntimeId(); this.action = this.getVarInt(); - BlockVector3 v = this.getBlockCoords(); + BlockVector3 v = this.getBlockVector3(); this.x = v.x; this.y = v.y; this.z = v.z; @@ -50,9 +51,9 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.entityId); + this.putEntityRuntimeId(this.entityId); this.putVarInt(this.action); - this.putBlockCoords(this.x, this.y, this.z); + this.putBlockVector3(this.x, this.y, this.z); this.putVarInt(this.face); } diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerHotbarPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerHotbarPacket.java new file mode 100644 index 0000000000..0dc249f454 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/PlayerHotbarPacket.java @@ -0,0 +1,45 @@ +package cn.nukkit.network.protocol; + +import cn.nukkit.network.protocol.types.ContainerIds; +import cn.nukkit.utils.Binary; + +public class PlayerHotbarPacket extends DataPacket { + + public int selectedHotbarSlot; + public int windowId = ContainerIds.INVENTORY; + + public int[] slots; + + public boolean selectHotbarSlot = true; + + @Override + public byte pid() { + return ProtocolInfo.PLAYER_HOTBAR_PACKET; + } + + @Override + public void decode() { + this.selectedHotbarSlot = (int) this.getUnsignedVarInt(); + this.windowId = this.getByte(); + int count = (int) this.getUnsignedVarInt(); + slots = new int[count]; + + for (int i = 0; i < count; ++i) { + this.slots[i] = Binary.signInt((int) this.getUnsignedVarInt()); + } + this.selectHotbarSlot = this.getBoolean(); + } + + @Override + public void encode() { + this.reset(); + this.putUnsignedVarInt(this.selectedHotbarSlot); + this.putByte((byte) this.windowId); + this.putUnsignedVarInt(this.slots.length); + for (int i : slots) { + this.putUnsignedVarInt(i); + } + + this.putBoolean(this.selectHotbarSlot); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java index 09bb760ce8..26d9a5cf5d 100644 --- a/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/PlayerListPacket.java @@ -33,6 +33,10 @@ public void encode() { this.putVarLong(entry.entityId); this.putString(entry.name); this.putSkin(entry.skin); + this.putByteArray(entry.skin.getCape().getData()); + this.putString(entry.geometryModel); + this.putByteArray(entry.geometryData); + this.putString(entry.xboxUserId); } else { this.putUUID(entry.uuid); } @@ -51,16 +55,26 @@ public static class Entry { public long entityId = 0; public String name = ""; public Skin skin; + public byte[] capeData = new byte[0]; //TODO + public String geometryModel = ""; + public byte[] geometryData = new byte[0]; //TODO + public String xboxUserId = ""; //TODO public Entry(UUID uuid) { this.uuid = uuid; } public Entry(UUID uuid, long entityId, String name, Skin skin) { + this(uuid, entityId, name, skin, ""); + } + + public Entry(UUID uuid, long entityId, String name, Skin skin, String xboxUserId) { this.uuid = uuid; this.entityId = entityId; this.name = name; this.skin = skin; + this.capeData = skin.getCape().getData(); + this.xboxUserId = xboxUserId == null ? "" : xboxUserId; } } diff --git a/src/main/java/cn/nukkit/network/protocol/PlayerSkinPacket.java b/src/main/java/cn/nukkit/network/protocol/PlayerSkinPacket.java new file mode 100644 index 0000000000..b7de833ab5 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/PlayerSkinPacket.java @@ -0,0 +1,49 @@ +package cn.nukkit.network.protocol; + +import cn.nukkit.entity.data.Skin; + +import java.util.UUID; + +public class PlayerSkinPacket extends DataPacket { + + public UUID uuid; + public Skin skin; + public String skinName; + public String serializeName; + public String geometryModel; + public String geometryData; + + @Override + public byte pid() { + return ProtocolInfo.PLAYER_SKIN_PACKET; + } + + @Override + public void decode() { + this.uuid = this.getUUID(); + String skinId = this.getString(); + this.skinName = this.getString(); + this.serializeName = this.getString(); + byte[] data = this.getByteArray(); + byte[] cape = this.getByteArray(); + + this.skin = new Skin(data, skinId); + this.skin.setCape(this.skin.new Cape(cape)); + + this.geometryModel = this.getString(); + this.geometryData = this.getString(); + } + + @Override + public void encode() { + this.reset(); + this.putUUID(this.uuid); + this.putString(this.skin.getModel()); + this.putString(this.skinName); + this.putString(this.serializeName); + this.putByteArray(this.skin.getData()); + this.putByteArray(this.skin.getCape().getData()); + this.putString(this.geometryModel); + this.putString(this.geometryData); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java index b02cbebe68..1a1238f5e4 100644 --- a/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java +++ b/src/main/java/cn/nukkit/network/protocol/ProtocolInfo.java @@ -9,9 +9,10 @@ public interface ProtocolInfo { /** * Actual Minecraft: PE protocol version */ - byte CURRENT_PROTOCOL = 113; - String MINECRAFT_VERSION = "v1.1.3"; - String MINECRAFT_VERSION_NETWORK = "1.1.3"; + int CURRENT_PROTOCOL = Integer.valueOf("137"); //plugins can change it + + String MINECRAFT_VERSION = "v1.2.2"; + String MINECRAFT_VERSION_NETWORK = "1.2.2"; byte LOGIN_PACKET = 0x01; byte PLAY_STATUS_PACKET = 0x02; @@ -33,21 +34,21 @@ public interface ProtocolInfo { byte MOVE_ENTITY_PACKET = 0x12; byte MOVE_PLAYER_PACKET = 0x13; byte RIDER_JUMP_PACKET = 0x14; - byte REMOVE_BLOCK_PACKET = 0x15; - byte UPDATE_BLOCK_PACKET = 0x16; - byte ADD_PAINTING_PACKET = 0x17; - byte EXPLODE_PACKET = 0x18; - byte LEVEL_SOUND_EVENT_PACKET = 0x19; - byte LEVEL_EVENT_PACKET = 0x1a; - byte BLOCK_EVENT_PACKET = 0x1b; - byte ENTITY_EVENT_PACKET = 0x1c; - byte MOB_EFFECT_PACKET = 0x1d; - byte UPDATE_ATTRIBUTES_PACKET = 0x1e; + byte UPDATE_BLOCK_PACKET = 0x15; + byte ADD_PAINTING_PACKET = 0x16; + byte EXPLODE_PACKET = 0x17; + byte LEVEL_SOUND_EVENT_PACKET = 0x18; + byte LEVEL_EVENT_PACKET = 0x19; + byte BLOCK_EVENT_PACKET = 0x1a; + byte ENTITY_EVENT_PACKET = 0x1b; + byte MOB_EFFECT_PACKET = 0x1c; + byte UPDATE_ATTRIBUTES_PACKET = 0x1d; + byte INVENTORY_TRANSACTION_PACKET = 0x1e; byte MOB_EQUIPMENT_PACKET = 0x1f; byte MOB_ARMOR_EQUIPMENT_PACKET = 0x20; byte INTERACT_PACKET = 0x21; byte BLOCK_PICK_REQUEST_PACKET = 0x22; - byte USE_ITEM_PACKET = 0x23; + byte ENTITY_PICK_REQUEST_PACKET = 0x23; byte PLAYER_ACTION_PACKET = 0x24; byte ENTITY_FALL_PACKET = 0x25; byte HURT_ARMOR_PACKET = 0x26; @@ -58,15 +59,15 @@ public interface ProtocolInfo { byte SET_SPAWN_POSITION_PACKET = 0x2b; byte ANIMATE_PACKET = 0x2c; byte RESPAWN_PACKET = 0x2d; - byte DROP_ITEM_PACKET = 0x2e; - byte INVENTORY_ACTION_PACKET = 0x2f; - byte CONTAINER_OPEN_PACKET = 0x30; - byte CONTAINER_CLOSE_PACKET = 0x31; - byte CONTAINER_SET_SLOT_PACKET = 0x32; + byte CONTAINER_OPEN_PACKET = 0x2e; + byte CONTAINER_CLOSE_PACKET = 0x2f; + byte PLAYER_HOTBAR_PACKET = 0x30; + byte INVENTORY_CONTENT_PACKET = 0x31; + byte INVENTORY_SLOT_PACKET = 0x32; byte CONTAINER_SET_DATA_PACKET = 0x33; - byte CONTAINER_SET_CONTENT_PACKET = 0x34; - byte CRAFTING_DATA_PACKET = 0x35; - byte CRAFTING_EVENT_PACKET = 0x36; + byte CRAFTING_DATA_PACKET = 0x34; + byte CRAFTING_EVENT_PACKET = 0x35; + byte GUI_DATA_PICK_ITEM_PACKET = 0x36; byte ADVENTURE_SETTINGS_PACKET = 0x37; byte BLOCK_ENTITY_DATA_PACKET = 0x38; byte PLAYER_INPUT_PACKET = 0x39; @@ -77,32 +78,46 @@ public interface ProtocolInfo { byte SET_PLAYER_GAME_TYPE_PACKET = 0x3e; byte PLAYER_LIST_PACKET = 0x3f; byte SIMPLE_EVENT_PACKET = 0x40; - byte EVENT_PACKET = 0x41; + byte TELEMETRY_EVENT_PACKET = 0x41; byte SPAWN_EXPERIENCE_ORB_PACKET = 0x42; byte CLIENTBOUND_MAP_ITEM_DATA_PACKET = 0x43; byte MAP_INFO_REQUEST_PACKET = 0x44; byte REQUEST_CHUNK_RADIUS_PACKET = 0x45; byte CHUNK_RADIUS_UPDATED_PACKET = 0x46; byte ITEM_FRAME_DROP_ITEM_PACKET = 0x47; - byte REPLACE_ITEM_IN_SLOT_PACKET = 0x48; - byte GAME_RULES_CHANGED_PACKET = 0x49; - byte CAMERA_PACKET = 0x4a; - byte ADD_ITEM_PACKET = 0x4b; - byte BOSS_EVENT_PACKET = 0x4c; - byte SHOW_CREDITS_PACKET = 0x4d; - byte AVAILABLE_COMMANDS_PACKET = 0x4e; - byte COMMAND_STEP_PACKET = 0x4f; - byte COMMAND_BLOCK_UPDATE_PACKET = 0x50; - byte UPDATE_TRADE_PACKET = 0x51; - byte UPDATE_EQUIP_PACKET = 0x52; - byte RESOURCE_PACK_DATA_INFO_PACKET = 0x53; - byte RESOURCE_PACK_CHUNK_DATA_PACKET = 0x54; - byte RESOURCE_PACK_CHUNK_REQUEST_PACKET = 0x55; - byte TRANSFER_PACKET = 0x56; - byte PLAY_SOUND_PACKET = 0x57; - byte STOP_SOUND_PACKET = 0x58; - byte SET_TITLE_PACKET = 0x59; - byte ADD_BEHAVIOR_TREE_PACKET = 0x5a; - byte STRUCTURE_BLOCK_UPDATE_PACKET = 0x5b; + byte GAME_RULES_CHANGED_PACKET = 0x48; + byte CAMERA_PACKET = 0x49; + byte BOSS_EVENT_PACKET = 0x4a; + byte SHOW_CREDITS_PACKET = 0x4b; + byte AVAILABLE_COMMANDS_PACKET = 0x4c; + byte COMMAND_REQUEST_PACKET = 0x4d; + byte COMMAND_BLOCK_UPDATE_PACKET = 0x4e; + byte COMMAND_OUTPUT_PACKET = 0x4f; + byte UPDATE_TRADE_PACKET = 0x50; + byte UPDATE_EQUIPMENT_PACKET = 0x51; + byte RESOURCE_PACK_DATA_INFO_PACKET = 0x52; + byte RESOURCE_PACK_CHUNK_DATA_PACKET = 0x53; + byte RESOURCE_PACK_CHUNK_REQUEST_PACKET = 0x54; + byte TRANSFER_PACKET = 0x55; + byte PLAY_SOUND_PACKET = 0x56; + byte STOP_SOUND_PACKET = 0x57; + byte SET_TITLE_PACKET = 0x58; + byte ADD_BEHAVIOR_TREE_PACKET = 0x59; + byte STRUCTURE_BLOCK_UPDATE_PACKET = 0x5a; + byte SHOW_STORE_OFFER_PACKET = 0x5b; + byte PURCHASE_RECEIPT_PACKET = 0x5c; + byte PLAYER_SKIN_PACKET = 0x5d; + byte SUB_CLIENT_LOGIN_PACKET = 0x5e; + byte INITIATE_WEB_SOCKET_CONNECTION_PACKET = 0x5f; + byte SET_LAST_HURT_BY_PACKET = 0x60; + byte BOOK_EDIT_PACKET = 0x61; + byte NPC_REQUEST_PACKET = 0x62; + byte PHOTO_TRANSFER_PACKET = 0x63; + byte MODAL_FORM_REQUEST_PACKET = 0x64; + byte MODAL_FORM_RESPONSE_PACKET = 0x65; + byte SERVER_SETTINGS_REQUEST_PACKET = 0x66; + byte SERVER_SETTINGS_RESPONSE_PACKET = 0x67; + byte SHOW_PROFILE_PACKET = 0x68; + byte SET_DEFAULT_GAME_TYPE_PACKET = 0x69; byte BATCH_PACKET = (byte) 0xff; } diff --git a/src/main/java/cn/nukkit/network/protocol/RemoveBlockPacket.java b/src/main/java/cn/nukkit/network/protocol/RemoveBlockPacket.java deleted file mode 100644 index d20f2f1632..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/RemoveBlockPacket.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.math.BlockVector3; - -/** - * @author Nukkit Project Team - */ -public class RemoveBlockPacket extends DataPacket { - - public static final byte NETWORK_ID = ProtocolInfo.REMOVE_BLOCK_PACKET; - - public int x; - public int y; - public int z; - - @Override - public void decode() { - BlockVector3 v = this.getBlockCoords(); - this.x = v.x; - this.y = v.y; - this.z = v.z; - } - - @Override - public void encode() { - } - - @Override - public byte pid() { - return NETWORK_ID; - } - -} diff --git a/src/main/java/cn/nukkit/network/protocol/RemoveEntityPacket.java b/src/main/java/cn/nukkit/network/protocol/RemoveEntityPacket.java index 46cba714f1..1d5ac0dded 100644 --- a/src/main/java/cn/nukkit/network/protocol/RemoveEntityPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/RemoveEntityPacket.java @@ -22,6 +22,6 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityUniqueId(this.eid); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ReplaceItemInSlotPacket.java b/src/main/java/cn/nukkit/network/protocol/ReplaceItemInSlotPacket.java deleted file mode 100644 index a70b68b7e1..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/ReplaceItemInSlotPacket.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.item.Item; - -/** - * Created by Pub4Game on 29.04.2016. - */ -public class ReplaceItemInSlotPacket extends DataPacket { - - public static final byte NETWORK_ID = ProtocolInfo.REPLACE_ITEM_IN_SLOT_PACKET; - - public Item item; - - @Override - public void decode() { - - } - - @Override - public void encode() { - this.reset(); - this.putSlot(this.item); - } - - @Override - public byte pid() { - return NETWORK_ID; - } - -} \ No newline at end of file diff --git a/src/main/java/cn/nukkit/network/protocol/RiderJumpPacket.java b/src/main/java/cn/nukkit/network/protocol/RiderJumpPacket.java index 3ef8d1e88c..86b11887c0 100644 --- a/src/main/java/cn/nukkit/network/protocol/RiderJumpPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/RiderJumpPacket.java @@ -13,11 +13,12 @@ public byte pid() { @Override public void decode() { - + this.unknown = this.getVarInt(); } @Override public void encode() { + this.reset(); this.putVarInt(this.unknown); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ServerSettingsRequestPacket.java b/src/main/java/cn/nukkit/network/protocol/ServerSettingsRequestPacket.java new file mode 100644 index 0000000000..026f7a99e0 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/ServerSettingsRequestPacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class ServerSettingsRequestPacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.SERVER_SETTINGS_REQUEST_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ServerSettingsResponsePacket.java b/src/main/java/cn/nukkit/network/protocol/ServerSettingsResponsePacket.java new file mode 100644 index 0000000000..f7efce36dd --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/ServerSettingsResponsePacket.java @@ -0,0 +1,24 @@ +package cn.nukkit.network.protocol; + +public class ServerSettingsResponsePacket extends DataPacket { + + public int formId; + public String data; + + @Override + public byte pid() { + return ProtocolInfo.SERVER_SETTINGS_RESPONSE_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + this.reset(); + this.putVarInt(this.formId); + this.putString(this.data); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/ServerToClientHandshakePacket.java b/src/main/java/cn/nukkit/network/protocol/ServerToClientHandshakePacket.java new file mode 100644 index 0000000000..ac887d33e6 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/ServerToClientHandshakePacket.java @@ -0,0 +1,23 @@ +package cn.nukkit.network.protocol; + +public class ServerToClientHandshakePacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.SERVER_TO_CLIENT_HANDSHAKE_PACKET; + } + + public String publicKey; + public String serverToken; + public String privateKey; + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java b/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java index f15027cdb7..49e90f1b0e 100644 --- a/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/SetEntityDataPacket.java @@ -26,7 +26,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityRuntimeId(this.eid); this.put(Binary.writeMetadata(this.metadata)); } } diff --git a/src/main/java/cn/nukkit/network/protocol/SetEntityLinkPacket.java b/src/main/java/cn/nukkit/network/protocol/SetEntityLinkPacket.java index da60c96b78..863d8d41e3 100644 --- a/src/main/java/cn/nukkit/network/protocol/SetEntityLinkPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/SetEntityLinkPacket.java @@ -14,6 +14,7 @@ public class SetEntityLinkPacket extends DataPacket { public long rider; public long riding; public byte type; + public byte unknownByte; @Override public void decode() { @@ -23,14 +24,14 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.rider); - this.putVarLong(this.riding); + this.putEntityUniqueId(this.rider); + this.putEntityUniqueId(this.riding); this.putByte(this.type); + this.putByte(this.unknownByte); } @Override public byte pid() { return NETWORK_ID; } - } diff --git a/src/main/java/cn/nukkit/network/protocol/SetEntityMotionPacket.java b/src/main/java/cn/nukkit/network/protocol/SetEntityMotionPacket.java index 7ee5d898ed..7aa9ae9f09 100644 --- a/src/main/java/cn/nukkit/network/protocol/SetEntityMotionPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/SetEntityMotionPacket.java @@ -25,7 +25,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.eid); + this.putEntityRuntimeId(this.eid); this.putVector3f(this.motionX, this.motionY, this.motionZ); } } diff --git a/src/main/java/cn/nukkit/network/protocol/SetLastHurtByPacket.java b/src/main/java/cn/nukkit/network/protocol/SetLastHurtByPacket.java new file mode 100644 index 0000000000..46001b273a --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/SetLastHurtByPacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class SetLastHurtByPacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.SET_LAST_HURT_BY_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/SetSpawnPositionPacket.java b/src/main/java/cn/nukkit/network/protocol/SetSpawnPositionPacket.java index 1b58639039..a3f4293c64 100644 --- a/src/main/java/cn/nukkit/network/protocol/SetSpawnPositionPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/SetSpawnPositionPacket.java @@ -25,7 +25,7 @@ public void decode() { public void encode() { this.reset(); this.putVarInt(this.spawnType); - this.putBlockCoords(this.x, this.y, this.z); + this.putBlockVector3(this.x, this.y, this.z); this.putBoolean(this.spawnForced); } diff --git a/src/main/java/cn/nukkit/network/protocol/ShowCreditsPacket.java b/src/main/java/cn/nukkit/network/protocol/ShowCreditsPacket.java index 113cb11f0a..8089289499 100644 --- a/src/main/java/cn/nukkit/network/protocol/ShowCreditsPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/ShowCreditsPacket.java @@ -22,7 +22,8 @@ public void decode() { @Override public void encode() { - this.putVarLong(this.eid); + this.reset(); + this.putEntityRuntimeId(this.eid); this.putVarInt(this.status); } } diff --git a/src/main/java/cn/nukkit/network/protocol/ShowProfilePacket.java b/src/main/java/cn/nukkit/network/protocol/ShowProfilePacket.java new file mode 100644 index 0000000000..89c85bdf1a --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/ShowProfilePacket.java @@ -0,0 +1,28 @@ +package cn.nukkit.network.protocol; + +/** + * author: MagicDroidX + * Nukkit Project + */ +public class ShowProfilePacket extends DataPacket { + public static final byte NETWORK_ID = ProtocolInfo.SHOW_PROFILE_PACKET; + + public String xuid; + + @Override + public byte pid() { + return NETWORK_ID; + } + + @Override + public void decode() { + this.xuid = this.getString(); + } + + @Override + public void encode() { + this.reset(); + this.putString(this.xuid); + } + +} diff --git a/src/main/java/cn/nukkit/network/protocol/SimpleEventPacket.java b/src/main/java/cn/nukkit/network/protocol/SimpleEventPacket.java new file mode 100644 index 0000000000..a045ff1243 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/SimpleEventPacket.java @@ -0,0 +1,22 @@ +package cn.nukkit.network.protocol; + +public class SimpleEventPacket extends DataPacket { + + public short unknown; + + @Override + public byte pid() { + return ProtocolInfo.SIMPLE_EVENT_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + this.reset(); + this.putShort(this.unknown); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java b/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java index 5863e5b575..a376b6abe1 100644 --- a/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/StartGamePacket.java @@ -25,7 +25,7 @@ public byte pid() { public int seed; public byte dimension; public int generator = 1; - public int gamemode; + public int worldGamemode; public int difficulty; public int spawnX; public int spawnY; @@ -35,15 +35,24 @@ public byte pid() { public boolean eduMode = false; public float rainLevel; public float lightningLevel; + public boolean multiplayerGame = true; + public boolean broadcastToLAN = true; + public boolean broadcastToXboxLive = true; public boolean commandsEnabled; public boolean isTexturePacksRequired = false; public RuleData[] ruleDatas = new RuleData[0]; + public boolean bonusChest = false; + public boolean trustPlayers = false; + public int permissionLevel = 1; + public int gamePublish = 4; public String levelId = ""; //base64 string, usually the same as world folder name in vanilla public String worldName; public String premiumWorldTemplateId = ""; public boolean unknown = false; public long currentTick; + public int enchantmentSeed; + @Override public void decode() { @@ -52,8 +61,8 @@ public void decode() { @Override public void encode() { this.reset(); - this.putVarLong(this.entityUniqueId); - this.putVarLong(this.entityRuntimeId); + this.putEntityUniqueId(this.entityUniqueId); + this.putEntityRuntimeId(this.entityRuntimeId); this.putVarInt(this.playerGamemode); this.putVector3f(this.x, this.y, this.z); this.putLFloat(this.yaw); @@ -61,25 +70,33 @@ public void encode() { this.putVarInt(this.seed); this.putVarInt(this.dimension); this.putVarInt(this.generator); - this.putVarInt(this.gamemode); + this.putVarInt(this.worldGamemode); this.putVarInt(this.difficulty); - this.putBlockCoords(this.spawnX, this.spawnY, this.spawnZ); + this.putBlockVector3(this.spawnX, this.spawnY, this.spawnZ); this.putBoolean(this.hasAchievementsDisabled); this.putVarInt(this.dayCycleStopTime); this.putBoolean(this.eduMode); this.putLFloat(this.rainLevel); this.putLFloat(this.lightningLevel); + this.putBoolean(this.multiplayerGame); + this.putBoolean(this.broadcastToLAN); + this.putBoolean(this.broadcastToXboxLive); this.putBoolean(this.commandsEnabled); this.putBoolean(this.isTexturePacksRequired); this.putUnsignedVarInt(this.ruleDatas.length); for (RuleData rule : this.ruleDatas) { this.putRuleData(rule); } + this.putBoolean(this.bonusChest); + this.putBoolean(this.trustPlayers); + this.putVarInt(this.permissionLevel); + this.putVarInt(this.gamePublish); this.putString(this.levelId); this.putString(this.worldName); this.putString(this.premiumWorldTemplateId); this.putBoolean(this.unknown); this.putLLong(this.currentTick); + this.putVarInt(this.enchantmentSeed); } } diff --git a/src/main/java/cn/nukkit/network/protocol/StopSoundPacket.java b/src/main/java/cn/nukkit/network/protocol/StopSoundPacket.java index 0c9d45acfa..32cf956b50 100644 --- a/src/main/java/cn/nukkit/network/protocol/StopSoundPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/StopSoundPacket.java @@ -19,6 +19,7 @@ public void decode() { @Override public void encode() { + this.reset(); this.putString(this.name); this.putBoolean(this.stopAll); } diff --git a/src/main/java/cn/nukkit/network/protocol/StructureBlockUpdatePacket.java b/src/main/java/cn/nukkit/network/protocol/StructureBlockUpdatePacket.java new file mode 100644 index 0000000000..1d22735de2 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/StructureBlockUpdatePacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class StructureBlockUpdatePacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.STRUCTURE_BLOCK_UPDATE_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/SubClientLoginPacket.java b/src/main/java/cn/nukkit/network/protocol/SubClientLoginPacket.java new file mode 100644 index 0000000000..386e4f67cc --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/SubClientLoginPacket.java @@ -0,0 +1,19 @@ +package cn.nukkit.network.protocol; + +public class SubClientLoginPacket extends DataPacket { + + @Override + public byte pid() { + return ProtocolInfo.SUB_CLIENT_LOGIN_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + //TODO + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/TakeItemEntityPacket.java b/src/main/java/cn/nukkit/network/protocol/TakeItemEntityPacket.java index 93187f28c4..0604347484 100644 --- a/src/main/java/cn/nukkit/network/protocol/TakeItemEntityPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/TakeItemEntityPacket.java @@ -12,13 +12,15 @@ public class TakeItemEntityPacket extends DataPacket { @Override public void decode() { + this.target = this.getEntityRuntimeId(); + this.entityId = this.getEntityRuntimeId(); } @Override public void encode() { this.reset(); - this.putVarLong(this.target); - this.putVarLong(this.entityId); + this.putEntityRuntimeId(this.target); + this.putEntityRuntimeId(this.entityId); } @Override diff --git a/src/main/java/cn/nukkit/network/protocol/TelemetryEventPacket.java b/src/main/java/cn/nukkit/network/protocol/TelemetryEventPacket.java new file mode 100644 index 0000000000..d429d6ab8c --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/TelemetryEventPacket.java @@ -0,0 +1,26 @@ +package cn.nukkit.network.protocol; + +public class TelemetryEventPacket extends DataPacket { + + public long eid; + public int unknown1; + public byte unknown2; + + @Override + public byte pid() { + return ProtocolInfo.TELEMETRY_EVENT_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + this.reset(); + this.putVarLong(this.eid); + this.putVarInt(this.unknown1); + this.putByte(this.unknown2); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/TextPacket.java b/src/main/java/cn/nukkit/network/protocol/TextPacket.java index c975d1dcb7..29ef8c6dcf 100644 --- a/src/main/java/cn/nukkit/network/protocol/TextPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/TextPacket.java @@ -25,10 +25,12 @@ public byte pid() { public String source = ""; public String message = ""; public String[] parameters = new String[0]; + public boolean isLocalized = false; @Override public void decode() { this.type = (byte) getByte(); + this.isLocalized = this.getBoolean(); switch (type) { case TYPE_POPUP: case TYPE_CHAT: @@ -55,6 +57,7 @@ public void decode() { public void encode() { this.reset(); this.putByte(this.type); + this.putBoolean(this.isLocalized); switch (this.type) { case TYPE_POPUP: case TYPE_CHAT: diff --git a/src/main/java/cn/nukkit/network/protocol/TransferPacket.java b/src/main/java/cn/nukkit/network/protocol/TransferPacket.java index 2ba3af1e8a..0c15ef2dd8 100644 --- a/src/main/java/cn/nukkit/network/protocol/TransferPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/TransferPacket.java @@ -3,10 +3,10 @@ // A wild TransferPacket appeared! public class TransferPacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.TRANSFER_PACKET; - + public String address; // Server address public int port = 19132; // Server port - + @Override public void decode() { this.address = this.getString(); diff --git a/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java b/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java index eef6281ac6..d720f86e5f 100644 --- a/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/UpdateAttributesPacket.java @@ -24,7 +24,7 @@ public void decode() { public void encode() { this.reset(); - this.putVarLong(this.entityId); + this.putEntityRuntimeId(this.entityId); if (this.entries == null) { this.putUnsignedVarInt(0); diff --git a/src/main/java/cn/nukkit/network/protocol/UpdateBlockPacket.java b/src/main/java/cn/nukkit/network/protocol/UpdateBlockPacket.java index 1ca274b022..debadf376e 100644 --- a/src/main/java/cn/nukkit/network/protocol/UpdateBlockPacket.java +++ b/src/main/java/cn/nukkit/network/protocol/UpdateBlockPacket.java @@ -1,5 +1,6 @@ package cn.nukkit.network.protocol; + /** * author: MagicDroidX * Nukkit Project @@ -36,7 +37,7 @@ public void decode() { @Override public void encode() { this.reset(); - this.putBlockCoords(x, y, z); + this.putBlockVector3(x, y, z); this.putUnsignedVarInt(blockId); this.putUnsignedVarInt((0xb << 4) | blockData & 0xf); } diff --git a/src/main/java/cn/nukkit/network/protocol/UpdateEquipmentPacket.java b/src/main/java/cn/nukkit/network/protocol/UpdateEquipmentPacket.java new file mode 100644 index 0000000000..5a6f37ae42 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/UpdateEquipmentPacket.java @@ -0,0 +1,30 @@ +package cn.nukkit.network.protocol; + +public class UpdateEquipmentPacket extends DataPacket { + + public int windowId; + public int windowType; + public int unknown; //TODO: find out what this is (vanilla always sends 0) + public long eid; + public byte[] namedtag; + + + @Override + public byte pid() { + return ProtocolInfo.UPDATE_EQUIPMENT_PACKET; + } + + @Override + public void decode() { + + } + + @Override + public void encode() { + this.reset(); + this.putByte((byte) this.windowId); + this.putByte((byte) this.windowType); + this.putEntityUniqueId(this.eid); + this.put(this.namedtag); + } +} diff --git a/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java b/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java index 129ed4605e..28502ba3db 100644 --- a/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java +++ b/src/main/java/cn/nukkit/network/protocol/UpdateTradePacket.java @@ -4,8 +4,8 @@ public class UpdateTradePacket extends DataPacket { public static final byte NETWORK_ID = ProtocolInfo.UPDATE_TRADE_PACKET; - public byte unknownByte1; - public byte unknownByte2; + public byte windowId; + public byte windowType = 15; //trading id public int unknownVarInt1; public int unknownVarInt2; public boolean isWilling; @@ -26,13 +26,14 @@ public void decode() { @Override public void encode() { - this.putByte(unknownByte1); - this.putByte(unknownByte2); + this.reset(); + this.putByte(windowId); + this.putByte(windowType); this.putVarInt(unknownVarInt1); this.putVarInt(unknownVarInt2); this.putBoolean(isWilling); - this.putVarLong(player); - this.putVarLong(trader); + this.putEntityUniqueId(player); + this.putEntityUniqueId(trader); this.putString(displayName); this.put(this.offers); } diff --git a/src/main/java/cn/nukkit/network/protocol/UseItemPacket.java b/src/main/java/cn/nukkit/network/protocol/UseItemPacket.java deleted file mode 100644 index a490656a18..0000000000 --- a/src/main/java/cn/nukkit/network/protocol/UseItemPacket.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.nukkit.network.protocol; - -import cn.nukkit.item.Item; -import cn.nukkit.math.BlockVector3; -import cn.nukkit.math.Vector3f; - -/** - * @author Nukkit Project Team - */ -public class UseItemPacket extends DataPacket { - - public static final byte NETWORK_ID = ProtocolInfo.USE_ITEM_PACKET; - - public int x; - public int y; - public int z; - - public int interactBlockId; - - public int face; - - public float fx; - public float fy; - public float fz; - - public float posX; - public float posY; - public float posZ; - - public int slot; - - public Item item; - - @Override - public byte pid() { - return NETWORK_ID; - } - - @Override - public void decode() { - BlockVector3 v = this.getBlockCoords(); - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.interactBlockId = (int) this.getUnsignedVarInt(); - this.face = this.getVarInt(); - Vector3f faceVector3 = this.getVector3f(); - this.fx = faceVector3.x; - this.fy = faceVector3.y; - this.fz = faceVector3.z; - Vector3f playerPos = this.getVector3f(); - this.posX = playerPos.x; - this.posY = playerPos.y; - this.posZ = playerPos.z; - this.slot = this.getByte(); - this.item = this.getSlot(); - } - - @Override - public void encode() { - - } - -} diff --git a/src/main/java/cn/nukkit/network/protocol/types/ContainerIds.java b/src/main/java/cn/nukkit/network/protocol/types/ContainerIds.java new file mode 100644 index 0000000000..539b8098ca --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/types/ContainerIds.java @@ -0,0 +1,18 @@ +package cn.nukkit.network.protocol.types; + +/** + * @author CreeperFace + */ +public interface ContainerIds { + + int NONE = -1; + int INVENTORY = 0; + int FIRST = 1; + int LAST = 100; + int OFFHAND = 119; + int ARMOR = 120; + int CREATIVE = 121; + int HOTBAR = 122; + int FIXED_INVENTORY = 123; + int CURSOR = 124; +} diff --git a/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java b/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java new file mode 100644 index 0000000000..ea76a85ec4 --- /dev/null +++ b/src/main/java/cn/nukkit/network/protocol/types/NetworkInventoryAction.java @@ -0,0 +1,233 @@ +package cn.nukkit.network.protocol.types; + +import cn.nukkit.Player; +import cn.nukkit.inventory.AnvilInventory; +import cn.nukkit.inventory.EnchantInventory; +import cn.nukkit.inventory.Inventory; +import cn.nukkit.inventory.transaction.action.CreativeInventoryAction; +import cn.nukkit.inventory.transaction.action.DropItemAction; +import cn.nukkit.inventory.transaction.action.InventoryAction; +import cn.nukkit.inventory.transaction.action.SlotChangeAction; +import cn.nukkit.item.Item; +import cn.nukkit.network.protocol.InventoryTransactionPacket; + +/** + * @author CreeperFace + */ +public class NetworkInventoryAction { + + public static final int SOURCE_CONTAINER = 0; + + public static final int SOURCE_WORLD = 2; //drop/pickup item entity + public static final int SOURCE_CREATIVE = 3; + public static final int SOURCE_TODO = 99999; + + /** + * Fake window IDs for the SOURCE_TODO type (99999) + *

+ * These identifiers are used for inventory source types which are not currently implemented server-side in MCPE. + * As a general rule of thumb, anything that doesn't have a permanent inventory is client-side. These types are + * to allow servers to track what is going on in client-side windows. + *

+ * Expect these to change in the future. + */ + public static final int SOURCE_TYPE_CRAFTING_ADD_INGREDIENT = -2; + public static final int SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT = -3; + public static final int SOURCE_TYPE_CRAFTING_RESULT = -4; + public static final int SOURCE_TYPE_CRAFTING_USE_INGREDIENT = -5; + + public static final int SOURCE_TYPE_ANVIL_INPUT = -10; + public static final int SOURCE_TYPE_ANVIL_MATERIAL = -11; + public static final int SOURCE_TYPE_ANVIL_RESULT = -12; + public static final int SOURCE_TYPE_ANVIL_OUTPUT = -13; + + public static final int SOURCE_TYPE_ENCHANT_INPUT = -15; + public static final int SOURCE_TYPE_ENCHANT_MATERIAL = -16; + public static final int SOURCE_TYPE_ENCHANT_OUTPUT = -17; + + public static final int SOURCE_TYPE_TRADING_INPUT_1 = -20; + public static final int SOURCE_TYPE_TRADING_INPUT_2 = -21; + public static final int SOURCE_TYPE_TRADING_USE_INPUTS = -22; + public static final int SOURCE_TYPE_TRADING_OUTPUT = -23; + + public static final int SOURCE_TYPE_BEACON = -24; + + /** + * Any client-side window dropping its contents when the player closes it + */ + public static final int SOURCE_TYPE_CONTAINER_DROP_CONTENTS = -100; + + + public int sourceType; + public int windowId; + public long unknown; + public int inventorySlot; + public Item oldItem; + public Item newItem; + + public NetworkInventoryAction read(InventoryTransactionPacket packet) { + this.sourceType = (int) packet.getUnsignedVarInt(); + + switch (this.sourceType) { + case SOURCE_CONTAINER: + this.windowId = packet.getVarInt(); + break; + case SOURCE_WORLD: + this.unknown = packet.getUnsignedVarInt(); + break; + case SOURCE_CREATIVE: + break; + case SOURCE_TODO: + this.windowId = packet.getVarInt(); + break; + } + + this.inventorySlot = (int) packet.getUnsignedVarInt(); + this.oldItem = packet.getSlot(); + this.newItem = packet.getSlot(); + + return this; + } + + public void write(InventoryTransactionPacket packet) { + packet.putUnsignedVarInt(this.sourceType); + + switch (this.sourceType) { + case SOURCE_CONTAINER: + packet.putVarInt(this.windowId); + break; + case SOURCE_WORLD: + packet.putUnsignedVarInt(this.unknown); + break; + case SOURCE_CREATIVE: + break; + case SOURCE_TODO: + packet.putVarInt(this.windowId); + break; + } + + packet.putUnsignedVarInt(this.inventorySlot); + packet.putSlot(this.oldItem); + packet.putSlot(this.newItem); + } + + public InventoryAction createInventoryAction(Player player) { + switch (this.sourceType) { + case SOURCE_CONTAINER: + if (this.windowId == ContainerIds.ARMOR) { + //TODO: HACK! + this.inventorySlot += 36; + this.windowId = ContainerIds.INVENTORY; + } + + Inventory window = player.getWindowById(this.windowId); + if (window != null) { + return new SlotChangeAction(window, this.inventorySlot, this.oldItem, this.newItem); + } + + throw new RuntimeException("Player " + player.getName() + " has no open container with window ID " + this.windowId); + case SOURCE_WORLD: + if (this.inventorySlot != InventoryTransactionPacket.ACTION_MAGIC_SLOT_DROP_ITEM) { + throw new RuntimeException("Only expecting drop-item world actions from the client!"); + } + + return new DropItemAction(this.oldItem, this.newItem); + case SOURCE_CREATIVE: + int type; + + switch (this.inventorySlot) { + case InventoryTransactionPacket.ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM: + type = CreativeInventoryAction.TYPE_DELETE_ITEM; + break; + case InventoryTransactionPacket.ACTION_MAGIC_SLOT_CREATIVE_CREATE_ITEM: + type = CreativeInventoryAction.TYPE_CREATE_ITEM; + break; + default: + throw new RuntimeException("Unexpected creative action type " + this.inventorySlot); + + } + + return new CreativeInventoryAction(this.oldItem, this.newItem, type); + case SOURCE_TODO: + //These types need special handling. + switch (this.windowId) { + case SOURCE_TYPE_CRAFTING_ADD_INGREDIENT: + case SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT: + case SOURCE_TYPE_CRAFTING_RESULT: + case SOURCE_TYPE_CRAFTING_USE_INGREDIENT: + window = player.getCraftingGrid(); + return new SlotChangeAction(window, this.inventorySlot, this.oldItem, this.newItem); + case SOURCE_TYPE_CONTAINER_DROP_CONTENTS: + window = player.getCraftingGrid(); + inventorySlot = window.first(this.oldItem, true); + if (inventorySlot == -1) { + throw new RuntimeException("Fake container " + window.getClass().getName() + " for " + player.getName() + " does not contain " + this.oldItem); + } + return new SlotChangeAction(window, inventorySlot, this.oldItem, this.newItem); + } + + if (this.windowId >= SOURCE_TYPE_ANVIL_OUTPUT && this.windowId <= SOURCE_TYPE_ANVIL_INPUT) { //anvil actions + Inventory inv = player.getWindowById(Player.ANVIL_WINDOW_ID); + + if (!(inv instanceof AnvilInventory)) { + throw new RuntimeException("Player " + player.getName() + " has no open anvil inventory"); + } + AnvilInventory anvil = (AnvilInventory) inv; + + 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); + anvil.clear(1); + anvil.setItem(2, this.oldItem); + //System.out.println("action result"); + return new SlotChangeAction(anvil, this.inventorySlot, this.oldItem, this.newItem); + } + } + + if (this.windowId >= SOURCE_TYPE_ENCHANT_OUTPUT && this.windowId <= SOURCE_TYPE_ENCHANT_INPUT) { + Inventory inv = player.getWindowById(Player.ENCHANT_WINDOW_ID); + + if (!(inv instanceof EnchantInventory)) { + throw new RuntimeException("Player " + player.getName() + " has no open enchant inventory"); + } + EnchantInventory enchant = (EnchantInventory) inv; + + switch (this.windowId) { + case SOURCE_TYPE_ENCHANT_INPUT: + this.inventorySlot = 0; + Item local = enchant.getItem(0); + if (local.equals(this.newItem, true, false)) { + enchant.setItem(0, this.newItem); + } + break; + case SOURCE_TYPE_ENCHANT_MATERIAL: + this.inventorySlot = 1; + break; + case SOURCE_TYPE_ENCHANT_OUTPUT: + enchant.sendSlot(0, player); + //ignore? + return null; + } + + return new SlotChangeAction(enchant, this.inventorySlot, this.oldItem, this.newItem); + } + + //TODO: more stuff + throw new RuntimeException("Player " + player.getName() + " has no open container with window ID " + this.windowId); + default: + throw new RuntimeException("Unknown inventory source type " + this.sourceType); + } + } +} diff --git a/src/main/java/cn/nukkit/network/query/QueryHandler.java b/src/main/java/cn/nukkit/network/query/QueryHandler.java index de232afe81..ff5fee844f 100644 --- a/src/main/java/cn/nukkit/network/query/QueryHandler.java +++ b/src/main/java/cn/nukkit/network/query/QueryHandler.java @@ -28,7 +28,7 @@ public QueryHandler() { this.server = Server.getInstance(); this.server.getLogger().info(this.server.getLanguage().translateString("nukkit.server.query.start")); String ip = this.server.getIp(); - String addr = (!"".equals(ip)) ? ip : "0.0.0.0"; + String addr = (!ip.isEmpty()) ? ip : "0.0.0.0"; int port = this.server.getPort(); this.server.getLogger().info(this.server.getLanguage().translateString("nukkit.server.query.info", String.valueOf(port))); diff --git a/src/main/java/cn/nukkit/plugin/PluginDescription.java b/src/main/java/cn/nukkit/plugin/PluginDescription.java index 3140a7958e..f9449dccc4 100644 --- a/src/main/java/cn/nukkit/plugin/PluginDescription.java +++ b/src/main/java/cn/nukkit/plugin/PluginDescription.java @@ -142,7 +142,7 @@ private void loadMap(Map plugin) throws PluginException { throw new PluginException("Invalid PluginDescription name"); } this.name = this.name.replace(" ", "_"); - this.version = (String) plugin.get("version"); + this.version = String.valueOf(plugin.get("version")); this.main = (String) plugin.get("main"); Object api = plugin.get("api"); if (api instanceof List) { diff --git a/src/main/java/cn/nukkit/potion/Effect.java b/src/main/java/cn/nukkit/potion/Effect.java index 4bd03b89d6..b6d47800a3 100644 --- a/src/main/java/cn/nukkit/potion/Effect.java +++ b/src/main/java/cn/nukkit/potion/Effect.java @@ -4,7 +4,6 @@ import cn.nukkit.entity.Entity; import cn.nukkit.event.entity.EntityDamageEvent; import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; -import cn.nukkit.event.entity.EntityEvent; import cn.nukkit.event.entity.EntityRegainHealthEvent; import cn.nukkit.network.protocol.MobEffectPacket; import cn.nukkit.utils.ServerException; @@ -262,6 +261,11 @@ public void add(Entity entity, boolean modify) { entity.setDataFlag(Entity.DATA_FLAGS, Entity.DATA_FLAG_INVISIBLE, true); entity.setNameTagVisible(false); } + + if (this.id == Effect.ABSORPTION) { + int add = (this.amplifier + 1) * 4; + if (add > entity.getAbsorption()) entity.setAbsorption(add); + } } public void remove(Entity entity) { @@ -282,6 +286,10 @@ public void remove(Entity entity) { entity.setDataFlag(Entity.DATA_FLAGS, Entity.DATA_FLAG_INVISIBLE, false); entity.setNameTagVisible(true); } + + if (this.id == Effect.ABSORPTION) { + entity.setAbsorption(0); + } } @Override diff --git a/src/main/java/cn/nukkit/potion/Potion.java b/src/main/java/cn/nukkit/potion/Potion.java index 83e0524c7c..60c7482255 100644 --- a/src/main/java/cn/nukkit/potion/Potion.java +++ b/src/main/java/cn/nukkit/potion/Potion.java @@ -175,13 +175,15 @@ public void applyPotion(Entity entity, double health) { } } - PotionApplyEvent event = new PotionApplyEvent(this, entity); + PotionApplyEvent event = new PotionApplyEvent(this, applyEffect, entity); entity.getServer().getPluginManager().callEvent(event); if (event.isCancelled()) { return; } + applyEffect = event.getApplyEffect(); + switch (this.getId()) { case INSTANT_HEALTH: case INSTANT_HEALTH_II: diff --git a/src/main/java/cn/nukkit/raknet/RakNet.java b/src/main/java/cn/nukkit/raknet/RakNet.java index 7e2110ec7f..e76a018149 100644 --- a/src/main/java/cn/nukkit/raknet/RakNet.java +++ b/src/main/java/cn/nukkit/raknet/RakNet.java @@ -89,13 +89,20 @@ public abstract class RakNet { public static final byte PACKET_RAW = 0x08; /* - * RAW payload: + * BLOCK_ADDRESS payload: * byte (address length) * byte[] (address) * int (timeout) */ public static final byte PACKET_BLOCK_ADDRESS = 0x09; + /* + * UNBLOCK_ADDRESS payload: + * byte (adress length) + * byte[] (address) + */ + public static final byte PACKET_UNBLOCK_ADDRESS = 0x10; + /* * No payload * diff --git a/src/main/java/cn/nukkit/raknet/server/ServerHandler.java b/src/main/java/cn/nukkit/raknet/server/ServerHandler.java index 23c9748676..c75e462d8c 100644 --- a/src/main/java/cn/nukkit/raknet/server/ServerHandler.java +++ b/src/main/java/cn/nukkit/raknet/server/ServerHandler.java @@ -78,6 +78,15 @@ public void blockAddress(String address, int timeout) { this.server.pushMainToThreadPacket(buffer); } + public void unblockAddress(String address) { + byte[] buffer = Binary.appendBytes( + RakNet.PACKET_UNBLOCK_ADDRESS, + new byte[]{(byte) (address.length() & 0xff)}, + address.getBytes(StandardCharsets.UTF_8) + ); + this.server.pushMainToThreadPacket(buffer); + } + public void shutdown() { this.server.pushMainToThreadPacket(new byte[]{RakNet.PACKET_SHUTDOWN}); this.server.shutdown(); diff --git a/src/main/java/cn/nukkit/raknet/server/SessionManager.java b/src/main/java/cn/nukkit/raknet/server/SessionManager.java index b97e7cd46b..df6053554e 100644 --- a/src/main/java/cn/nukkit/raknet/server/SessionManager.java +++ b/src/main/java/cn/nukkit/raknet/server/SessionManager.java @@ -82,7 +82,7 @@ private void tickProcessor() throws Exception { } --max; } catch (Exception e) { - if (!"".equals(currentSource)) { + if (!currentSource.isEmpty()) { this.blockAddress(currentSource); } // else ignore @@ -383,6 +383,11 @@ public boolean receiveStream() throws Exception { int timeout = Binary.readInt(Binary.subBytes(packet, offset, 4)); this.blockAddress(address, timeout); break; + case RakNet.PACKET_UNBLOCK_ADDRESS: + len = packet[offset++]; + address = new String(Binary.subBytes(packet, offset, len), StandardCharsets.UTF_8); + this.unblockAddress(address); + break; case RakNet.PACKET_SHUTDOWN: for (Session session : new ArrayList<>(this.sessions.values())) { this.removeSession(session); @@ -420,6 +425,10 @@ public void blockAddress(String address, int timeout) { } } + public void unblockAddress(String address) { + this.block.remove(address); + } + public Session getSession(String ip, int port) { String id = ip + ":" + port; if (!this.sessions.containsKey(id)) { diff --git a/src/main/java/cn/nukkit/resourcepacks/AbstractResourcePack.java b/src/main/java/cn/nukkit/resourcepacks/AbstractResourcePack.java index 440435e0d9..0c6ae3fdb3 100644 --- a/src/main/java/cn/nukkit/resourcepacks/AbstractResourcePack.java +++ b/src/main/java/cn/nukkit/resourcepacks/AbstractResourcePack.java @@ -9,7 +9,7 @@ public abstract class AbstractResourcePack implements ResourcePack { protected boolean verifyManifest() { if (this.manifest.has("format_version") && this.manifest.has("header") && this.manifest.has("modules")) { JsonObject header = this.manifest.getAsJsonObject("header"); - return header.has("description") && + return header.has("description") && header.has("name") && header.has("uuid") && header.has("version") && diff --git a/src/main/java/cn/nukkit/scheduler/AsyncPool.java b/src/main/java/cn/nukkit/scheduler/AsyncPool.java index 42a0044089..ed3b03fdca 100644 --- a/src/main/java/cn/nukkit/scheduler/AsyncPool.java +++ b/src/main/java/cn/nukkit/scheduler/AsyncPool.java @@ -5,7 +5,6 @@ import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; /** * @author Nukkit Project Team diff --git a/src/main/java/cn/nukkit/scheduler/NukkitRunnable.java b/src/main/java/cn/nukkit/scheduler/NukkitRunnable.java index ee992d88f5..55db31ab63 100644 --- a/src/main/java/cn/nukkit/scheduler/NukkitRunnable.java +++ b/src/main/java/cn/nukkit/scheduler/NukkitRunnable.java @@ -24,31 +24,31 @@ public synchronized Runnable runTask(Plugin plugin) throws IllegalArgumentExcept return taskHandler.getTask(); } - public synchronized Runnable runTaskAsynchronously(Plugin plugin) throws IllegalArgumentException, IllegalStateException { + public synchronized Runnable runTaskAsynchronously(Plugin plugin) throws IllegalArgumentException, IllegalStateException { checkState(); this.taskHandler = Server.getInstance().getScheduler().scheduleTask(plugin, this, true); return taskHandler.getTask(); } - public synchronized Runnable runTaskLater(Plugin plugin, int delay) throws IllegalArgumentException, IllegalStateException { + public synchronized Runnable runTaskLater(Plugin plugin, int delay) throws IllegalArgumentException, IllegalStateException { checkState(); this.taskHandler = Server.getInstance().getScheduler().scheduleDelayedTask(plugin, this, delay); return taskHandler.getTask(); } - public synchronized Runnable runTaskLaterAsynchronously(Plugin plugin, int delay) throws IllegalArgumentException, IllegalStateException { + public synchronized Runnable runTaskLaterAsynchronously(Plugin plugin, int delay) throws IllegalArgumentException, IllegalStateException { checkState(); this.taskHandler = Server.getInstance().getScheduler().scheduleDelayedTask(plugin, this, delay, true); return taskHandler.getTask(); } - public synchronized Runnable runTaskTimer(Plugin plugin, int delay, int period) throws IllegalArgumentException, IllegalStateException { + public synchronized Runnable runTaskTimer(Plugin plugin, int delay, int period) throws IllegalArgumentException, IllegalStateException { checkState(); this.taskHandler = Server.getInstance().getScheduler().scheduleDelayedRepeatingTask(plugin, this, delay, period); return taskHandler.getTask(); } - public synchronized Runnable runTaskTimerAsynchronously(Plugin plugin, int delay, int period) throws IllegalArgumentException, IllegalStateException { + public synchronized Runnable runTaskTimerAsynchronously(Plugin plugin, int delay, int period) throws IllegalArgumentException, IllegalStateException { checkState(); this.taskHandler = Server.getInstance().getScheduler().scheduleDelayedRepeatingTask(plugin, this, delay, period, true); return taskHandler.getTask(); diff --git a/src/main/java/cn/nukkit/scheduler/ServerScheduler.java b/src/main/java/cn/nukkit/scheduler/ServerScheduler.java index c07e6ef469..ad62bafbd0 100644 --- a/src/main/java/cn/nukkit/scheduler/ServerScheduler.java +++ b/src/main/java/cn/nukkit/scheduler/ServerScheduler.java @@ -57,7 +57,7 @@ public TaskHandler scheduleTask(Runnable task) { public TaskHandler scheduleTask(Plugin plugin, Runnable task) { return addTask(plugin, task, 0, 0, false); } - + /** * @deprecated Use {@link #scheduleTask(Plugin, Runnable, boolean) */ @@ -69,7 +69,7 @@ public TaskHandler scheduleTask(Runnable task, boolean asynchronous) { public TaskHandler scheduleTask(Plugin plugin, Runnable task, boolean asynchronous) { return addTask(plugin, task, 0, 0, asynchronous); } - + /** * @deprecated Use {@link #scheduleAsyncTask(Plugin, AsyncTask) */ @@ -81,7 +81,7 @@ public TaskHandler scheduleAsyncTask(AsyncTask task) { public TaskHandler scheduleAsyncTask(Plugin plugin, AsyncTask task) { return addTask(plugin, task, 0, 0, true); } - + @Deprecated public void scheduleAsyncTaskToWorker(AsyncTask task, int worker) { scheduleAsyncTask(task); @@ -114,7 +114,7 @@ public TaskHandler scheduleDelayedTask(Runnable task, int delay) { public TaskHandler scheduleDelayedTask(Plugin plugin, Runnable task, int delay) { return addTask(plugin, task, delay, 0, false); } - + /** * @deprecated Use {@link #scheduleDelayedTask(Plugin, Runnable, int, boolean) */ @@ -126,7 +126,7 @@ public TaskHandler scheduleDelayedTask(Runnable task, int delay, boolean asynchr public TaskHandler scheduleDelayedTask(Plugin plugin, Runnable task, int delay, boolean asynchronous) { return addTask(plugin, task, delay, 0, asynchronous); } - + /** * @deprecated Use {@link #scheduleRepeatingTask(Plugin, Runnable, int) */ @@ -134,7 +134,7 @@ public TaskHandler scheduleDelayedTask(Plugin plugin, Runnable task, int delay, public TaskHandler scheduleRepeatingTask(Runnable task, int period) { return addTask(null, task, 0, period, false); } - + public TaskHandler scheduleRepeatingTask(Plugin plugin, Runnable task, int period) { return addTask(plugin, task, 0, period, false); } @@ -150,7 +150,7 @@ public TaskHandler scheduleRepeatingTask(Runnable task, int period, boolean asyn public TaskHandler scheduleRepeatingTask(Plugin plugin, Runnable task, int period, boolean asynchronous) { return addTask(plugin, task, 0, period, asynchronous); } - + public TaskHandler scheduleRepeatingTask(Task task, int period) { return addTask(task, 0, period, false); } @@ -174,7 +174,7 @@ public TaskHandler scheduleDelayedRepeatingTask(Task task, int delay, int period public TaskHandler scheduleDelayedRepeatingTask(Runnable task, int delay, int period) { return addTask(null, task, delay, period, false); } - + public TaskHandler scheduleDelayedRepeatingTask(Plugin plugin, Runnable task, int delay, int period) { return addTask(plugin, task, delay, period, false); } @@ -186,7 +186,7 @@ public TaskHandler scheduleDelayedRepeatingTask(Plugin plugin, Runnable task, in public TaskHandler scheduleDelayedRepeatingTask(Runnable task, int delay, int period, boolean asynchronous) { return addTask(null, task, delay, period, asynchronous); } - + public TaskHandler scheduleDelayedRepeatingTask(Plugin plugin, Runnable task, int delay, int period, boolean asynchronous) { return addTask(plugin, task, delay, period, asynchronous); } diff --git a/src/main/java/cn/nukkit/utils/Binary.java b/src/main/java/cn/nukkit/utils/Binary.java index 9405f1b4e9..c07c44cf19 100644 --- a/src/main/java/cn/nukkit/utils/Binary.java +++ b/src/main/java/cn/nukkit/utils/Binary.java @@ -18,6 +18,30 @@ */ public class Binary { + public static int signByte(int value) { + return value << 56 >> 56; + } + + public static int unsignByte(int value) { + return value & 0xff; + } + + public static int signShort(int value) { + return value << 48 >> 48; + } + + public int unsignShort(int value) { + return value & 0xffff; + } + + public static int signInt(int value) { + return value << 32 >> 32; + } + + public static int unsignInt(int value) { + return value; + } + //Triad: {0x00,0x00,0x01}<=>1 public static int readTriad(byte[] bytes) { return readInt(new byte[]{ @@ -55,7 +79,7 @@ public static byte[] writeLTriad(int value) { } public static UUID readUUID(byte[] bytes) { - return new UUID(readLong(bytes), readLong(new byte[]{ + return new UUID(readLLong(bytes), readLLong(new byte[]{ bytes[8], bytes[9], bytes[10], @@ -68,7 +92,7 @@ public static UUID readUUID(byte[] bytes) { } public static byte[] writeUUID(UUID uuid) { - return appendBytes(writeLong(uuid.getMostSignificantBits()), writeLong(uuid.getLeastSignificantBits())); + return appendBytes(writeLLong(uuid.getMostSignificantBits()), writeLLong(uuid.getLeastSignificantBits())); } public static byte[] writeMetadata(EntityMetadata metadata) { @@ -99,14 +123,12 @@ public static byte[] writeMetadata(EntityMetadata metadata) { break; case Entity.DATA_TYPE_SLOT: SlotEntityData slot = (SlotEntityData) d; - stream.putLShort(slot.blockId); - stream.putByte((byte) slot.meta); - stream.putLShort(slot.count); + stream.putSlot(slot.getData()); break; case Entity.DATA_TYPE_POS: IntPositionEntityData pos = (IntPositionEntityData) d; stream.putVarInt(pos.x); - stream.putByte((byte) pos.y); + stream.putVarInt(pos.y); stream.putVarInt(pos.z); break; case Entity.DATA_TYPE_LONG: @@ -153,7 +175,7 @@ public static EntityMetadata readMetadata(byte[] payload) { value = new SlotEntityData(key, item.getId(), item.getDamage(), item.getCount()); break; case Entity.DATA_TYPE_POS: - BlockVector3 v3 = stream.getBlockCoords(); + BlockVector3 v3 = stream.getSignedBlockPosition(); value = new IntPositionEntityData(key, v3.x, v3.y, v3.z); break; case Entity.DATA_TYPE_LONG: diff --git a/src/main/java/cn/nukkit/utils/BinaryStream.java b/src/main/java/cn/nukkit/utils/BinaryStream.java index 65dbb8637f..170bfee573 100644 --- a/src/main/java/cn/nukkit/utils/BinaryStream.java +++ b/src/main/java/cn/nukkit/utils/BinaryStream.java @@ -3,10 +3,10 @@ import cn.nukkit.entity.Attribute; import cn.nukkit.entity.data.Skin; import cn.nukkit.item.Item; +import cn.nukkit.math.BlockFace; import cn.nukkit.math.BlockVector3; import cn.nukkit.math.Vector3f; -import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -204,27 +204,22 @@ public void putByte(byte b) { /** * Reads a list of Attributes from the stream. + * * @return Attribute[] */ public Attribute[] getAttributeList() throws Exception { List list = new ArrayList<>(); long count = this.getUnsignedVarInt(); - for(int i = 0; i < count; ++i){ - float min = this.getLFloat(); - float max = this.getLFloat(); - float current = this.getLFloat(); - float defaultValue = this.getLFloat(); + for (int i = 0; i < count; ++i) { String name = this.getString(); - Attribute attr = Attribute.getAttributeByName(name); - if(attr != null){ - attr.setMinValue(min); - attr.setMaxValue(max); - attr.setValue(current); - attr.setDefaultValue(defaultValue); + if (attr != null) { + attr.setMinValue(this.getLFloat()); + attr.setValue(this.getLFloat()); + attr.setMaxValue(this.getLFloat()); list.add(attr); - }else{ + } else { throw new Exception("Unknown attribute type \"" + name + "\""); } } @@ -235,14 +230,13 @@ public Attribute[] getAttributeList() throws Exception { /** * Writes a list of Attributes to the packet buffer using the standard format. */ - public void putAttributeList(Attribute[] attributes){ + public void putAttributeList(Attribute[] attributes) { this.putUnsignedVarInt(attributes.length); - for (Attribute attribute: attributes){ + for (Attribute attribute : attributes) { + this.putString(attribute.getName()); this.putLFloat(attribute.getMinValue()); - this.putLFloat(attribute.getMaxValue()); this.putLFloat(attribute.getValue()); - this.putLFloat(attribute.getDefaultValue()); - this.putString(attribute.getName()); + this.putLFloat(attribute.getMaxValue()); } } @@ -286,16 +280,16 @@ public Item getSlot() { //TODO int canPlaceOn = this.getVarInt(); - if(canPlaceOn > 0){ - for(int i = 0; i < canPlaceOn; ++i){ + if (canPlaceOn > 0) { + for (int i = 0; i < canPlaceOn; ++i) { this.getString(); } } //TODO int canDestroy = this.getVarInt(); - if(canDestroy > 0){ - for(int i = 0; i < canDestroy; ++i){ + if (canDestroy > 0) { + for (int i = 0; i < canDestroy; ++i) { this.getString(); } } @@ -371,11 +365,25 @@ public void putUnsignedVarLong(long v) { VarInt.writeUnsignedVarLong(this, v); } - public BlockVector3 getBlockCoords() { + public BlockVector3 getBlockVector3() { return new BlockVector3(this.getVarInt(), (int) this.getUnsignedVarInt(), this.getVarInt()); } - public void putBlockCoords(int x, int y, int z) { + public BlockVector3 getSignedBlockPosition() { + return new BlockVector3(getVarInt(), getVarInt(), getVarInt()); + } + + public void putSignedBlockPosition(BlockVector3 v) { + putVarInt(v.x); + putVarInt(v.y); + putVarInt(v.z); + } + + public void putBlockVector3(BlockVector3 v) { + this.putBlockVector3(v.x, v.y, v.z); + } + + public void putBlockVector3(int x, int y, int z) { this.putVarInt(x); this.putUnsignedVarInt(y); this.putVarInt(z); @@ -385,6 +393,10 @@ public Vector3f getVector3f() { return new Vector3f(this.getLFloat(4), this.getLFloat(4), this.getLFloat(4)); } + public void putVector3f(Vector3f v) { + this.putVector3f(v.x, v.y, v.z); + } + public void putVector3f(float x, float y, float z) { this.putLFloat(x); this.putLFloat(y); @@ -405,6 +417,44 @@ public void putRuleData(RuleData rule) { this.putBoolean(rule.unknown2); } + /** + * Reads and returns an EntityUniqueID + * + * @return int + */ + public long getEntityUniqueId() { + return this.getVarLong(); + } + + /** + * Writes an EntityUniqueID + */ + public void putEntityUniqueId(long eid) { + this.putVarLong(eid); + } + + /** + * Reads and returns an EntityRuntimeID + */ + public long getEntityRuntimeId() { + return this.getUnsignedVarLong(); + } + + /** + * Writes an EntityUniqueID + */ + public void putEntityRuntimeId(long eid) { + this.putUnsignedVarLong(eid); + } + + public BlockFace getBlockFace() { + return BlockFace.fromIndex(this.getVarInt()); + } + + public void putBlockFace(BlockFace face) { + this.putVarInt(face.getIndex()); + } + public boolean feof() { return this.offset < 0 || this.offset >= this.buffer.length; } diff --git a/src/main/java/cn/nukkit/utils/ClientChainData.java b/src/main/java/cn/nukkit/utils/ClientChainData.java index b444d04100..358bd6fd25 100644 --- a/src/main/java/cn/nukkit/utils/ClientChainData.java +++ b/src/main/java/cn/nukkit/utils/ClientChainData.java @@ -10,182 +10,192 @@ /** * ClientChainData is a container of chain data sent from clients. - * + *

* Device information such as client UUID, xuid and serverAddress, can be * read from instances of this object. - * + *

* To get chain data, you can use player.getLoginChainData() or read(loginPacket) - * + *

* =============== * author: boybook * Nukkit Project * =============== */ -public final class ClientChainData { +public final class ClientChainData implements LoginChainData { - public static ClientChainData of(byte[] buffer) { - return new ClientChainData(buffer); - } - - public static ClientChainData read(LoginPacket pk) { - return of(pk.getBuffer()); - } - - public String getUsername() { - return username; - } - - public UUID getClientUUID() { - return clientUUID; - } - - public String getIdentityPublicKey() { - return identityPublicKey; - } - - public long getClientId() { - return clientId; - } - - public String getServerAddress() { - return serverAddress; - } - - public String getDeviceModel() { - return deviceModel; - } - - public int getDeviceOS() { - return deviceOS; - } - - public String getGameVersion() { - return gameVersion; - } - - public int getGuiScale() { - return guiScale; - } - - public String getLanguageCode() { - return languageCode; - } - - public String getXUID() { - return xuid; - } - - public int getCurrentInputMode() { - return currentInputMode; - } - - public int getDefaultInputMode() { - return defaultInputMode; - } - - public String getADRole() { - return ADRole; - } - - public String getTenantId() { - return tenantId; - } - - public final static int UI_PROFILE_CLASSIC = 0; - public final static int UI_PROFILE_POCKET = 1; - - public int getUIProfile() { - return UIProfile; - } - - /////////////////////////////////////////////////////////////////////////// - // Override - /////////////////////////////////////////////////////////////////////////// - - @Override - public boolean equals(Object obj) { - return obj instanceof ClientChainData && Objects.equals(bs, ((ClientChainData) obj).bs); - } - - @Override - public int hashCode() { - return bs.hashCode(); - } - - /////////////////////////////////////////////////////////////////////////// - // Internal - /////////////////////////////////////////////////////////////////////////// - - private String username; - private UUID clientUUID; - private String xuid; - private String identityPublicKey; - - private long clientId; - private String serverAddress; - private String deviceModel; - private int deviceOS; - private String gameVersion; - private int guiScale; - private String languageCode; - private int currentInputMode; - private int defaultInputMode; - private String ADRole; - private String tenantId; - - private int UIProfile; - - private BinaryStream bs = new BinaryStream(); - - private ClientChainData(byte[] buffer) { - bs.setBuffer(buffer, 0); - decodeChainData(); - decodeSkinData(); - } - - private void decodeChainData() { - Map> map = new Gson().fromJson(new String(bs.get(bs.getLInt()), StandardCharsets.UTF_8), - new TypeToken>>() { - }.getType()); - if (map.isEmpty() || !map.containsKey("chain") || map.get("chain").isEmpty()) return; - List chains = map.get("chain"); - for (String c : chains) { - JsonObject chainMap = decodeToken(c); - if (chainMap == null) continue; - if (chainMap.has("extraData")) { - JsonObject extra = chainMap.get("extraData").getAsJsonObject(); - if (extra.has("displayName")) this.username = extra.get("displayName").getAsString(); - if (extra.has("identity")) this.clientUUID = UUID.fromString(extra.get("identity").getAsString()); - if (extra.has("XUID")) this.xuid = extra.get("XUID").getAsString(); - } - if (chainMap.has("identityPublicKey")) - this.identityPublicKey = chainMap.get("identityPublicKey").getAsString(); - } - } - - private void decodeSkinData() { - JsonObject skinToken = decodeToken(new String(bs.get(bs.getLInt()))); - if(skinToken == null) return; - if (skinToken.has("ClientRandomId")) this.clientId = skinToken.get("ClientRandomId").getAsLong(); - if (skinToken.has("ServerAddress")) this.serverAddress = skinToken.get("ServerAddress").getAsString(); - if (skinToken.has("DeviceModel")) this.deviceModel = skinToken.get("DeviceModel").getAsString(); - if (skinToken.has("DeviceOS")) this.deviceOS = skinToken.get("DeviceOS").getAsInt(); - if (skinToken.has("GameVersion")) this.gameVersion = skinToken.get("GameVersion").getAsString(); - if (skinToken.has("GuiScale")) this.guiScale = skinToken.get("GuiScale").getAsInt(); - if (skinToken.has("LanguageCode")) this.languageCode = skinToken.get("LanguageCode").getAsString(); - if (skinToken.has("CurrentInputMode")) this.currentInputMode = skinToken.get("CurrentInputMode").getAsInt(); - if (skinToken.has("DefaultInputMode")) this.defaultInputMode = skinToken.get("DefaultInputMode").getAsInt(); - if (skinToken.has("ADRole")) this.ADRole = skinToken.get("ADRole").getAsString(); - if (skinToken.has("TenantId")) this.tenantId = skinToken.get("TenantId").getAsString(); - if (skinToken.has("UIProfile")) this.UIProfile = skinToken.get("UIProfile").getAsInt(); - } - - private JsonObject decodeToken(String token) { - String[] base = token.split("\\."); - if (base.length < 2) return null; - String json = new String(Base64.getDecoder().decode(base[1]), StandardCharsets.UTF_8); - //Server.getInstance().getLogger().debug(json); - return new Gson().fromJson(json, JsonObject.class); - } + public static ClientChainData of(byte[] buffer) { + return new ClientChainData(buffer); + } + + public static ClientChainData read(LoginPacket pk) { + return of(pk.getBuffer()); + } + + @Override + public String getUsername() { + return username; + } + + @Override + public UUID getClientUUID() { + return clientUUID; + } + + @Override + public String getIdentityPublicKey() { + return identityPublicKey; + } + + @Override + public long getClientId() { + return clientId; + } + + @Override + public String getServerAddress() { + return serverAddress; + } + + @Override + public String getDeviceModel() { + return deviceModel; + } + + @Override + public int getDeviceOS() { + return deviceOS; + } + + @Override + public String getGameVersion() { + return gameVersion; + } + + @Override + public int getGuiScale() { + return guiScale; + } + + @Override + public String getLanguageCode() { + return languageCode; + } + + @Override + public String getXUID() { + return xuid; + } + + @Override + public int getCurrentInputMode() { + return currentInputMode; + } + + @Override + public int getDefaultInputMode() { + return defaultInputMode; + } + + @Override + public String getCapeData() { + return capeData; + } + + public final static int UI_PROFILE_CLASSIC = 0; + public final static int UI_PROFILE_POCKET = 1; + + @Override + public int getUIProfile() { + return UIProfile; + } + + /////////////////////////////////////////////////////////////////////////// + // Override + /////////////////////////////////////////////////////////////////////////// + + @Override + public boolean equals(Object obj) { + return obj instanceof ClientChainData && Objects.equals(bs, ((ClientChainData) obj).bs); + } + + @Override + public int hashCode() { + return bs.hashCode(); + } + + /////////////////////////////////////////////////////////////////////////// + // Internal + /////////////////////////////////////////////////////////////////////////// + + private String username; + private UUID clientUUID; + private String xuid; + private String identityPublicKey; + + private long clientId; + private String serverAddress; + private String deviceModel; + private int deviceOS; + private String gameVersion; + private int guiScale; + private String languageCode; + private int currentInputMode; + private int defaultInputMode; + + private int UIProfile; + + private String capeData; + + private BinaryStream bs = new BinaryStream(); + + private ClientChainData(byte[] buffer) { + bs.setBuffer(buffer, 0); + decodeChainData(); + decodeSkinData(); + } + + private void decodeChainData() { + Map> map = new Gson().fromJson(new String(bs.get(bs.getLInt()), StandardCharsets.UTF_8), + new TypeToken>>() { + }.getType()); + if (map.isEmpty() || !map.containsKey("chain") || map.get("chain").isEmpty()) return; + List chains = map.get("chain"); + for (String c : chains) { + JsonObject chainMap = decodeToken(c); + if (chainMap == null) continue; + if (chainMap.has("extraData")) { + JsonObject extra = chainMap.get("extraData").getAsJsonObject(); + if (extra.has("displayName")) this.username = extra.get("displayName").getAsString(); + if (extra.has("identity")) this.clientUUID = UUID.fromString(extra.get("identity").getAsString()); + if (extra.has("XUID")) this.xuid = extra.get("XUID").getAsString(); + } + if (chainMap.has("identityPublicKey")) + this.identityPublicKey = chainMap.get("identityPublicKey").getAsString(); + } + } + + private void decodeSkinData() { + JsonObject skinToken = decodeToken(new String(bs.get(bs.getLInt()))); + if (skinToken == null) return; + if (skinToken.has("ClientRandomId")) this.clientId = skinToken.get("ClientRandomId").getAsLong(); + if (skinToken.has("ServerAddress")) this.serverAddress = skinToken.get("ServerAddress").getAsString(); + if (skinToken.has("DeviceModel")) this.deviceModel = skinToken.get("DeviceModel").getAsString(); + if (skinToken.has("DeviceOS")) this.deviceOS = skinToken.get("DeviceOS").getAsInt(); + if (skinToken.has("GameVersion")) this.gameVersion = skinToken.get("GameVersion").getAsString(); + if (skinToken.has("GuiScale")) this.guiScale = skinToken.get("GuiScale").getAsInt(); + if (skinToken.has("LanguageCode")) this.languageCode = skinToken.get("LanguageCode").getAsString(); + if (skinToken.has("CurrentInputMode")) this.currentInputMode = skinToken.get("CurrentInputMode").getAsInt(); + if (skinToken.has("DefaultInputMode")) this.defaultInputMode = skinToken.get("DefaultInputMode").getAsInt(); + if (skinToken.has("UIProfile")) this.UIProfile = skinToken.get("UIProfile").getAsInt(); + if (skinToken.has("CapeData")) this.capeData = skinToken.get("CapeData").getAsString(); + } + + private JsonObject decodeToken(String token) { + String[] base = token.split("\\."); + if (base.length < 2) return null; + String json = new String(Base64.getDecoder().decode(base[1]), StandardCharsets.UTF_8); + //Server.getInstance().getLogger().debug(json); + return new Gson().fromJson(json, JsonObject.class); + } } diff --git a/src/main/java/cn/nukkit/utils/Config.java b/src/main/java/cn/nukkit/utils/Config.java index 6a672fc691..35b117c7c1 100644 --- a/src/main/java/cn/nukkit/utils/Config.java +++ b/src/main/java/cn/nukkit/utils/Config.java @@ -99,6 +99,10 @@ public Config(String file, int type, ConfigSection defaultMap) { this.load(file, type, defaultMap); } + public Config(File file, int type, ConfigSection defaultMap) { + this.load(file.toString(), type, defaultMap); + } + @Deprecated public Config(File file, int type, LinkedHashMap defaultMap) { this(file.toString(), type, new ConfigSection(defaultMap)); @@ -569,4 +573,4 @@ public Set getKeys(boolean child) { if (this.correct) return config.getKeys(child); return new HashSet<>(); } -} \ No newline at end of file +} diff --git a/src/main/java/cn/nukkit/utils/ConfigSection.java b/src/main/java/cn/nukkit/utils/ConfigSection.java index bdab8c57d3..05bb97b2fd 100644 --- a/src/main/java/cn/nukkit/utils/ConfigSection.java +++ b/src/main/java/cn/nukkit/utils/ConfigSection.java @@ -133,17 +133,17 @@ public ConfigSection getSection(String key) { /** * Get all ConfigSections in root path. * Example config: - * a1: - * b1: - * c1: - * c2: - * a2: - * b2: - * c3: - * c4: - * a3: true - * a4: "hello" - * a5: 100 + * a1: + * b1: + * c1: + * c2: + * a2: + * b2: + * c3: + * c4: + * a3: true + * a4: "hello" + * a5: 100 *

* getSections() will return new ConfigSection, that contains sections a1 and a2 only. * diff --git a/src/main/java/cn/nukkit/utils/DummyBossBar.java b/src/main/java/cn/nukkit/utils/DummyBossBar.java new file mode 100644 index 0000000000..0fb66b68ec --- /dev/null +++ b/src/main/java/cn/nukkit/utils/DummyBossBar.java @@ -0,0 +1,243 @@ +package cn.nukkit.utils; + +import cn.nukkit.Player; +import cn.nukkit.entity.Attribute; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.data.EntityMetadata; +import cn.nukkit.entity.mob.EntityCreeper; +import cn.nukkit.network.protocol.*; + +import java.awt.*; +import java.util.concurrent.ThreadLocalRandom; + +/** + * DummyBossBar + * =============== + * author: boybook + * Nukkit Project + * =============== + */ +public class DummyBossBar { + + private final Player player; + private final long bossBarId; + + private String text; + private float length; + private Color color; + + private DummyBossBar(Builder builder) { + this.player = builder.player; + this.bossBarId = builder.bossBarId; + this.text = builder.text; + this.length = builder.length; + this.color = builder.color; + } + + public static class Builder { + private final Player player; + private final long bossBarId; + + private String text = ""; + private float length = 100; + private Color color = null; + + public Builder(Player player) { + this.player = player; + this.bossBarId = 1095216660480L + ThreadLocalRandom.current().nextLong(0, 0x7fffffffL); + } + + public Builder text(String text) { + this.text = text; + return this; + } + + public Builder length(float length) { + if (length >= 0 && length <= 100) this.length = length; + return this; + } + + public Builder color(Color color) { + this.color = color; + return this; + } + + public Builder color(int red, int green, int blue) { + return color(new Color(red, green, blue)); + } + + public DummyBossBar build() { + return new DummyBossBar(this); + } + } + + public Player getPlayer() { + return player; + } + + public long getBossBarId() { + return bossBarId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + if (!this.text.equals(text)) { + this.text = text; + this.updateBossEntityNameTag(); + this.sendSetBossBarTitle(); + } + } + + public float getLength() { + return length; + } + + public void setLength(float length) { + if (this.length != length) { + this.length = length; + this.sendAttributes(); + } + } + + /** + * Color is not working in the current version. We are keep waiting for client support. + * @param color the boss bar color + */ + public void setColor(Color color) { + if (this.color == null || !this.color.equals(color)) { + this.color = color; + this.sendSetBossBarTexture(); + } + } + + public void setColor(int red, int green, int blue) { + this.setColor(new Color(red, green, blue)); + } + + public int getMixedColor() { + return this.color.getRGB();//(this.color.getRed() << 16 | this.color.getGreen() << 8 | this.color.getBlue()) & 0xffffff; + } + + public Color getColor() { + return this.color; + } + + private void createBossEntity() { + AddEntityPacket pkAdd = new AddEntityPacket(); + pkAdd.type = EntityCreeper.NETWORK_ID; + pkAdd.entityUniqueId = bossBarId; + pkAdd.entityRuntimeId = bossBarId; + pkAdd.x = (float) player.x; + pkAdd.y = (float) -10; // Below the bedrock + pkAdd.z = (float) player.z; + pkAdd.speedX = 0; + pkAdd.speedY = 0; + pkAdd.speedZ = 0; + pkAdd.metadata = new EntityMetadata() + // Default Metadata tags + .putLong(Entity.DATA_FLAGS, 0) + .putShort(Entity.DATA_AIR, 400) + .putShort(Entity.DATA_MAX_AIR, 400) + .putLong(Entity.DATA_LEAD_HOLDER_EID, -1) + .putString(Entity.DATA_NAMETAG, text) // Set the entity name + .putFloat(Entity.DATA_SCALE, 0); // And make it invisible + + player.dataPacket(pkAdd); + } + + private void sendAttributes() { + UpdateAttributesPacket pkAttributes = new UpdateAttributesPacket(); + pkAttributes.entityId = bossBarId; + Attribute attr = Attribute.getAttribute(Attribute.MAX_HEALTH); + attr.setMaxValue(100); // Max value - We need to change the max value first, or else the "setValue" will return a IllegalArgumentException + attr.setValue(length); // Entity health + pkAttributes.entries = new Attribute[]{attr}; + player.dataPacket(pkAttributes); + } + + private void sendShowBossBar() { + BossEventPacket pkBoss = new BossEventPacket(); + pkBoss.bossEid = bossBarId; + pkBoss.type = BossEventPacket.TYPE_SHOW; + pkBoss.title = text; + pkBoss.healthPercent = this.length; + player.dataPacket(pkBoss); + } + + private void sendHideBossBar() { + BossEventPacket pkBoss = new BossEventPacket(); + pkBoss.bossEid = bossBarId; + pkBoss.type = BossEventPacket.TYPE_HIDE; + player.dataPacket(pkBoss); + } + + private void sendSetBossBarTexture() { + BossEventPacket pk = new BossEventPacket(); + pk.bossEid = this.bossBarId; + pk.type = BossEventPacket.TYPE_TEXTURE; + pk.color = this.getMixedColor(); + player.dataPacket(pk); + } + + private void sendSetBossBarTitle() { + BossEventPacket pkBoss = new BossEventPacket(); + pkBoss.bossEid = bossBarId; + pkBoss.type = BossEventPacket.TYPE_TITLE; + pkBoss.title = text; + pkBoss.healthPercent = this.length; + player.dataPacket(pkBoss); + } + + /** + * Don't let the entity go too far from the player, or the BossBar will disappear. + * Update boss entity's position when teleport and each 5s. + */ + public void updateBossEntityPosition() { + MoveEntityPacket pk = new MoveEntityPacket(); + pk.eid = this.bossBarId; + pk.x = this.player.x; + pk.y = -10; + pk.z = this.player.z; + pk.headYaw = 0; + pk.yaw = 0; + pk.pitch = 0; + player.dataPacket(pk); + } + + private void updateBossEntityNameTag() { + SetEntityDataPacket pk = new SetEntityDataPacket(); + pk.eid = this.bossBarId; + pk.metadata = new EntityMetadata().putString(Entity.DATA_NAMETAG, this.text); + player.dataPacket(pk); + } + + private void removeBossEntity() { + RemoveEntityPacket pkRemove = new RemoveEntityPacket(); + pkRemove.eid = bossBarId; + player.dataPacket(pkRemove); + } + + public void create() { + createBossEntity(); + sendAttributes(); + sendShowBossBar(); + if (color != null) this.sendSetBossBarTexture(); + } + + /** + * Once the player has teleported, resend Show BossBar + */ + public void reshow() { + updateBossEntityPosition(); + sendShowBossBar(); + } + + public void destroy() { + sendHideBossBar(); + removeBossEntity(); + } + +} diff --git a/src/main/java/cn/nukkit/utils/LogLevel.java b/src/main/java/cn/nukkit/utils/LogLevel.java index 9a674f6431..c883031763 100644 --- a/src/main/java/cn/nukkit/utils/LogLevel.java +++ b/src/main/java/cn/nukkit/utils/LogLevel.java @@ -4,7 +4,8 @@ * author: MagicDroidX * Nukkit Project */ -public enum LogLevel { +public enum LogLevel implements Comparable { + NONE, EMERGENCY, ALERT, CRITICAL, @@ -12,5 +13,10 @@ public enum LogLevel { WARNING, NOTICE, INFO, - DEBUG + DEBUG; + public static final LogLevel DEFAULT_LEVEL = INFO; + + int getLevel() { + return ordinal(); + } } diff --git a/src/main/java/cn/nukkit/utils/LoginChainData.java b/src/main/java/cn/nukkit/utils/LoginChainData.java new file mode 100644 index 0000000000..f29fa36db3 --- /dev/null +++ b/src/main/java/cn/nukkit/utils/LoginChainData.java @@ -0,0 +1,39 @@ +package cn.nukkit.utils; + +import java.util.UUID; + +/** + * @author CreeperFace + */ +public interface LoginChainData { + + String getUsername(); + + UUID getClientUUID(); + + String getIdentityPublicKey(); + + long getClientId(); + + String getServerAddress(); + + String getDeviceModel(); + + int getDeviceOS(); + + String getGameVersion(); + + int getGuiScale(); + + String getLanguageCode(); + + String getXUID(); + + int getCurrentInputMode(); + + int getDefaultInputMode(); + + String getCapeData(); + + int getUIProfile(); +} diff --git a/src/main/java/cn/nukkit/utils/MainLogger.java b/src/main/java/cn/nukkit/utils/MainLogger.java index 147b4962e2..25b2bc7836 100644 --- a/src/main/java/cn/nukkit/utils/MainLogger.java +++ b/src/main/java/cn/nukkit/utils/MainLogger.java @@ -22,75 +22,84 @@ public class MainLogger extends ThreadedLogger { protected final String logPath; protected final ConcurrentLinkedQueue logBuffer = new ConcurrentLinkedQueue<>(); protected boolean shutdown; - protected boolean logDebug = false; + protected LogLevel logLevel = LogLevel.DEFAULT_LEVEL; private final Map replacements = new EnumMap<>(TextFormat.class); private final TextFormat[] colors = TextFormat.values(); protected static MainLogger logger; public MainLogger(String logFile) { - this(logFile, false); + this(logFile, LogLevel.DEFAULT_LEVEL); } - public MainLogger(String logFile, boolean logDebug) { + public MainLogger(String logFile, LogLevel logLevel) { + if (logger != null) { throw new RuntimeException("MainLogger has been already created"); } logger = this; this.logPath = logFile; - this.logDebug = logDebug; this.start(); } + public MainLogger(String logFile, boolean logDebug) { + this(logFile, logDebug ? LogLevel.DEBUG : LogLevel.INFO); + } + public static MainLogger getLogger() { return logger; } @Override public void emergency(String message) { - this.send(TextFormat.RED + "[EMERGENCY] " + message); + if (LogLevel.EMERGENCY.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.RED + "[EMERGENCY] " + message); } @Override public void alert(String message) { - this.send(TextFormat.RED + "[ALERT] " + message); + if (LogLevel.ALERT.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.RED + "[ALERT] " + message); } @Override public void critical(String message) { - this.send(TextFormat.RED + "[CRITICAL] " + message); + if (LogLevel.CRITICAL.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.RED + "[CRITICAL] " + message); } @Override public void error(String message) { - this.send(TextFormat.DARK_RED + "[ERROR] " + message); + if (LogLevel.ERROR.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.DARK_RED + "[ERROR] " + message); } @Override public void warning(String message) { - this.send(TextFormat.YELLOW + "[WARNING] " + message); + if (LogLevel.WARNING.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.YELLOW + "[WARNING] " + message); } @Override public void notice(String message) { - this.send(TextFormat.AQUA + "[NOTICE] " + message); + if (LogLevel.NOTICE.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.AQUA + "[NOTICE] " + message); } @Override public void info(String message) { - this.send(TextFormat.WHITE + "[INFO] " + message); + if (LogLevel.INFO.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.WHITE + "[INFO] " + message); } @Override public void debug(String message) { - if (!this.logDebug) { - return; - } - this.send(TextFormat.GRAY + "[DEBUG] " + message); + if (LogLevel.DEBUG.getLevel() <= logLevel.getLevel()) + this.send(TextFormat.GRAY + "[DEBUG] " + message); } public void setLogDebug(Boolean logDebug) { - this.logDebug = logDebug; + this.logLevel = logDebug ? LogLevel.DEBUG : LogLevel.INFO; } public void logException(Exception e) { @@ -294,4 +303,4 @@ public void log(LogLevel level, String message, Throwable t) { this.log(level, message + "\r\n" + Utils.getExceptionMessage(t)); } -} \ No newline at end of file +} diff --git a/src/main/java/cn/nukkit/utils/MinecartType.java b/src/main/java/cn/nukkit/utils/MinecartType.java new file mode 100644 index 0000000000..1bf3fb21de --- /dev/null +++ b/src/main/java/cn/nukkit/utils/MinecartType.java @@ -0,0 +1,106 @@ +package cn.nukkit.utils; + +import cn.nukkit.api.API; + +import java.util.HashMap; +import java.util.Map; + +/** + * Helper class of Minecart variants + *

+ * By Adam Matthew + * Creation time: 2017/7/17 19:55. + */ +@API(usage = API.Usage.STABLE, definition = API.Definition.INTERNAL) +public enum MinecartType { + /** + * Represents an empty vehicle. + */ + MINECART_EMPTY(0, false, "Minecart"), + /** + * Represents a chest holder. + */ + MINECART_CHEST(1, true, "Minecart with Chest"), + /** + * Represents a furnace minecart. + */ + MINECART_FURNACE(2, true, "Minecart with Furnace"), + /** + * Represents a TNT minecart. + */ + MINECART_TNT(3, true, "Minecart with TNT"), + /** + * Represents a mob spawner minecart. + */ + MINECART_MOB_SPAWNER(4, true, "Minecart with Mob Spawner"), + /** + * Represents a hopper minecart. + */ + MINECART_HOPPER(5, true, "Minecart with Hopper"), + /** + * Represents a command block minecart. + */ + MINECART_COMMAND_BLOCK(6, true, "Minecart with Command Block"), + /** + * Represents an unknown minecart. + */ + MINECART_UNKNOWN(-1, false, "Unknown Minecart"); + + private final int type; + private final boolean hasBlockInside; + private final String realName; + private static final Map TYPES = new HashMap<>(); + + static { + MinecartType[] types = values(); + int var1 = types.length; + for (int var2 = 0; var2 < var1; var2++) { + MinecartType var3 = types[var2]; + TYPES.put(var3.getId(), var3); + } + } + + MinecartType(int number, boolean hasBlockInside, String name) { + type = number; + this.hasBlockInside = hasBlockInside; + realName = name; + } + + /** + * Get the variants of the current minecart + * + * @return Integer + */ + public int getId() { + return type; + } + + /** + * Get the name of the minecart variants + * + * @return String + */ + public String getName() { + return realName; + } + + /** + * Gets if the minecart contains block + * + * @return Boolean + */ + public boolean hasBlockInside() { + return hasBlockInside; + } + + /** + * Returns of an instance of Minecart-variants + * + * @param types The number of minecart + * @return Integer + */ + public static MinecartType valueOf(int types) { + MinecartType what = TYPES.get(types); + return what == null ? MINECART_UNKNOWN : what; + } +} diff --git a/src/main/java/cn/nukkit/utils/Rail.java b/src/main/java/cn/nukkit/utils/Rail.java new file mode 100644 index 0000000000..7cb9d958e2 --- /dev/null +++ b/src/main/java/cn/nukkit/utils/Rail.java @@ -0,0 +1,161 @@ +package cn.nukkit.utils; + +import cn.nukkit.api.API; +import cn.nukkit.block.Block; +import cn.nukkit.math.BlockFace; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import static cn.nukkit.math.BlockFace.*; +import static cn.nukkit.utils.Rail.Orientation.State.*; + +/** + * INTERNAL helper class of railway + *

+ * By lmlstarqaq http://snake1999.com/ + * Creation time: 2017/7/1 17:42. + */ +@API(usage = API.Usage.BLEEDING, definition = API.Definition.INTERNAL) +public final class Rail { + + public static boolean isRailBlock(Block block) { + Objects.requireNonNull(block, "Rail block predicate can not accept null block"); + return isRailBlock(block.getId()); + } + + public enum Orientation { + STRAIGHT_NORTH_SOUTH(0, STRAIGHT, NORTH, SOUTH, null), + STRAIGHT_EAST_WEST(1, STRAIGHT, EAST, WEST, null), + ASCENDING_EAST(2, ASCENDING, EAST, WEST, EAST), + ASCENDING_WEST(3, ASCENDING, EAST, WEST, WEST), + ASCENDING_NORTH(4, ASCENDING, NORTH, SOUTH, NORTH), + ASCENDING_SOUTH(5, ASCENDING, NORTH, SOUTH, SOUTH), + CURVED_SOUTH_EAST(6, CURVED, SOUTH, EAST, null), + CURVED_SOUTH_WEST(7, CURVED, SOUTH, WEST, null), + CURVED_NORTH_WEST(8, CURVED, NORTH, WEST, null), + CURVED_NORTH_EAST(9, CURVED, NORTH, EAST, null); + + private static final Orientation[] META_LOOKUP = new Orientation[values().length]; + private final int meta; + private final State state; + private final List connectingDirections; + private final BlockFace ascendingDirection; + + Orientation(int meta, State state, BlockFace from, BlockFace to, BlockFace ascendingDirection) { + this.meta = meta; + this.state = state; + this.connectingDirections = Arrays.asList(from, to); + this.ascendingDirection = ascendingDirection; + } + + public static Orientation byMetadata(int meta) { + if (meta < 0 || meta >= META_LOOKUP.length) { + meta = 0; + } + + return META_LOOKUP[meta]; + } + + public static Orientation straight(BlockFace face) { + switch (face) { + case NORTH: + case SOUTH: + return STRAIGHT_NORTH_SOUTH; + case EAST: + case WEST: + return STRAIGHT_EAST_WEST; + } + return STRAIGHT_NORTH_SOUTH; + } + + public static Orientation ascending(BlockFace face) { + switch (face) { + case NORTH: + return ASCENDING_NORTH; + case SOUTH: + return ASCENDING_SOUTH; + case EAST: + return ASCENDING_EAST; + case WEST: + return ASCENDING_WEST; + } + return ASCENDING_EAST; + } + + public static Orientation curved(BlockFace f1, BlockFace f2) { + for (Orientation o : new Orientation[]{CURVED_SOUTH_EAST, CURVED_SOUTH_WEST, CURVED_NORTH_WEST, CURVED_NORTH_EAST}) { + if (o.connectingDirections.contains(f1) && o.connectingDirections.contains(f2)) { + return o; + } + } + return CURVED_SOUTH_EAST; + } + + public static Orientation straightOrCurved(BlockFace f1, BlockFace f2) { + for (Orientation o : new Orientation[]{STRAIGHT_NORTH_SOUTH, STRAIGHT_EAST_WEST, CURVED_SOUTH_EAST, CURVED_SOUTH_WEST, CURVED_NORTH_WEST, CURVED_NORTH_EAST}) { + if (o.connectingDirections.contains(f1) && o.connectingDirections.contains(f2)) { + return o; + } + } + return STRAIGHT_NORTH_SOUTH; + } + + public int metadata() { + return meta; + } + + public boolean hasConnectingDirections(BlockFace... faces) { + return Stream.of(faces).allMatch(connectingDirections::contains); + } + + public List connectingDirections() { + return connectingDirections; + } + + public Optional ascendingDirection() { + return Optional.ofNullable(ascendingDirection); + } + + public enum State { + STRAIGHT, ASCENDING, CURVED + } + + public boolean isStraight() { + return state == STRAIGHT; + } + + public boolean isAscending() { + return state == ASCENDING; + } + + public boolean isCurved() { + return state == CURVED; + } + + static { + for (Orientation o : values()) { + META_LOOKUP[o.meta] = o; + } + } + } + + public static boolean isRailBlock(int blockId) { + switch (blockId) { + case Block.RAIL: + case Block.POWERED_RAIL: + case Block.ACTIVATOR_RAIL: + case Block.DETECTOR_RAIL: + return true; + default: + return false; + } + } + + private Rail() { + //no instance + } +} diff --git a/src/main/java/cn/nukkit/utils/Utils.java b/src/main/java/cn/nukkit/utils/Utils.java index 911357dc30..69a2e8f457 100644 --- a/src/main/java/cn/nukkit/utils/Utils.java +++ b/src/main/java/cn/nukkit/utils/Utils.java @@ -5,7 +5,6 @@ import java.lang.management.ThreadInfo; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; import java.util.UUID; @@ -170,22 +169,25 @@ public static long toRGB(byte r, byte g, byte b, byte a) { return result & 0xFFFFFFFFL; } - public static java.util.List toChunk(java.util.List list, int size) { - java.util.List result = new ArrayList<>(); - T[] arr = (T[]) list.stream().toArray(); + public static Object[][] splitArray(Object[] arrayToSplit, int chunkSize) { + if (chunkSize <= 0) { + return null; + } - int from = 0; - int to = size >= arr.length ? arr.length : size; + int rest = arrayToSplit.length % chunkSize; + int chunks = arrayToSplit.length / chunkSize + (rest > 0 ? 1 : 0); - while (from < arr.length) { - T[] subArray = Arrays.copyOfRange(arr, from, to); - from = to; - to += size; - if (to > arr.length) { - to = arr.length; - } - result.add(subArray); + Object[][] arrays = new Object[chunks][]; + for (int i = 0; i < (rest > 0 ? chunks - 1 : chunks); i++) { + arrays[i] = Arrays.copyOfRange(arrayToSplit, i * chunkSize, i * chunkSize + chunkSize); + } + if (rest > 0) { + arrays[chunks - 1] = Arrays.copyOfRange(arrayToSplit, (chunks - 1) * chunkSize, (chunks - 1) * chunkSize + rest); } - return result; + return arrays; + } + + public static int toInt(Object number) { + return (int) Math.round((double) number); } } diff --git a/src/main/java/cn/nukkit/utils/VarInt.java b/src/main/java/cn/nukkit/utils/VarInt.java index 373b3b07ea..e80f6fbbe4 100644 --- a/src/main/java/cn/nukkit/utils/VarInt.java +++ b/src/main/java/cn/nukkit/utils/VarInt.java @@ -20,9 +20,9 @@ @API(usage = EXPERIMENTAL, definition = UNIVERSAL) public final class VarInt { - private VarInt() { - //no instance - } + private VarInt() { + //no instance + } /** * @param v Signed int @@ -46,7 +46,7 @@ public static int decodeZigZag32(long v) { * @return Unsigned encoded long */ public static long encodeZigZag64(long v) { - return (v << 1) ^ (v >> 63); + return (v << 1) ^ (v >> 63); } /** @@ -58,31 +58,31 @@ public static long decodeZigZag64(long v) { } private static long read(BinaryStream stream, int maxSize) { - long value = 0; - int size = 0; - int b; - while (((b = stream.getByte()) & 0x80) == 0x80) { - value |= (long) (b & 0x7F) << (size++ * 7); - if (size >= maxSize) { - throw new IllegalArgumentException("VarLong too big"); - } - } + long value = 0; + int size = 0; + int b; + while (((b = stream.getByte()) & 0x80) == 0x80) { + value |= (long) (b & 0x7F) << (size++ * 7); + if (size >= maxSize) { + throw new IllegalArgumentException("VarLong too big"); + } + } - return value | ((long) (b & 0x7F) << (size * 7)); + return value | ((long) (b & 0x7F) << (size * 7)); } private static long read(InputStream stream, int maxSize) throws IOException { - long value = 0; - int size = 0; - int b; - while (((b = stream.read()) & 0x80) == 0x80) { - value |= (long) (b & 0x7F) << (size++ * 7); - if (size >= maxSize) { - throw new IllegalArgumentException("VarLong too big"); - } - } + long value = 0; + int size = 0; + int b; + while (((b = stream.read()) & 0x80) == 0x80) { + value |= (long) (b & 0x7F) << (size++ * 7); + if (size >= maxSize) { + throw new IllegalArgumentException("VarLong too big"); + } + } - return value | ((long) (b & 0x7F) << (size * 7)); + return value | ((long) (b & 0x7F) << (size * 7)); } /** @@ -150,27 +150,27 @@ public static long readUnsignedVarLong(InputStream stream) throws IOException { } private static void write(BinaryStream stream, long value) { - do { - byte temp = (byte)(value & 0b01111111); - // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone - value >>>= 7; - if (value != 0) { - temp |= 0b10000000; - } - stream.putByte(temp); - } while (value != 0); + do { + byte temp = (byte) (value & 0b01111111); + // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone + value >>>= 7; + if (value != 0) { + temp |= 0b10000000; + } + stream.putByte(temp); + } while (value != 0); } private static void write(OutputStream stream, long value) throws IOException { - do { - byte temp = (byte)(value & 0b01111111); - // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone - value >>>= 7; - if (value != 0) { - temp |= 0b10000000; - } - stream.write(temp); - } while (value != 0); + do { + byte temp = (byte) (value & 0b01111111); + // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone + value >>>= 7; + if (value != 0) { + temp |= 0b10000000; + } + stream.write(temp); + } while (value != 0); } /** diff --git a/src/main/java/cn/nukkit/utils/Zlib.java b/src/main/java/cn/nukkit/utils/Zlib.java index 7ec14f26fb..dc4347c73f 100644 --- a/src/main/java/cn/nukkit/utils/Zlib.java +++ b/src/main/java/cn/nukkit/utils/Zlib.java @@ -9,56 +9,60 @@ public abstract class Zlib { - + public static byte[] deflate(byte[] data) throws Exception { return deflate(data, Deflater.DEFAULT_COMPRESSION); } public static byte[] deflate(byte[] data, int level) throws Exception { - Deflater deflater = new Deflater(level); + Deflater deflater = getDef(level); + if (deflater == null) throw new IllegalArgumentException("No deflate for level " + level + " !"); deflater.reset(); deflater.setInput(data); deflater.finish(); ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length); - byte[] buf = new byte[1024]; - try { - while (!deflater.finished()) { - int i = deflater.deflate(buf); - bos.write(buf, 0, i); - } - } finally { - deflater.end(); + while (!deflater.finished()) { + int i = deflater.deflate(buf.get()); + bos.write(buf.get(), 0, i); } + //Deflater::end is called the time when the process exits. return bos.toByteArray(); } - public static byte[] inflate(InputStream stream) throws IOException { + public static byte[] inflate(byte[] data) throws IOException { + return inflate(new ByteArrayInputStream(data)); + } + + public static byte[] inflate(byte[] data, int maxSize) throws IOException { + return inflate(new ByteArrayInputStream(data, 0, maxSize)); + } + + /* -=-=-=-=-=- Internal -=-=-=-=-=- Do NOT attempt to use in production -=-=-=-=-=- */ + + private static final ThreadLocal buf = ThreadLocal.withInitial(() -> new byte[1024]); + private static final ThreadLocal def = ThreadLocal.withInitial(Deflater::new); + + private static Deflater getDef(int level) { + def.get().setLevel(level); + return def.get(); + } + + private static byte[] inflate(InputStream stream) throws IOException { InflaterInputStream inputStream = new InflaterInputStream(stream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; int length; try { - while ((length = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, length); + while ((length = inputStream.read(buf.get())) != -1) { + outputStream.write(buf.get(), 0, length); } } finally { - buffer = outputStream.toByteArray(); + buf.set(outputStream.toByteArray()); outputStream.flush(); outputStream.close(); inputStream.close(); } - return buffer; - } - - public static byte[] inflate(byte[] data) throws IOException { - return inflate(new ByteArrayInputStream(data)); + return buf.get(); } - - public static byte[] inflate(byte[] data, int maxSize) throws IOException { - return inflate(new ByteArrayInputStream(data, 0, maxSize)); - } - -} - +} \ No newline at end of file diff --git a/src/main/java/cn/nukkit/utils/bugreport/BugReportGenerator.java b/src/main/java/cn/nukkit/utils/bugreport/BugReportGenerator.java index bd2e64281d..392a410d34 100644 --- a/src/main/java/cn/nukkit/utils/bugreport/BugReportGenerator.java +++ b/src/main/java/cn/nukkit/utils/bugreport/BugReportGenerator.java @@ -13,6 +13,7 @@ import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Properties; /** * Project nukkit @@ -61,10 +62,17 @@ private String generate() throws IOException { StringWriter stringWriter = new StringWriter(); throwable.printStackTrace(new PrintWriter(stringWriter)); + File mdReport = new File(reports, date + "_" + throwable.getClass().getSimpleName() + ".md"); mdReport.createNewFile(); String content = Utils.readFile(this.getClass().getClassLoader().getResourceAsStream("report_template.md")); + + Properties properties = getGitRepositoryState(); + System.out.println(properties.getProperty("git.commit.id.abbrev")); + String abbrev = properties.getProperty("git.commit.id.abbrev"); + content = content.replace("${NUKKIT_VERSION}", Nukkit.VERSION); + content = content.replace("${GIT_COMMIT_ABBREV}", abbrev); content = content.replace("${JAVA_VERSION}", System.getProperty("java.vm.name") + " (" + System.getProperty("java.runtime.version") + ")"); content = content.replace("${HOSTOS}", systemInfo.getOperatingSystem().getFamily() + " [" + systemInfo.getOperatingSystem().getVersion().getVersion() + "]"); content = content.replace("${MEMORY}", getCount(systemInfo.getHardware().getMemory().getTotal(), true)); @@ -75,11 +83,18 @@ private String generate() throws IOException { content = content.replace("${STACKTRACE}", stringWriter.toString()); content = content.replace("${PLUGIN_ERROR}", String.valueOf(!throwable.getStackTrace()[0].getClassName().startsWith("cn.nukkit")).toUpperCase()); content = content.replace("${STORAGE_TYPE}", model.toString()); + Utils.writeFile(mdReport, content); return mdReport.getAbsolutePath(); } + public Properties getGitRepositoryState() throws IOException { + Properties properties = new Properties(); + properties.load(getClass().getClassLoader().getResourceAsStream("git.properties")); + return properties; + } + //Code section from SOF public static String getCount(long bytes, boolean si) { int unit = si ? 1000 : 1024; diff --git a/src/main/java/cn/nukkit/utils/bugreport/ExceptionHandler.java b/src/main/java/cn/nukkit/utils/bugreport/ExceptionHandler.java index d0b3b4519d..1493dd189a 100644 --- a/src/main/java/cn/nukkit/utils/bugreport/ExceptionHandler.java +++ b/src/main/java/cn/nukkit/utils/bugreport/ExceptionHandler.java @@ -1,8 +1,5 @@ package cn.nukkit.utils.bugreport; -import cn.nukkit.Server; -import cn.nukkit.lang.BaseLang; - /** * Project nukkit */ diff --git a/src/main/java/cn/nukkit/utils/completers/CommandsCompleter.java b/src/main/java/cn/nukkit/utils/completers/CommandsCompleter.java index 41e77e5ac1..30248759e4 100644 --- a/src/main/java/cn/nukkit/utils/completers/CommandsCompleter.java +++ b/src/main/java/cn/nukkit/utils/completers/CommandsCompleter.java @@ -1,13 +1,13 @@ package cn.nukkit.utils.completers; -import static jline.internal.Preconditions.checkNotNull; +import cn.nukkit.Server; +import jline.console.completer.Completer; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; -import cn.nukkit.Server; -import jline.console.completer.Completer; +import static jline.internal.Preconditions.checkNotNull; public class CommandsCompleter implements Completer { @@ -18,15 +18,14 @@ public int complete(String buffer, int cursor, List candidates) { if (buffer == null) { Server.getInstance().getCommandMap().getCommands().keySet().forEach((cmd) -> candidates.add(cmd)); - } - else { + } else { SortedSet names = new TreeSet(); Server.getInstance().getCommandMap().getCommands().keySet().forEach((cmd) -> names.add(cmd)); for (String match : names) { if (!match.toLowerCase().startsWith(buffer.toLowerCase())) { continue; } - + candidates.add(match); } } diff --git a/src/main/java/cn/nukkit/utils/completers/PlayersCompleter.java b/src/main/java/cn/nukkit/utils/completers/PlayersCompleter.java index 10930903b1..0c057dd91d 100644 --- a/src/main/java/cn/nukkit/utils/completers/PlayersCompleter.java +++ b/src/main/java/cn/nukkit/utils/completers/PlayersCompleter.java @@ -1,13 +1,13 @@ package cn.nukkit.utils.completers; -import static jline.internal.Preconditions.checkNotNull; +import cn.nukkit.Server; +import jline.console.completer.Completer; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; -import cn.nukkit.Server; -import jline.console.completer.Completer; +import static jline.internal.Preconditions.checkNotNull; public class PlayersCompleter implements Completer { @@ -18,8 +18,7 @@ public int complete(String buffer, int cursor, List candidates) { if (buffer == null) { Server.getInstance().getOnlinePlayers().values().forEach((p) -> candidates.add(p.getName())); - } - else { + } else { // We are auto completing player names, so 99% of the times we are doing this, it will be something like // "say John*TAB*" // however the buffer will be "say John", but we don't want that, the buffer must be the player's name @@ -33,7 +32,7 @@ public int complete(String buffer, int cursor, List candidates) { if (!match.toLowerCase().startsWith(buffer.toLowerCase())) { continue; } - + candidates.add(cmd + match); } } diff --git a/src/main/resources/creativeitems.json b/src/main/resources/creativeitems.json new file mode 100644 index 0000000000..9390ddb4f4 --- /dev/null +++ b/src/main/resources/creativeitems.json @@ -0,0 +1,3352 @@ +{ + "items": [ + { + "id": 5 + }, + { + "id": 5, + "damage": 1 + }, + { + "id": 5, + "damage": 2 + }, + { + "id": 5, + "damage": 3 + }, + { + "id": 5, + "damage": 4 + }, + { + "id": 5, + "damage": 5 + }, + { + "id": 139 + }, + { + "id": 139, + "damage": 1 + }, + { + "id": 85 + }, + { + "id": 85, + "damage": 1 + }, + { + "id": 85, + "damage": 2 + }, + { + "id": 85, + "damage": 3 + }, + { + "id": 85, + "damage": 4 + }, + { + "id": 85, + "damage": 5 + }, + { + "id": 113 + }, + { + "id": 107 + }, + { + "id": 183 + }, + { + "id": 184 + }, + { + "id": 185 + }, + { + "id": 187 + }, + { + "id": 186 + }, + { + "id": 67 + }, + { + "id": 53 + }, + { + "id": 134 + }, + { + "id": 135 + }, + { + "id": 136 + }, + { + "id": 163 + }, + { + "id": 164 + }, + { + "id": 108 + }, + { + "id": 109 + }, + { + "id": 114 + }, + { + "id": 128 + }, + { + "id": 180 + }, + { + "id": 156 + }, + { + "id": 203 + }, + { + "id": 324 + }, + { + "id": 427 + }, + { + "id": 428 + }, + { + "id": 429 + }, + { + "id": 430 + }, + { + "id": 431 + }, + { + "id": 330 + }, + { + "id": 96 + }, + { + "id": 167 + }, + { + "id": 101 + }, + { + "id": 20 + }, + { + "id": 241 + }, + { + "id": 241, + "damage": 8 + }, + { + "id": 241, + "damage": 7 + }, + { + "id": 241, + "damage": 15 + }, + { + "id": 241, + "damage": 12 + }, + { + "id": 241, + "damage": 14 + }, + { + "id": 241, + "damage": 1 + }, + { + "id": 241, + "damage": 4 + }, + { + "id": 241, + "damage": 5 + }, + { + "id": 241, + "damage": 13 + }, + { + "id": 241, + "damage": 9 + }, + { + "id": 241, + "damage": 3 + }, + { + "id": 241, + "damage": 11 + }, + { + "id": 241, + "damage": 10 + }, + { + "id": 241, + "damage": 2 + }, + { + "id": 241, + "damage": 6 + }, + { + "id": 102 + }, + { + "id": 160 + }, + { + "id": 160, + "damage": 8 + }, + { + "id": 160, + "damage": 7 + }, + { + "id": 160, + "damage": 15 + }, + { + "id": 160, + "damage": 12 + }, + { + "id": 160, + "damage": 14 + }, + { + "id": 160, + "damage": 1 + }, + { + "id": 160, + "damage": 4 + }, + { + "id": 160, + "damage": 5 + }, + { + "id": 160, + "damage": 13 + }, + { + "id": 160, + "damage": 9 + }, + { + "id": 160, + "damage": 3 + }, + { + "id": 160, + "damage": 11 + }, + { + "id": 160, + "damage": 10 + }, + { + "id": 160, + "damage": 2 + }, + { + "id": 160, + "damage": 6 + }, + { + "id": 65 + }, + { + "id": 44 + }, + { + "id": 44, + "damage": 3 + }, + { + "id": 158 + }, + { + "id": 158, + "damage": 1 + }, + { + "id": 158, + "damage": 2 + }, + { + "id": 158, + "damage": 3 + }, + { + "id": 158, + "damage": 4 + }, + { + "id": 158, + "damage": 5 + }, + { + "id": 44, + "damage": 4 + }, + { + "id": 44, + "damage": 5 + }, + { + "id": 44, + "damage": 7 + }, + { + "id": 44, + "damage": 1 + }, + { + "id": 182 + }, + { + "id": 44, + "damage": 6 + }, + { + "id": 182, + "damage": 1 + }, + { + "id": 45 + }, + { + "id": 98 + }, + { + "id": 98, + "damage": 1 + }, + { + "id": 98, + "damage": 2 + }, + { + "id": 98, + "damage": 3 + }, + { + "id": 206 + }, + { + "id": 168, + "damage": 2 + }, + { + "id": 112 + }, + { + "id": 215 + }, + { + "id": 4 + }, + { + "id": 48 + }, + { + "id": 406 + }, + { + "id": 24 + }, + { + "id": 24, + "damage": 1 + }, + { + "id": 24, + "damage": 2 + }, + { + "id": 179 + }, + { + "id": 179, + "damage": 1 + }, + { + "id": 179, + "damage": 2 + }, + { + "id": 173 + }, + { + "id": 152 + }, + { + "id": 41 + }, + { + "id": 42 + }, + { + "id": 133 + }, + { + "id": 57 + }, + { + "id": 22 + }, + { + "id": 155 + }, + { + "id": 155, + "damage": 2 + }, + { + "id": 155, + "damage": 1 + }, + { + "id": 168 + }, + { + "id": 168, + "damage": 1 + }, + { + "id": 165 + }, + { + "id": 170 + }, + { + "id": 216 + }, + { + "id": 214 + }, + { + "id": 35 + }, + { + "id": 35, + "damage": 8 + }, + { + "id": 35, + "damage": 7 + }, + { + "id": 35, + "damage": 15 + }, + { + "id": 35, + "damage": 12 + }, + { + "id": 35, + "damage": 14 + }, + { + "id": 35, + "damage": 1 + }, + { + "id": 35, + "damage": 4 + }, + { + "id": 35, + "damage": 5 + }, + { + "id": 35, + "damage": 13 + }, + { + "id": 35, + "damage": 9 + }, + { + "id": 35, + "damage": 3 + }, + { + "id": 35, + "damage": 11 + }, + { + "id": 35, + "damage": 10 + }, + { + "id": 35, + "damage": 2 + }, + { + "id": 35, + "damage": 6 + }, + { + "id": 171 + }, + { + "id": 171, + "damage": 8 + }, + { + "id": 171, + "damage": 7 + }, + { + "id": 171, + "damage": 15 + }, + { + "id": 171, + "damage": 12 + }, + { + "id": 171, + "damage": 14 + }, + { + "id": 171, + "damage": 1 + }, + { + "id": 171, + "damage": 4 + }, + { + "id": 171, + "damage": 5 + }, + { + "id": 171, + "damage": 13 + }, + { + "id": 171, + "damage": 9 + }, + { + "id": 171, + "damage": 3 + }, + { + "id": 171, + "damage": 11 + }, + { + "id": 171, + "damage": 10 + }, + { + "id": 171, + "damage": 2 + }, + { + "id": 171, + "damage": 6 + }, + { + "id": 237 + }, + { + "id": 237, + "damage": 8 + }, + { + "id": 237, + "damage": 7 + }, + { + "id": 237, + "damage": 15 + }, + { + "id": 237, + "damage": 12 + }, + { + "id": 237, + "damage": 14 + }, + { + "id": 237, + "damage": 1 + }, + { + "id": 237, + "damage": 4 + }, + { + "id": 237, + "damage": 5 + }, + { + "id": 237, + "damage": 13 + }, + { + "id": 237, + "damage": 9 + }, + { + "id": 237, + "damage": 3 + }, + { + "id": 237, + "damage": 11 + }, + { + "id": 237, + "damage": 10 + }, + { + "id": 237, + "damage": 2 + }, + { + "id": 237, + "damage": 6 + }, + { + "id": 236 + }, + { + "id": 236, + "damage": 8 + }, + { + "id": 236, + "damage": 7 + }, + { + "id": 236, + "damage": 15 + }, + { + "id": 236, + "damage": 12 + }, + { + "id": 236, + "damage": 14 + }, + { + "id": 236, + "damage": 1 + }, + { + "id": 236, + "damage": 4 + }, + { + "id": 236, + "damage": 5 + }, + { + "id": 236, + "damage": 13 + }, + { + "id": 236, + "damage": 9 + }, + { + "id": 236, + "damage": 3 + }, + { + "id": 236, + "damage": 11 + }, + { + "id": 236, + "damage": 10 + }, + { + "id": 236, + "damage": 2 + }, + { + "id": 236, + "damage": 6 + }, + { + "id": 82 + }, + { + "id": 172 + }, + { + "id": 159 + }, + { + "id": 159, + "damage": 8 + }, + { + "id": 159, + "damage": 7 + }, + { + "id": 159, + "damage": 15 + }, + { + "id": 159, + "damage": 12 + }, + { + "id": 159, + "damage": 14 + }, + { + "id": 159, + "damage": 1 + }, + { + "id": 159, + "damage": 4 + }, + { + "id": 159, + "damage": 5 + }, + { + "id": 159, + "damage": 13 + }, + { + "id": 159, + "damage": 9 + }, + { + "id": 159, + "damage": 3 + }, + { + "id": 159, + "damage": 11 + }, + { + "id": 159, + "damage": 10 + }, + { + "id": 159, + "damage": 2 + }, + { + "id": 159, + "damage": 6 + }, + { + "id": 220 + }, + { + "id": 228 + }, + { + "id": 227 + }, + { + "id": 235 + }, + { + "id": 232 + }, + { + "id": 234 + }, + { + "id": 221 + }, + { + "id": 224 + }, + { + "id": 225 + }, + { + "id": 233 + }, + { + "id": 229 + }, + { + "id": 223 + }, + { + "id": 231 + }, + { + "id": 219 + }, + { + "id": 222 + }, + { + "id": 226 + }, + { + "id": 201 + }, + { + "id": 201, + "damage": 2 + }, + { + "id": 3 + }, + { + "id": 3, + "damage": 1 + }, + { + "id": 2 + }, + { + "id": 243 + }, + { + "id": 110 + }, + { + "id": 1 + }, + { + "id": 15 + }, + { + "id": 14 + }, + { + "id": 56 + }, + { + "id": 21 + }, + { + "id": 73 + }, + { + "id": 16 + }, + { + "id": 129 + }, + { + "id": 153 + }, + { + "id": 13 + }, + { + "id": 1, + "damage": 1 + }, + { + "id": 1, + "damage": 3 + }, + { + "id": 1, + "damage": 5 + }, + { + "id": 1, + "damage": 2 + }, + { + "id": 1, + "damage": 4 + }, + { + "id": 1, + "damage": 6 + }, + { + "id": 12 + }, + { + "id": 12, + "damage": 1 + }, + { + "id": 81 + }, + { + "id": 17 + }, + { + "id": 17, + "damage": 1 + }, + { + "id": 17, + "damage": 2 + }, + { + "id": 17, + "damage": 3 + }, + { + "id": 162 + }, + { + "id": 162, + "damage": 1 + }, + { + "id": 18 + }, + { + "id": 18, + "damage": 1 + }, + { + "id": 18, + "damage": 2 + }, + { + "id": 18, + "damage": 3 + }, + { + "id": 161 + }, + { + "id": 161, + "damage": 1 + }, + { + "id": 6 + }, + { + "id": 6, + "damage": 1 + }, + { + "id": 6, + "damage": 2 + }, + { + "id": 6, + "damage": 3 + }, + { + "id": 6, + "damage": 4 + }, + { + "id": 6, + "damage": 5 + }, + { + "id": 295 + }, + { + "id": 361 + }, + { + "id": 362 + }, + { + "id": 458 + }, + { + "id": 296 + }, + { + "id": 457 + }, + { + "id": 392 + }, + { + "id": 394 + }, + { + "id": 391 + }, + { + "id": 396 + }, + { + "id": 260 + }, + { + "id": 322 + }, + { + "id": 466 + }, + { + "id": 103 + }, + { + "id": 360 + }, + { + "id": 382 + }, + { + "id": 86 + }, + { + "id": 91 + }, + { + "id": 31, + "damage": 2 + }, + { + "id": 175, + "damage": 3 + }, + { + "id": 31, + "damage": 1 + }, + { + "id": 175, + "damage": 2 + }, + { + "id": 37 + }, + { + "id": 38 + }, + { + "id": 38, + "damage": 1 + }, + { + "id": 38, + "damage": 2 + }, + { + "id": 38, + "damage": 3 + }, + { + "id": 38, + "damage": 4 + }, + { + "id": 38, + "damage": 5 + }, + { + "id": 38, + "damage": 6 + }, + { + "id": 38, + "damage": 7 + }, + { + "id": 38, + "damage": 8 + }, + { + "id": 175 + }, + { + "id": 175, + "damage": 1 + }, + { + "id": 175, + "damage": 4 + }, + { + "id": 175, + "damage": 5 + }, + { + "id": 351, + "damage": 7 + }, + { + "id": 351, + "damage": 8 + }, + { + "id": 351 + }, + { + "id": 351, + "damage": 1 + }, + { + "id": 351, + "damage": 14 + }, + { + "id": 351, + "damage": 11 + }, + { + "id": 351, + "damage": 10 + }, + { + "id": 351, + "damage": 2 + }, + { + "id": 351, + "damage": 6 + }, + { + "id": 351, + "damage": 12 + }, + { + "id": 351, + "damage": 4 + }, + { + "id": 351, + "damage": 5 + }, + { + "id": 351, + "damage": 13 + }, + { + "id": 351, + "damage": 9 + }, + { + "id": 351, + "damage": 15 + }, + { + "id": 351, + "damage": 3 + }, + { + "id": 106 + }, + { + "id": 111 + }, + { + "id": 32 + }, + { + "id": 80 + }, + { + "id": 79 + }, + { + "id": 174 + }, + { + "id": 78 + }, + { + "id": 365 + }, + { + "id": 319 + }, + { + "id": 363 + }, + { + "id": 423 + }, + { + "id": 411 + }, + { + "id": 349 + }, + { + "id": 460 + }, + { + "id": 461 + }, + { + "id": 462 + }, + { + "id": 366 + }, + { + "id": 320 + }, + { + "id": 364 + }, + { + "id": 424 + }, + { + "id": 412 + }, + { + "id": 350 + }, + { + "id": 463 + }, + { + "id": 297 + }, + { + "id": 282 + }, + { + "id": 459 + }, + { + "id": 413 + }, + { + "id": 393 + }, + { + "id": 357 + }, + { + "id": 400 + }, + { + "id": 354 + }, + { + "id": 39 + }, + { + "id": 40 + }, + { + "id": 99, + "damage": 14 + }, + { + "id": 100, + "damage": 14 + }, + { + "id": 99, + "damage": 15 + }, + { + "id": 99 + }, + { + "id": 344 + }, + { + "id": 338 + }, + { + "id": 353 + }, + { + "id": 52 + }, + { + "id": 97 + }, + { + "id": 97, + "damage": 1 + }, + { + "id": 97, + "damage": 2 + }, + { + "id": 97, + "damage": 3 + }, + { + "id": 97, + "damage": 4 + }, + { + "id": 97, + "damage": 5 + }, + { + "id": 122 + }, + { + "id": 383, + "damage": 10 + }, + { + "id": 383, + "damage": 11 + }, + { + "id": 383, + "damage": 12 + }, + { + "id": 383, + "damage": 13 + }, + { + "id": 383, + "damage": 14 + }, + { + "id": 383, + "damage": 28 + }, + { + "id": 383, + "damage": 22 + }, + { + "id": 383, + "damage": 16 + }, + { + "id": 383, + "damage": 19 + }, + { + "id": 383, + "damage": 30 + }, + { + "id": 383, + "damage": 18 + }, + { + "id": 383, + "damage": 29 + }, + { + "id": 383, + "damage": 23 + }, + { + "id": 383, + "damage": 24 + }, + { + "id": 383, + "damage": 25 + }, + { + "id": 383, + "damage": 26 + }, + { + "id": 383, + "damage": 27 + }, + { + "id": 383, + "damage": 33 + }, + { + "id": 383, + "damage": 38 + }, + { + "id": 383, + "damage": 39 + }, + { + "id": 383, + "damage": 34 + }, + { + "id": 383, + "damage": 48 + }, + { + "id": 383, + "damage": 46 + }, + { + "id": 383, + "damage": 37 + }, + { + "id": 383, + "damage": 35 + }, + { + "id": 383, + "damage": 32 + }, + { + "id": 383, + "damage": 36 + }, + { + "id": 383, + "damage": 47 + }, + { + "id": 383, + "damage": 17 + }, + { + "id": 383, + "damage": 40 + }, + { + "id": 383, + "damage": 45 + }, + { + "id": 383, + "damage": 49 + }, + { + "id": 383, + "damage": 50 + }, + { + "id": 383, + "damage": 55 + }, + { + "id": 383, + "damage": 42 + }, + { + "id": 383, + "damage": 41 + }, + { + "id": 383, + "damage": 43 + }, + { + "id": 383, + "damage": 54 + }, + { + "id": 383, + "damage": 57 + }, + { + "id": 383, + "damage": 104 + }, + { + "id": 383, + "damage": 105 + }, + { + "id": 383, + "damage": 15 + }, + { + "id": 383, + "damage": 44 + }, + { + "id": 49 + }, + { + "id": 7 + }, + { + "id": 88 + }, + { + "id": 87 + }, + { + "id": 213 + }, + { + "id": 372 + }, + { + "id": 121 + }, + { + "id": 200 + }, + { + "id": 240 + }, + { + "id": 432 + }, + { + "id": 433 + }, + { + "id": 19 + }, + { + "id": 19, + "damage": 1 + }, + { + "id": 367 + }, + { + "id": 352 + }, + { + "id": 30 + }, + { + "id": 298 + }, + { + "id": 302 + }, + { + "id": 306 + }, + { + "id": 314 + }, + { + "id": 310 + }, + { + "id": 299 + }, + { + "id": 303 + }, + { + "id": 307 + }, + { + "id": 315 + }, + { + "id": 311 + }, + { + "id": 300 + }, + { + "id": 304 + }, + { + "id": 308 + }, + { + "id": 316 + }, + { + "id": 312 + }, + { + "id": 301 + }, + { + "id": 305 + }, + { + "id": 309 + }, + { + "id": 317 + }, + { + "id": 313 + }, + { + "id": 329 + }, + { + "id": 416 + }, + { + "id": 417 + }, + { + "id": 418 + }, + { + "id": 419 + }, + { + "id": 444 + }, + { + "id": 268 + }, + { + "id": 272 + }, + { + "id": 267 + }, + { + "id": 283 + }, + { + "id": 276 + }, + { + "id": 271 + }, + { + "id": 275 + }, + { + "id": 258 + }, + { + "id": 286 + }, + { + "id": 279 + }, + { + "id": 270 + }, + { + "id": 274 + }, + { + "id": 257 + }, + { + "id": 285 + }, + { + "id": 278 + }, + { + "id": 269 + }, + { + "id": 273 + }, + { + "id": 256 + }, + { + "id": 284 + }, + { + "id": 277 + }, + { + "id": 290 + }, + { + "id": 291 + }, + { + "id": 292 + }, + { + "id": 294 + }, + { + "id": 293 + }, + { + "id": 261 + }, + { + "id": 262 + }, + { + "id": 262, + "damage": 6 + }, + { + "id": 262, + "damage": 7 + }, + { + "id": 262, + "damage": 8 + }, + { + "id": 262, + "damage": 9 + }, + { + "id": 262, + "damage": 10 + }, + { + "id": 262, + "damage": 11 + }, + { + "id": 262, + "damage": 12 + }, + { + "id": 262, + "damage": 13 + }, + { + "id": 262, + "damage": 14 + }, + { + "id": 262, + "damage": 15 + }, + { + "id": 262, + "damage": 16 + }, + { + "id": 262, + "damage": 17 + }, + { + "id": 262, + "damage": 18 + }, + { + "id": 262, + "damage": 19 + }, + { + "id": 262, + "damage": 20 + }, + { + "id": 262, + "damage": 21 + }, + { + "id": 262, + "damage": 22 + }, + { + "id": 262, + "damage": 23 + }, + { + "id": 262, + "damage": 24 + }, + { + "id": 262, + "damage": 25 + }, + { + "id": 262, + "damage": 26 + }, + { + "id": 262, + "damage": 27 + }, + { + "id": 262, + "damage": 28 + }, + { + "id": 262, + "damage": 29 + }, + { + "id": 262, + "damage": 30 + }, + { + "id": 262, + "damage": 31 + }, + { + "id": 262, + "damage": 32 + }, + { + "id": 262, + "damage": 33 + }, + { + "id": 262, + "damage": 34 + }, + { + "id": 262, + "damage": 35 + }, + { + "id": 262, + "damage": 36 + }, + { + "id": 262, + "damage": 37 + }, + { + "id": 346 + }, + { + "id": 398 + }, + { + "id": 332 + }, + { + "id": 359 + }, + { + "id": 259 + }, + { + "id": 450 + }, + { + "id": 374 + }, + { + "id": 384 + }, + { + "id": 373 + }, + { + "id": 373, + "damage": 1 + }, + { + "id": 373, + "damage": 2 + }, + { + "id": 373, + "damage": 3 + }, + { + "id": 373, + "damage": 4 + }, + { + "id": 373, + "damage": 5 + }, + { + "id": 373, + "damage": 6 + }, + { + "id": 373, + "damage": 7 + }, + { + "id": 373, + "damage": 8 + }, + { + "id": 373, + "damage": 9 + }, + { + "id": 373, + "damage": 10 + }, + { + "id": 373, + "damage": 11 + }, + { + "id": 373, + "damage": 12 + }, + { + "id": 373, + "damage": 13 + }, + { + "id": 373, + "damage": 14 + }, + { + "id": 373, + "damage": 15 + }, + { + "id": 373, + "damage": 16 + }, + { + "id": 373, + "damage": 17 + }, + { + "id": 373, + "damage": 18 + }, + { + "id": 373, + "damage": 19 + }, + { + "id": 373, + "damage": 20 + }, + { + "id": 373, + "damage": 21 + }, + { + "id": 373, + "damage": 22 + }, + { + "id": 373, + "damage": 23 + }, + { + "id": 373, + "damage": 24 + }, + { + "id": 373, + "damage": 25 + }, + { + "id": 373, + "damage": 26 + }, + { + "id": 373, + "damage": 27 + }, + { + "id": 373, + "damage": 28 + }, + { + "id": 373, + "damage": 29 + }, + { + "id": 373, + "damage": 30 + }, + { + "id": 373, + "damage": 31 + }, + { + "id": 373, + "damage": 32 + }, + { + "id": 373, + "damage": 33 + }, + { + "id": 373, + "damage": 34 + }, + { + "id": 373, + "damage": 35 + }, + { + "id": 373, + "damage": 36 + }, + { + "id": 438 + }, + { + "id": 438, + "damage": 1 + }, + { + "id": 438, + "damage": 2 + }, + { + "id": 438, + "damage": 3 + }, + { + "id": 438, + "damage": 4 + }, + { + "id": 438, + "damage": 5 + }, + { + "id": 438, + "damage": 6 + }, + { + "id": 438, + "damage": 7 + }, + { + "id": 438, + "damage": 8 + }, + { + "id": 438, + "damage": 9 + }, + { + "id": 438, + "damage": 10 + }, + { + "id": 438, + "damage": 11 + }, + { + "id": 438, + "damage": 12 + }, + { + "id": 438, + "damage": 13 + }, + { + "id": 438, + "damage": 14 + }, + { + "id": 438, + "damage": 15 + }, + { + "id": 438, + "damage": 16 + }, + { + "id": 438, + "damage": 17 + }, + { + "id": 438, + "damage": 18 + }, + { + "id": 438, + "damage": 19 + }, + { + "id": 438, + "damage": 20 + }, + { + "id": 438, + "damage": 21 + }, + { + "id": 438, + "damage": 22 + }, + { + "id": 438, + "damage": 23 + }, + { + "id": 438, + "damage": 24 + }, + { + "id": 438, + "damage": 25 + }, + { + "id": 438, + "damage": 26 + }, + { + "id": 438, + "damage": 27 + }, + { + "id": 438, + "damage": 28 + }, + { + "id": 438, + "damage": 29 + }, + { + "id": 438, + "damage": 30 + }, + { + "id": 438, + "damage": 31 + }, + { + "id": 438, + "damage": 32 + }, + { + "id": 438, + "damage": 33 + }, + { + "id": 438, + "damage": 34 + }, + { + "id": 438, + "damage": 35 + }, + { + "id": 438, + "damage": 36 + }, + { + "id": 441 + }, + { + "id": 441, + "damage": 1 + }, + { + "id": 441, + "damage": 2 + }, + { + "id": 441, + "damage": 3 + }, + { + "id": 441, + "damage": 4 + }, + { + "id": 441, + "damage": 5 + }, + { + "id": 441, + "damage": 6 + }, + { + "id": 441, + "damage": 7 + }, + { + "id": 441, + "damage": 8 + }, + { + "id": 441, + "damage": 9 + }, + { + "id": 441, + "damage": 10 + }, + { + "id": 441, + "damage": 11 + }, + { + "id": 441, + "damage": 12 + }, + { + "id": 441, + "damage": 13 + }, + { + "id": 441, + "damage": 14 + }, + { + "id": 441, + "damage": 15 + }, + { + "id": 441, + "damage": 16 + }, + { + "id": 441, + "damage": 17 + }, + { + "id": 441, + "damage": 18 + }, + { + "id": 441, + "damage": 19 + }, + { + "id": 441, + "damage": 20 + }, + { + "id": 441, + "damage": 21 + }, + { + "id": 441, + "damage": 22 + }, + { + "id": 441, + "damage": 23 + }, + { + "id": 441, + "damage": 24 + }, + { + "id": 441, + "damage": 25 + }, + { + "id": 441, + "damage": 26 + }, + { + "id": 441, + "damage": 27 + }, + { + "id": 441, + "damage": 28 + }, + { + "id": 441, + "damage": 29 + }, + { + "id": 441, + "damage": 30 + }, + { + "id": 441, + "damage": 31 + }, + { + "id": 441, + "damage": 32 + }, + { + "id": 441, + "damage": 33 + }, + { + "id": 441, + "damage": 34 + }, + { + "id": 441, + "damage": 35 + }, + { + "id": 441, + "damage": 36 + }, + { + "id": 280 + }, + { + "id": 355 + }, + { + "id": 355, + "damage": 8 + }, + { + "id": 355, + "damage": 7 + }, + { + "id": 355, + "damage": 15 + }, + { + "id": 355, + "damage": 12 + }, + { + "id": 355, + "damage": 14 + }, + { + "id": 355, + "damage": 1 + }, + { + "id": 355, + "damage": 4 + }, + { + "id": 355, + "damage": 5 + }, + { + "id": 355, + "damage": 13 + }, + { + "id": 355, + "damage": 9 + }, + { + "id": 355, + "damage": 3 + }, + { + "id": 355, + "damage": 11 + }, + { + "id": 355, + "damage": 10 + }, + { + "id": 355, + "damage": 2 + }, + { + "id": 355, + "damage": 6 + }, + { + "id": 50 + }, + { + "id": 76 + }, + { + "id": 123 + }, + { + "id": 169 + }, + { + "id": 89 + }, + { + "id": 348 + }, + { + "id": 323 + }, + { + "id": 321 + }, + { + "id": 389 + }, + { + "id": 390 + }, + { + "id": 281 + }, + { + "id": 425 + }, + { + "id": 58 + }, + { + "id": 61 + }, + { + "id": 379 + }, + { + "id": 380 + }, + { + "id": 145 + }, + { + "id": 145, + "damage": 1 + }, + { + "id": 145, + "damage": 2 + }, + { + "id": 245 + }, + { + "id": 54 + }, + { + "id": 146 + }, + { + "id": 130 + }, + { + "id": 205, + "damage": 16 + }, + { + "id": 218 + }, + { + "id": 218, + "damage": 8 + }, + { + "id": 218, + "damage": 7 + }, + { + "id": 218, + "damage": 15 + }, + { + "id": 218, + "damage": 12 + }, + { + "id": 218, + "damage": 14 + }, + { + "id": 218, + "damage": 1 + }, + { + "id": 218, + "damage": 4 + }, + { + "id": 218, + "damage": 5 + }, + { + "id": 218, + "damage": 13 + }, + { + "id": 218, + "damage": 9 + }, + { + "id": 218, + "damage": 3 + }, + { + "id": 218, + "damage": 11 + }, + { + "id": 218, + "damage": 10 + }, + { + "id": 218, + "damage": 2 + }, + { + "id": 218, + "damage": 6 + }, + { + "id": 47 + }, + { + "id": 116 + }, + { + "id": 25 + }, + { + "id": 84 + }, + { + "id": 500 + }, + { + "id": 501 + }, + { + "id": 502 + }, + { + "id": 503 + }, + { + "id": 504 + }, + { + "id": 505 + }, + { + "id": 506 + }, + { + "id": 507 + }, + { + "id": 508 + }, + { + "id": 509 + }, + { + "id": 510 + }, + { + "id": 511 + }, + { + "id": 397, + "damage": 3 + }, + { + "id": 397, + "damage": 2 + }, + { + "id": 397, + "damage": 4 + }, + { + "id": 397, + "damage": 5 + }, + { + "id": 397 + }, + { + "id": 397, + "damage": 1 + }, + { + "id": 138 + }, + { + "id": 151 + }, + { + "id": 120 + }, + { + "id": 287 + }, + { + "id": 325 + }, + { + "id": 325, + "damage": 1 + }, + { + "id": 325, + "damage": 8 + }, + { + "id": 325, + "damage": 10 + }, + { + "id": 288 + }, + { + "id": 318 + }, + { + "id": 289 + }, + { + "id": 334 + }, + { + "id": 415 + }, + { + "id": 414 + }, + { + "id": 452 + }, + { + "id": 371 + }, + { + "id": 385 + }, + { + "id": 377 + }, + { + "id": 369 + }, + { + "id": 378 + }, + { + "id": 375 + }, + { + "id": 376 + }, + { + "id": 437 + }, + { + "id": 445 + }, + { + "id": 370 + }, + { + "id": 341 + }, + { + "id": 368 + }, + { + "id": 381 + }, + { + "id": 399 + }, + { + "id": 208 + }, + { + "id": 426 + }, + { + "id": 339 + }, + { + "id": 395 + }, + { + "id": 395, + "damage": 2 + }, + { + "id": 386 + }, + { + "id": 340 + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696400000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696401000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696402000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696403000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696404000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696405000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696405000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696405000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696406000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696406000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696406000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696407000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696407000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696407000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696408000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696409000203006c766c05000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640a000203006c766c05000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640b000203006c766c05000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640c000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640c000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640d000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640d000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640e000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640e000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640e000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069640f000203006c766c05000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696410000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696411000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696411000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696411000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696412000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696412000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696412000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c04000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696413000203006c766c05000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696414000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696414000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696415000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696416000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696417000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696417000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696417000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696418000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696418000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696418000203006c766c03000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696419000203006c766c01000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a01000000020200696419000203006c766c02000000" + }, + { + "id": 403, + "nbt_hex": "0a0000090400656e63680a0100000002020069641a000203006c766c01000000" + }, + { + "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": 69 + }, + { + "id": 143, + "damage": 5 + }, + { + "id": 77, + "damage": 5 + }, + { + "id": 410 + }, + { + "id": 125, + "damage": 3 + }, + { + "id": 23, + "damage": 3 + }, + { + "id": 33, + "damage": 1 + }, + { + "id": 29, + "damage": 1 + }, + { + "id": 251 + }, + { + "id": 356 + }, + { + "id": 404 + }, + { + "id": 131 + }, + { + "id": 72 + }, + { + "id": 70 + }, + { + "id": 147 + }, + { + "id": 148 + }, + { + "id": 331 + }, + { + "id": 263 + }, + { + "id": 263, + "damage": 1 + }, + { + "id": 264 + }, + { + "id": 265 + }, + { + "id": 266 + }, + { + "id": 388 + }, + { + "id": 336 + }, + { + "id": 405 + }, + { + "id": 337 + }, + { + "id": 409 + }, + { + "id": 422 + }, + { + "id": 46 + }, + { + "id": 420 + }, + { + "id": 421 + }, + { + "id": 347 + }, + { + "id": 345 + }, + { + "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": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730000000000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000000070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000008070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000007070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000f070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000c070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000e070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000001070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000004070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000005070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000d070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000009070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000003070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000b070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f72010000000a070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000002070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 401, + "nbt_hex": "0a00000a090046697265776f726b73090a004578706c6f73696f6e730a01000000070d0046697265776f726b436f6c6f720100000006070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000010600466c69676874010000" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000000070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72211d1dff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000008070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72524f47ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000007070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72979d9dff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000f070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72f0f0f0ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000c070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72dab33aff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000e070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f721d80f9ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000001070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72262eb0ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000004070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72aa443cff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000005070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72b83289ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000d070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72bd4ec7ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000009070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72aa8bf3ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000003070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72325483ff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000b070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f723dd8feff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f72010000000a070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f721fc780ff00" + }, + { + "id": 402, + "c": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000002070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f72167c5eff00" + }, + { + "id": 402, + "nbt_hex": "0a00000a0d0046697265776f726b734974656d070d0046697265776f726b436f6c6f720100000006070c0046697265776f726b4661646500000000010f0046697265776f726b466c69636b657200010d0046697265776f726b547261696c00010c0046697265776f726b547970650000030b00637573746f6d436f6c6f729c9c16ff00" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/recipes.json b/src/main/resources/recipes.json index 6c93d99033..3e42cb856f 100644 --- a/src/main/resources/recipes.json +++ b/src/main/resources/recipes.json @@ -1,4 +1,5 @@ -[ +{ + "recipes": [ { "type": 1, "width": 3, @@ -20891,4 +20892,5 @@ "nbt": "" } } -] \ No newline at end of file +] +} \ No newline at end of file diff --git a/src/main/resources/report_template.md b/src/main/resources/report_template.md index 576de6c706..6ea821b8f4 100644 --- a/src/main/resources/report_template.md +++ b/src/main/resources/report_template.md @@ -1,4 +1,7 @@ -## Nukkit Bug Report (DO NOT OPEN A ISSUE IF THIS IS A PLUGIN ERROR) +## Nukkit Bug Report + +(DO NOT OPEN A ISSUE IF THIS IS A PLUGIN ERROR) + PLUGIN ERROR: ${PLUGIN_ERROR} ### Issue Description @@ -10,36 +13,35 @@ PLUGIN ERROR: ${PLUGIN_ERROR} ?? ### OS and Versions - -* Nukkit Version: ${NUKKIT_VERSION} - +* Nukkit Version: ${NUKKIT_VERSION} +* Git Commit Abbrev: ${GIT_COMMIT_ABBREV} * Java Version: ``` ${JAVA_VERSION} ``` - + * Host Configuration: - + | Item | Value | |:----:|:-----:| -| Host OS | ${HOSTOS} | -| Memory(RAM) | ${MEMORY} | -| Storage Size | ${STORAGE_SIZE} | -| Storage Type | ${STORAGE_TYPE} | -| CPU Type | ${CPU_TYPE} | +| Host OS | ${HOSTOS} | +| Memory(RAM) | ${MEMORY} | +| Storage Size | ${STORAGE_SIZE} | +| Storage Type | ${STORAGE_TYPE} | +| CPU Type | ${CPU_TYPE} | | CPU Core Count | ${PHYSICAL_CORE} cores ${LOGICAL_CORE} threads | -| Upstream Bandwidth | ?? | +| Upstream Bandwidth | ?? | * Client Configuration: | Item | Value | |:----:|:-----:| -| Client Edition | ?? | -| Client Version | ?? | +| Client Edition | ?? | +| Client Version | ?? | ### Crashdump, Backtrace or Other Files - + ``` ${STACKTRACE} ``` diff --git a/src/test/java/cn/nukkit/test/ClientChainDataTest.java b/src/test/java/cn/nukkit/test/ClientChainDataTest.java index 09476116e6..a6cec4b87e 100644 --- a/src/test/java/cn/nukkit/test/ClientChainDataTest.java +++ b/src/test/java/cn/nukkit/test/ClientChainDataTest.java @@ -30,8 +30,7 @@ void testGetter() throws Exception { "deviceOS=%d, gameVersion=%s, " + "guiScale=%d, languageCode=%s, " + "xuid=%s, currentInputMode=%d, " + - "defaultInputMode=%d, ADRole=%s," + - "tenantId=%s, UIProfile=%d" + "defaultInputMode=%d, UIProfile=%d" , data.getUsername(), data.getClientUUID(), data.getIdentityPublicKey(), data.getClientId(), @@ -39,8 +38,7 @@ void testGetter() throws Exception { data.getDeviceOS(), data.getGameVersion(), data.getGuiScale(), data.getLanguageCode(), data.getXUID(), data.getCurrentInputMode(), - data.getDefaultInputMode(), data.getADRole(), - data.getTenantId(), data.getUIProfile() + data.getDefaultInputMode(), data.getUIProfile() ); String expecting = "userName=lmlstarqaq, clientUUID=8323afe1-641e-3b61-9a92-d5d20b279065, " + "identityPublicKey=MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4lyvA1iVhV2u3pLQqJAjJnJZSlSjib8mM1uB5h5yqOBSvCHW+nZxDmkOAW6MS1GA7yGHitGmfS4jW/yUISUdWvLzEWJYOzphb3GNh5J1oLJRwESc5278i4MEDk1y21/q, " + @@ -49,8 +47,7 @@ void testGetter() throws Exception { "deviceOS=2, gameVersion=1.1.0, " + "guiScale=0, languageCode=zh_CN, " + "xuid=2535465134455915, currentInputMode=2, " + - "defaultInputMode=2, ADRole=2,tenantId=, " + - "UIProfile=1"; + "defaultInputMode=2, UIProfile=1"; assertEquals(got, expecting); } diff --git a/src/test/java/cn/nukkit/test/VarIntTest.java b/src/test/java/cn/nukkit/test/VarIntTest.java index a67a4f2cc8..12fc9db37f 100644 --- a/src/test/java/cn/nukkit/test/VarIntTest.java +++ b/src/test/java/cn/nukkit/test/VarIntTest.java @@ -5,10 +5,12 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.io.*; -import java.math.BigInteger; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * By lmlstarqaq http://snake1999.com/ diff --git a/src/test/java/cn/nukkit/test/ZlibTest.java b/src/test/java/cn/nukkit/test/ZlibTest.java new file mode 100644 index 0000000000..fd372776a8 --- /dev/null +++ b/src/test/java/cn/nukkit/test/ZlibTest.java @@ -0,0 +1,23 @@ +package cn.nukkit.test; + +import cn.nukkit.utils.Zlib; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@DisplayName("Zlib") +class ZlibTest { + + @DisplayName("Inflate and Deflate") + @Test + void testAll() throws Exception { + byte[] in = "lmlstarqaq".getBytes(); + byte[] compressed = Zlib.deflate(in); + byte[] out = Zlib.inflate(compressed); + assertTrue(Arrays.equals(in, out)); + } + +}