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