lastChunk = new Long2ObjectOpenHashMap<>(this.usedChunks);
- int centerX = (int) this.x >> 4;
- int centerZ = (int) this.z >> 4;
+ int centerX = this.getChunkX();
+ int centerZ = this.getChunkZ();
- int radius = spawned ? this.chunkRadius : (int) Math.ceil(Math.sqrt(spawnThreshold));
+ int radius = spawned ? this.chunkRadius : server.c_s_spawnThreshold;
int radiusSqr = radius * radius;
-
-
long index;
for (int x = 0; x <= radius; x++) {
int xx = x * x;
@@ -969,43 +1234,43 @@ protected boolean orderChunks() {
if (distanceSqr > radiusSqr) continue;
/* Top right quadrant */
- if(this.usedChunks.get(index = Level.chunkHash(centerX + x, centerZ + z)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX + x, centerZ + z)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
/* Top left quadrant */
- if(this.usedChunks.get(index = Level.chunkHash(centerX - x - 1, centerZ + z)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX - x - 1, centerZ + z)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
/* Bottom right quadrant */
- if(this.usedChunks.get(index = Level.chunkHash(centerX + x, centerZ - z - 1)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX + x, centerZ - z - 1)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
/* Bottom left quadrant */
- if(this.usedChunks.get(index = Level.chunkHash(centerX - x - 1, centerZ - z - 1)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX - x - 1, centerZ - z - 1)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
- if(x != z){
+ if (x != z) {
/* Top right quadrant mirror */
- if(this.usedChunks.get(index = Level.chunkHash(centerX + z, centerZ + x)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX + z, centerZ + x)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
/* Top left quadrant mirror */
- if(this.usedChunks.get(index = Level.chunkHash(centerX - z - 1, centerZ + x)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX - z - 1, centerZ + x)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
/* Bottom right quadrant mirror */
- if(this.usedChunks.get(index = Level.chunkHash(centerX + z, centerZ - x - 1)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX + z, centerZ - x - 1)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
/* Bottom left quadrant mirror */
- if(this.usedChunks.get(index = Level.chunkHash(centerX - z - 1, centerZ - x - 1)) != Boolean.TRUE) {
+ if (this.usedChunks.get(index = Level.chunkHash(centerX - z - 1, centerZ - x - 1)) != Boolean.TRUE) {
this.loadQueue.put(index, Boolean.TRUE);
}
lastChunk.remove(index);
@@ -1022,83 +1287,99 @@ protected boolean orderChunks() {
if (!loadQueue.isEmpty()) {
NetworkChunkPublisherUpdatePacket packet = new NetworkChunkPublisherUpdatePacket();
packet.position = this.asBlockVector3();
- packet.radius = viewDistance << 4;
+ packet.radius = this.chunkRadius << 4;
this.dataPacket(packet);
}
- Timings.playerChunkOrderTimer.stopTiming();
return true;
}
- @Deprecated
+ /**
+ * This method no longer has special function. Calls dataPacket().
+ * @param packet data packet
+ * @return return value of dataPacket()
+ */
public boolean batchDataPacket(DataPacket packet) {
return this.dataPacket(packet);
}
/**
- * 0 is true
- * -1 is false
- * other is identifer
- * @param packet packet to send
- * @return packet successfully sent
+ * Send a data packet
+ * @param packet data packet
+ * @return sent
*/
public boolean dataPacket(DataPacket packet) {
if (!this.connected) {
return false;
}
- try (Timing ignored = Timings.getSendDataPacketTiming(packet)) {
- DataPacketSendEvent ev = new DataPacketSendEvent(this, packet);
- this.server.getPluginManager().callEvent(ev);
- if (ev.isCancelled()) {
- return false;
- }
+ DataPacket dataPacket = packet.clone();
- if (log.isTraceEnabled() && !server.isIgnoredPacket(packet.getClass())) {
- log.trace("Outbound {}: {}", this.getName(), packet);
- }
+ DataPacketSendEvent ev = new DataPacketSendEvent(this, dataPacket);
+ this.server.getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return false;
+ }
+
+ if (Nukkit.DEBUG > 2 /*&& !server.isIgnoredPacket(packet.getClass())*/) {
+ log.trace("Outbound {}: {}", this.getName(), dataPacket);
+ }
- this.networkSession.sendPacket(packet);
+ if (dataPacket instanceof BatchPacket) {
+ this.networkSession.sendPacket(dataPacket);
+ } else {
+ // Make sure packets always go to BatchingHelper so they are not reordered
+ this.server.batchPackets(new Player[]{this}, new DataPacket[]{dataPacket});
}
return true;
}
- @Deprecated
public int dataPacket(DataPacket packet, boolean needACK) {
- return dataPacket(packet) ? 1 : 0;
+ return this.dataPacket(packet) ? 1 : 0;
}
- /**
- * 0 is true
- * -1 is false
- * other is identifer
- * @param packet packet to send
- * @return packet successfully sent
- */
- @Deprecated
public boolean directDataPacket(DataPacket packet) {
return this.dataPacket(packet);
}
- @Deprecated
public int directDataPacket(DataPacket packet, boolean needACK) {
- return this.dataPacket(packet) ? 1 : 0;
+ return this.directDataPacket(packet) ? 1 : 0;
}
public void forceDataPacket(DataPacket packet, Runnable callback) {
+ DataPacketSendEvent ev = new DataPacketSendEvent(this, packet);
+ this.server.getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return;
+ }
+
+ if (Nukkit.DEBUG > 2 /*&& !server.isIgnoredPacket(packet.getClass())*/) {
+ log.trace("Outbound {}: {}", this.getName(), packet);
+ }
+
this.networkSession.sendImmediatePacket(packet, (callback == null ? () -> {} : callback));
}
+ /**
+ * Get network latency
+ * @return network latency in milliseconds
+ */
public int getPing() {
return this.interfaz.getNetworkLatency(this);
}
+ /**
+ * Attempt to sleep at position
+ * @param pos position
+ * @return successfully set sleeping
+ */
public boolean sleepOn(Vector3 pos) {
if (!this.isOnline()) {
return false;
}
- for (Entity p : this.level.getNearbyEntities(this.boundingBox.grow(2, 1, 2), this)) {
+ Entity[] e = this.level.getNearbyEntities(this.boundingBox.grow(2, 1, 2), this);
+ for (Entity p : e) {
if (p instanceof Player) {
if (((Player) p).sleeping != null && pos.distance(((Player) p).sleeping) <= 0.1) {
return false;
@@ -1118,15 +1399,21 @@ public boolean sleepOn(Vector3 pos) {
this.setDataProperty(new IntPositionEntityData(DATA_PLAYER_BED_POSITION, (int) pos.x, (int) pos.y, (int) pos.z));
this.setDataFlag(DATA_PLAYER_FLAGS, DATA_PLAYER_FLAG_SLEEP, true);
- this.setSpawn(pos);
+ if (!pos.equals(this.getSpawnPosition())) {
+ this.setSpawn(pos);
+ this.sendMessage("§7%tile.bed.respawnSet", true);
+ }
this.level.sleepTicks = 60;
-
this.timeSinceRest = 0;
return true;
}
+ /**
+ * Set player's spawn position
+ * @param pos spawn position
+ */
public void setSpawn(Vector3 pos) {
Level level;
if (!(pos instanceof Position)) {
@@ -1134,15 +1421,26 @@ public void setSpawn(Vector3 pos) {
} else {
level = ((Position) pos).getLevel();
}
- this.spawnPosition = new Position(pos.x, pos.y, pos.z, level);
+ this.spawnPosition = pos instanceof Block ? ((Block) pos).clone().setLevel(level) : new Position(pos.x, pos.y, pos.z, level);
+ this.sendSpawnPos((int) pos.x, (int) pos.y, (int) pos.z, level.getDimension());
+ }
+
+ /**
+ * Internal: Send player spawn position
+ */
+ private void sendSpawnPos(int x, int y, int z, int dimension) {
SetSpawnPositionPacket pk = new SetSpawnPositionPacket();
pk.spawnType = SetSpawnPositionPacket.TYPE_PLAYER_SPAWN;
- pk.x = (int) this.spawnPosition.x;
- pk.y = (int) this.spawnPosition.y;
- pk.z = (int) this.spawnPosition.z;
+ pk.x = x;
+ pk.y = y;
+ pk.z = z;
+ pk.dimension = dimension;
this.dataPacket(pk);
}
+ /**
+ * Stop sleeping. Does nothing if the player is not sleeping.
+ */
public void stopSleep() {
if (this.sleeping != null) {
this.server.getPluginManager().callEvent(new PlayerBedLeaveEvent(this, this.level.getBlock(this.sleeping)));
@@ -1151,7 +1449,6 @@ public void stopSleep() {
this.setDataProperty(new IntPositionEntityData(DATA_PLAYER_BED_POSITION, 0, 0, 0));
this.setDataFlag(DATA_PLAYER_FLAGS, DATA_PLAYER_FLAG_SLEEP, false);
-
this.level.sleepTicks = 0;
AnimatePacket pk = new AnimatePacket();
@@ -1161,8 +1458,21 @@ public void stopSleep() {
}
}
+ /**
+ * Get sleeping position
+ * @return current sleeping position or null if not sleeping
+ */
+ public Vector3 getSleepingPos() {
+ return this.sleeping;
+ }
+
+ /**
+ * Attempts to award an achievement
+ * @param achievementId achievement id
+ * @return new achievement awarded
+ */
public boolean awardAchievement(String achievementId) {
- if (!Server.getInstance().getPropertyBoolean("achievements", true)) {
+ if (!server.achievementsEnabled) {
return false;
}
@@ -1189,6 +1499,15 @@ public boolean awardAchievement(String achievementId) {
return true;
}
+ /**
+ * Get player's gamemode:
+ * 0 = survival
+ * 1 = creative
+ * 2 = adventure
+ * 3 = spectator
+ *
+ * @return gamemode (number)
+ */
public int getGamemode() {
return gamemode;
}
@@ -1196,8 +1515,6 @@ 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) {
gamemode &= 0x03;
@@ -1207,32 +1524,46 @@ private static int getClientFriendlyGamemode(int gamemode) {
return gamemode;
}
+ /**
+ * Set player's gamemode
+ * @param gamemode new gamemode
+ * @return gamemode changed
+ */
public boolean setGamemode(int gamemode) {
return this.setGamemode(gamemode, false, null);
}
+ /**
+ * Set player's gamemode
+ * @param gamemode new gamemode
+ * @param clientSide whether change was client initiated
+ * @return gamemode changed
+ */
public boolean setGamemode(int gamemode, boolean clientSide) {
return this.setGamemode(gamemode, clientSide, null);
}
+ /**
+ * Set player's gamemode
+ * @param gamemode new gamemode
+ * @param clientSide whether change was client initiated
+ * @param newSettings updated adventure settings for the new gamemode; calculated automatically if null
+ * @return gamemode changed
+ */
public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings newSettings) {
if (gamemode < 0 || gamemode > 3 || this.gamemode == gamemode) {
return false;
}
if (newSettings == null) {
- newSettings = this.getAdventureSettings().clone(this);
+ newSettings = this.adventureSettings.clone(this);
newSettings.set(Type.WORLD_IMMUTABLE, (gamemode & 0x02) > 0);
newSettings.set(Type.MINE, (gamemode & 0x02) <= 0);
newSettings.set(Type.BUILD, (gamemode & 0x02) <= 0);
newSettings.set(Type.NO_PVM, gamemode == SPECTATOR);
newSettings.set(Type.ALLOW_FLIGHT, (gamemode & 0x01) > 0);
newSettings.set(Type.NO_CLIP, gamemode == SPECTATOR);
- if (gamemode == SPECTATOR) {
- newSettings.set(Type.FLYING, true);
- } else if ((gamemode & 0x1) == 0) {
- newSettings.set(Type.FLYING, false);
- }
+ newSettings.set(Type.FLYING, (gamemode & 0x1) == 1);
}
PlayerGameModeChangeEvent ev;
@@ -1265,18 +1596,11 @@ public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings n
if (this.isSpectator()) {
this.teleport(this, null);
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_SILENT, true);
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, false);
- /*InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
- inventoryContentPacket.inventoryId = InventoryContentPacket.SPECIAL_CREATIVE;
- this.dataPacket(inventoryContentPacket);*/
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_SILENT, true, false);
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, false); // Sends both
} else {
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_SILENT, false);
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, true);
- /*InventoryContentPacket inventoryContentPacket = new InventoryContentPacket();
- inventoryContentPacket.inventoryId = InventoryContentPacket.SPECIAL_CREATIVE;
- inventoryContentPacket.slots = Item.getCreativeItems().toArray(new Item[0]);
- this.dataPacket(inventoryContentPacket);*/
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_SILENT, false, false);
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, true); // Sends both
}
this.resetFallDistance();
@@ -1290,23 +1614,41 @@ public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings n
return true;
}
- @Deprecated
+ /**
+ * Send adventure settings
+ */
public void sendSettings() {
- this.getAdventureSettings().update();
+ this.adventureSettings.update();
}
+ /**
+ * Check player game mode
+ * @return whether player is in survival mode
+ */
public boolean isSurvival() {
return this.gamemode == SURVIVAL;
}
+ /**
+ * Check player game mode
+ * @return whether player is in creative mode
+ */
public boolean isCreative() {
return this.gamemode == CREATIVE;
}
+ /**
+ * Check player game mode
+ * @return whether player is in spectator mode
+ */
public boolean isSpectator() {
return this.gamemode == SPECTATOR;
}
+ /**
+ * Check player game mode
+ * @return whether player is in adventure mode
+ */
public boolean isAdventure() {
return this.gamemode == ADVENTURE;
}
@@ -1314,45 +1656,16 @@ public boolean isAdventure() {
@Override
public Item[] getDrops() {
if (!this.isCreative() && !this.isSpectator()) {
- return super.getDrops();
- }
-
- return new Item[0];
- }
-
- @Override
- public boolean fastMove(double dx, double dy, double dz) {
- if (dx == 0 && dy == 0 && dz == 0) {
- return true;
- }
-
- Timings.entityMoveTimer.startTiming();
-
- AxisAlignedBB newBB = this.boundingBox.getOffsetBoundingBox(dx, dy, dz);
-
- if (this.isSpectator() || server.getAllowFlight() || !this.level.hasCollision(this, newBB.shrink(0, this.getStepHeight(), 0), false)) {
- this.boundingBox = newBB;
- }
-
- this.x = (this.boundingBox.getMinX() + this.boundingBox.getMaxX()) / 2;
- this.y = this.boundingBox.getMinY() - this.ySize;
- this.z = (this.boundingBox.getMinZ() + this.boundingBox.getMaxZ()) / 2;
-
- this.checkChunks();
-
- if (!this.isSpectator()) {
- if (!this.onGround || dy != 0) {
- AxisAlignedBB bb = this.boundingBox.clone();
- bb.setMinY(bb.getMinY() - 0.75);
-
- this.onGround = this.level.getCollisionBlocks(bb).length > 0;
+ if (this.inventory != null) {
+ List- drops = new ArrayList<>(this.inventory.getContents().values());
+ drops.addAll(this.offhandInventory.getContents().values());
+ //drops.addAll(this.playerUIInventory.getContents().values()); // handled in resetCraftingGridType
+ return drops.toArray(new Item[0]);
}
- this.isCollided = this.onGround;
- this.updateFallState(this.onGround);
+ return new Item[0];
}
- Timings.entityMoveTimer.stopTiming();
- return true;
+ return new Item[0];
}
@Override
@@ -1378,7 +1691,7 @@ protected void checkGroundState(double movX, double movY, double movZ, double dx
for (int z = minZ; z <= maxZ; ++z) {
for (int x = minX; x <= maxX; ++x) {
for (int y = minY; y <= maxY; ++y) {
- Block block = this.level.getBlock(this.temporalVector.setComponents(x, y, z));
+ Block block = this.level.getBlock(chunk, x, y, z, true);
if (!block.canPassThrough() && block.collidesWithBB(realBB)) {
onGround = true;
@@ -1397,189 +1710,379 @@ protected void checkGroundState(double movX, double movY, double movZ, double dx
@Override
protected void checkBlockCollision() {
if (this.isSpectator()) {
- if (this.blocksAround == null) {
- this.blocksAround = new ArrayList<>();
- }
- if (this.collisionBlocks == null) {
- this.collisionBlocks = new ArrayList<>();
- }
return;
}
- boolean portal = false;
+ boolean netherPortal = false;
+ boolean endPortal = false;
for (Block block : this.getCollisionBlocks()) {
if (block.getId() == Block.NETHER_PORTAL) {
- portal = true;
+ netherPortal = true;
+ continue;
+ } else if (block.getId() == Block.END_PORTAL) {
+ endPortal = true;
continue;
}
block.onEntityCollide(this);
}
- if (portal) {
- if (this.isCreative() && this.inPortalTicks < 80) {
- this.inPortalTicks = 80;
- } else {
- this.inPortalTicks++;
+ if (endPortal) {
+ inEndPortalTicks++;
+ } else {
+ this.inEndPortalTicks = 0;
+ }
+
+ if (server.endEnabled && inEndPortalTicks == 1) {
+ EntityPortalEnterEvent ev = new EntityPortalEnterEvent(this, EntityPortalEnterEvent.PortalType.END);
+ this.getServer().getPluginManager().callEvent(ev);
+
+ if (!ev.isCancelled()) {
+ int oldDimension = this.getLevel().getDimension();
+ if (oldDimension == Level.DIMENSION_THE_END) {
+ Position spawn;
+ if ((spawn = this.getSpawn()).getLevel().getDimension() == Level.DIMENSION_OVERWORLD) {
+ if (this.teleport(spawn, TeleportCause.END_PORTAL)) {
+ this.awardAchievement("theEnd2");
+ }
+ } else {
+ if (this.teleport(this.getServer().getDefaultLevel().getSafeSpawn(), TeleportCause.END_PORTAL)) {
+ this.awardAchievement("theEnd2");
+ }
+ }
+ } else {
+ Level end = this.getServer().getLevelByName("the_end");
+ if (end != null) {
+ Position pos = new Position(100.5, 49, 0.5, end);
+
+ FullChunk chunk = end.getChunk(pos.getChunkX(), pos.getChunkZ(), false);
+ if (chunk == null || !chunk.isGenerated()) {
+ end.generateChunk(pos.getChunkX(), pos.getChunkZ(), true);
+
+ int x = pos.getFloorX();
+ int y = pos.getFloorY();
+ int z = pos.getFloorZ();
+ for (int xx = x - 2; xx < x + 3; xx++) {
+ for (int zz = z - 2; zz < z + 3; zz++) {
+ end.setBlockAt(xx, y - 1, zz, BlockID.OBSIDIAN);
+ for (int yy = y; yy < y + 4; yy++) {
+ end.setBlockAt(xx, yy, zz, BlockID.AIR);
+ }
+ }
+ }
+ }
+
+ if (this.teleport(pos, TeleportCause.END_PORTAL) && oldDimension == Level.DIMENSION_OVERWORLD) {
+ this.awardAchievement("theEnd");
+ }
+ }
+ }
}
+ }
+
+ if (netherPortal) {
+ this.inPortalTicks++;
} else {
this.inPortalTicks = 0;
+ this.portalPos = null;
+ }
+
+ if (this.server.isNetherAllowed()) {
+ if (this.inPortalTicks == (this.gamemode == CREATIVE ? 1 : 40) && this.portalPos == null) {
+ Position portalPos = this.level.calculatePortalMirror(this);
+ if (portalPos == null) {
+ return;
+ }
+
+ for (int x = -1; x < 2; x++) {
+ for (int z = -1; z < 2; z++) {
+ int chunkX = (portalPos.getChunkX()) + x, chunkZ = (portalPos.getChunkZ()) + z;
+ FullChunk chunk = portalPos.level.getChunk(chunkX, chunkZ, false);
+ if (chunk == null || !(chunk.isGenerated() || chunk.isPopulated())) {
+ portalPos.level.generateChunk(chunkX, chunkZ, true);
+ }
+ }
+ }
+ this.portalPos = portalPos;
+ }
+
+ if (this.inPortalTicks == (this.gamemode == CREATIVE ? 1 : 80)) {
+ EntityPortalEnterEvent ev = new EntityPortalEnterEvent(this, EntityPortalEnterEvent.PortalType.NETHER);
+ this.getServer().getPluginManager().callEvent(ev);
+
+ if (ev.isCancelled()) {
+ this.portalPos = null;
+ return;
+ }
+
+ int oldDimension = this.getLevel().getDimension();
+ Position foundPortal = BlockNetherPortal.findNearestPortal(this.portalPos);
+ if (foundPortal == null) {
+ BlockNetherPortal.spawnPortal(this.portalPos);
+ if (this.teleport(this.portalPos.add(1.5, 1, 0.5), TeleportCause.NETHER_PORTAL) && oldDimension == Level.DIMENSION_OVERWORLD) {
+ this.awardAchievement("portal");
+ }
+ } else {
+ if (this.teleport(BlockNetherPortal.getSafePortal(foundPortal), TeleportCause.NETHER_PORTAL) && oldDimension == Level.DIMENSION_OVERWORLD) {
+ this.awardAchievement("portal");
+ }
+ }
+ this.portalPos = null;
+ }
}
}
+ /**
+ * Internal: Check nearby entities and try to pick them up
+ */
protected void checkNearEntities() {
- for (Entity entity : this.level.getNearbyEntities(this.boundingBox.grow(1, 0.5, 1), this)) {
- entity.scheduleUpdate();
-
- if (!entity.isAlive() || !this.isAlive()) {
+ Entity[] e = this.level.getNearbyEntities(this.boundingBox.grow(1, 0.5, 1), this);
+ for (Entity entity : e) {
+ if (!entity.isAlive()) {
continue;
}
+ // Update pickup delay
+ if (entity.updateMode % 3 == 2) {
+ entity.scheduleUpdate();
+ }
+
this.pickupEntity(entity, true);
}
}
- protected void handleMovement(Vector3 clientPos) {
+ /**
+ * Internal: Process player movement
+ *
+ * @param newPos client position
+ */
+ protected void handleMovement(Vector3 newPos) {
if (!this.isAlive() || !this.spawned || this.teleportPosition != null || this.isSleeping()) {
return;
}
- boolean invalidMotion = false;
- double distance = clientPos.distanceSquared(this);
- if (distance > 128) {
- invalidMotion = true;
- } else if (!this.level.isChunkGenerated(clientPos.getChunkX(), clientPos.getChunkZ())) {
- invalidMotion = true;
- this.nextChunkOrderRun = 0;
- }
+ double distanceSquared = newPos.distanceSquared(this);
+ if (distanceSquared == 0) {
+ if (this.lastYaw != this.yaw || this.lastPitch != this.pitch) {
+ this.lastYaw = this.yaw;
+ this.lastPitch = this.pitch;
+ this.needSendRotation = true;
+ }
- if (invalidMotion) {
- this.revertClientMotion(this.getLocation());
+ if (this.speed == null) speed = new Vector3(0, 0, 0);
+ else this.speed.setComponents(0, 0, 0);
return;
}
- double diffX = clientPos.getX() - this.x;
- double diffY = clientPos.getY() - this.y;
- double diffZ = clientPos.getZ() - this.z;
+ int maxDist = 9;
+ if (this.riptideTicks > 95 || newPos.y - this.y < 2 || this.isOnLadder()) { // TODO: Remove ladder/vines check when block collisions are fixed
+ maxDist = 49;
+ }
- // Client likes to clip into few blocks like stairs or slabs
- // This should help reduce the server mis-prediction at least a bit
- diffY += this.ySize * (1 - 0.4D);
+ if (distanceSquared > maxDist) {
+ this.revertClientMotion(this);
+ server.getLogger().debug(username + ": distanceSquared=" + distanceSquared + " > maxDist=" + maxDist);
+ return;
+ }
- this.fastMove(diffX, diffY, diffZ);
+ if (this.chunk == null || !this.chunk.isGenerated()) {
+ BaseFullChunk chunk = this.level.getChunk(newPos.getChunkX(), newPos.getChunkZ(), false);
+ if (chunk == null || !chunk.isGenerated()) {
+ this.nextChunkOrderRun = 0;
+ this.revertClientMotion(this);
+ return;
+ } else {
+ if (this.chunk != null) {
+ this.chunk.removeEntity(this);
+ }
+ this.chunk = chunk;
+ }
+ }
- double corrX = this.x - clientPos.getX();
- double corrY = this.y - clientPos.getY();
- double corrZ = this.z - clientPos.getZ();
+ double dx = newPos.x - this.x;
+ double dy = newPos.y - this.y;
+ double dz = newPos.z - this.z;
- double yS = this.getStepHeight() + this.ySize;
- if (corrY >= -yS || corrY <= yS) {
- corrY = 0;
- }
+ //the client likes to clip into blocks like stairs, but we do full server-side prediction of that without
+ //help from the client's position changes, so we deduct the expected clip height from the moved distance.
+ dy += this.ySize * (1 - STEP_CLIP_MULTIPLIER); // FIXME: ySize is always 0
- if (this.checkMovement && (Math.abs(corrX) > 0.5 || Math.abs(corrY) > 0.5 || Math.abs(corrZ) > 0.5) &&
- this.riding == null && !this.hasEffect(Effect.LEVITATION) && !this.hasEffect(Effect.SLOW_FALLING)) {
- double diff = corrX * corrX + corrZ * corrZ;
- if (diff > 0.5) {
- PlayerInvalidMoveEvent event = new PlayerInvalidMoveEvent(this, true);
- this.getServer().getPluginManager().callEvent(event);
- if (!event.isCancelled() && (invalidMotion = event.isRevert())) {
- this.server.getLogger().warning(this.getServer().getLanguage().translateString("nukkit.player.invalidMove", this.getName()));
+ if (this.checkMovement && this.riptideTicks <= 0 && this.riding == null && !this.isGliding() && !this.getAllowFlight()) {
+ double hSpeed = dx * dx + dz * dz;
+ if (hSpeed > MAXIMUM_SPEED) {
+ PlayerInvalidMoveEvent ev;
+ this.getServer().getPluginManager().callEvent(ev = new PlayerInvalidMoveEvent(this, true));
+ if (!ev.isCancelled()) {
+ server.getLogger().debug(username + ": hSpeed=" + hSpeed + " > MAXIMUM_SPEED=" + MAXIMUM_SPEED);
+ this.revertClientMotion(this);
+ return;
}
}
+ }
- if (!invalidMotion) {
- this.x = clientPos.getX();
- this.y = clientPos.getY();
- this.z = clientPos.getZ();
- double radius = this.getWidth() / 2;
- this.boundingBox.setBounds(this.x - radius, this.y, this.z - radius, this.x + radius, this.y + this.getHeight(), this.z + radius);
- }
+ // Replacement for this.fastMove(dx, dy, dz) start
+ if (this.isSpectator() || !this.level.hasCollision(this, this.boundingBox.getOffsetBoundingBox(dx, dy, dz).shrink(0.1, this.getStepHeight(), 0.1), false)) {
+ this.x = newPos.x;
+ this.y = newPos.y;
+ this.z = newPos.z;
+
+ this.boundingBox.setBounds(this.x - 0.3, this.y, this.z - 0.3, this.x + 0.3, this.y + this.getHeight(), this.z + 0.3);
}
- if (invalidMotion) {
- this.revertClientMotion(this.getLocation());
- return;
+ this.checkChunks();
+
+ if (!this.isSpectator() && (!this.onGround || dy != 0)) {
+ AxisAlignedBB bb = this.boundingBox.clone();
+ bb.setMinY(bb.getMinY() - 0.75);
+
+ // Hack: fix fall damage from walls while falling
+ if (Math.abs(dy) > 0.01) {
+ bb.setMinX(bb.getMinX() + 0.1);
+ bb.setMaxX(bb.getMaxX() - 0.1);
+ bb.setMinZ(bb.getMinZ() + 0.1);
+ bb.setMaxZ(bb.getMaxZ() - 0.1);
+ }
+
+ this.onGround = this.level.hasCollisionBlocks(this, bb);
}
- Location source = new Location(this.lastX, this.lastY, this.lastZ, this.lastYaw, this.lastPitch, this.level);
- Location target = this.getLocation();
- double delta = Math.pow(this.lastX - target.getX(), 2) + Math.pow(this.lastY - target.getY(), 2) + Math.pow(this.lastZ - target.getZ(), 2);
- double deltaAngle = Math.abs(this.lastYaw - target.getYaw()) + Math.abs(this.lastPitch - target.getPitch());
+ this.isCollided = this.onGround;
+ this.updateFallState(this.onGround);
+ // Replacement for this.fastMove(dx, dy, dz) end
+
+ Location from = new Location(
+ this.lastX,
+ this.lastY,
+ this.lastZ,
+ this.lastYaw,
+ this.lastPitch,
+ this.level);
+ Location to = this.getLocation();
+
+ if (!this.firstMove) {
+ PlayerMoveEvent moveEvent = new PlayerMoveEvent(this, from, to);
+ this.server.getPluginManager().callEvent(moveEvent);
+
+ if (moveEvent.isCancelled()) {
+ this.teleport(from, null);
+ return;
+ }
+
+ this.lastX = to.x;
+ this.lastY = to.y;
+ this.lastZ = to.z;
- if (delta > 0.0005 || deltaAngle > 1) {
- boolean isFirst = this.firstMove;
- this.firstMove = false;
+ this.lastYaw = to.yaw;
+ this.lastPitch = to.pitch;
- this.lastX = target.x;
- this.lastY = target.y;
- this.lastZ = target.z;
- this.lastYaw = target.yaw;
- this.lastPitch = target.pitch;
+ this.blocksAround = null;
+ this.collisionBlocks = null;
- if (!isFirst) {
- List blocksAround = null;
- if (this.blocksAround != null) {
- blocksAround = new ObjectArrayList<>(this.blocksAround);
- }
- List collidingBlocks = null;
- if (this.collisionBlocks != null) {
- collidingBlocks = new ObjectArrayList<>(this.collisionBlocks);
- }
+ if (!to.equals(moveEvent.getTo())) { // If plugins modify the destination
+ this.teleport(moveEvent.getTo(), null);
+ } else {
+ this.addMovement(this.x, this.y, this.z, this.yaw, this.pitch, this.yaw);
+ }
+ } else {
+ this.lastX = to.x;
+ this.lastY = to.y;
+ this.lastZ = to.z;
- PlayerMoveEvent event = new PlayerMoveEvent(this, source, target);
- this.blocksAround = null;
- this.collisionBlocks = null;
- this.server.getPluginManager().callEvent(event);
+ this.lastYaw = to.yaw;
+ this.lastPitch = to.pitch;
+ }
- if (!(invalidMotion = event.isCancelled())) {
- if (!target.equals(event.getTo())) {
- this.teleport(event.getTo(), null);
- } else {
- this.addMovement(this.x, this.y, this.z, this.yaw, this.pitch, this.yaw);
+ this.firstMove = false;
+
+ if (this.speed == null) speed = new Vector3(from.x - to.x, from.y - to.y, from.z - to.z);
+ else this.speed.setComponents(from.x - to.x, from.y - to.y, from.z - to.z);
+
+ if (this.isFoodEnabled() && this.getServer().getDifficulty() > 0) {
+ if (distanceSquared >= 0.05) {
+ double jump = 0;
+ double swimming = this.isInsideOfWater() ? 0.01 * distanceSquared : 0;
+ double dd = distanceSquared;
+ if (swimming != 0) dd = 0;
+ if (this.isSprinting()) {
+ if (this.inAirTicks == 3 && swimming == 0) {
+ jump = 0.2;
}
+ this.foodData.updateFoodExpLevel(0.1 * dd + jump + swimming);
} else {
- this.blocksAround = blocksAround;
- this.collisionBlocks = collidingBlocks;
+ if (this.inAirTicks == 3 && swimming == 0) {
+ jump = 0.05;
+ }
+ this.foodData.updateFoodExpLevel(jump + swimming);
}
}
-
- if (this.speed == null) {
- this.speed = new Vector3(source.x - target.x, source.y - target.y, source.z - target.z);
- } else {
- this.speed.setComponents(source.x - target.x, source.y - target.y, source.z - target.z);
- }
- } else {
- if (this.speed == null) speed = new Vector3(0, 0, 0);
- else this.speed.setComponents(0, 0, 0);
}
- if ((this.isFoodEnabled() || this.getServer().getDifficulty() == 0) && distance >= 0.05) {
- double jump = 0;
- double swimming = this.isInsideOfWater() ? 0.015 * distance : 0;
- double distance2 = distance;
- if (swimming != 0) distance2 = 0;
- if (this.isSprinting()) {
- if (this.inAirTicks == 3 && swimming == 0) {
- jump = 0.2;
+ if (this.riding == null && this.inventory != null) {
+ Item boots = this.inventory.getBootsFast();
+
+ Enchantment frostWalker = boots.getEnchantment(Enchantment.ID_FROST_WALKER);
+ if (frostWalker != null && frostWalker.getLevel() > 0 && !this.isSpectator() && this.y > this.level.getMinBlockY() && this.y <= this.level.getMaxBlockY()) {
+ int radius = 2 + frostWalker.getLevel();
+ for (int coordX = this.getFloorX() - radius; coordX < this.getFloorX() + radius + 1; coordX++) {
+ for (int coordZ = this.getFloorZ() - radius; coordZ < this.getFloorZ() + radius + 1; coordZ++) {
+ Block block = level.getBlock(this.chunk, coordX, this.getFloorY() - 1, coordZ, true);
+ if ((block.getId() == Block.STILL_WATER || block.getId() == Block.WATER && block.getDamage() == 0) && block.up().getId() == Block.AIR) {
+ WaterFrostEvent waterFrostEvent = new WaterFrostEvent(block);
+ server.getPluginManager().callEvent(waterFrostEvent);
+ if (!waterFrostEvent.isCancelled()) {
+ level.setBlockAt((int) block.x, (int) block.y, (int) block.z, Block.ICE_FROSTED, 0);
+ level.scheduleUpdate(level.getBlock(this.chunk, block.getFloorX(), block.getFloorY(), block.getFloorZ(), true), Utils.random.nextInt(20, 40));
+ }
+ }
+ }
}
- this.getFoodData().updateFoodExpLevel(0.1 * distance2 + jump + swimming);
- } else {
- if (this.inAirTicks == 3 && swimming == 0) {
- jump = 0.05;
+ }
+
+ Enchantment soulSpeedEnchantment = boots.getEnchantment(Enchantment.ID_SOUL_SPEED);
+ if (soulSpeedEnchantment != null && soulSpeedEnchantment.getLevel() > 0) {
+ int down = this.getLevel().getBlockIdAt(chunk, getFloorX(), getFloorY() - 1, getFloorZ());
+ if (this.inSoulSand && down != BlockID.SOUL_SAND) {
+ this.inSoulSand = false;
+ this.setMovementSpeed(DEFAULT_SPEED, true);
+ } else if (!this.inSoulSand && down == BlockID.SOUL_SAND) {
+ this.inSoulSand = true;
+ float soulSpeed = (soulSpeedEnchantment.getLevel() * 0.105f) + 1.3f;
+ this.setMovementSpeed(DEFAULT_SPEED * soulSpeed, true);
}
- this.getFoodData().updateFoodExpLevel(jump + swimming);
}
}
this.forceMovement = null;
- if (distance != 0 && this.nextChunkOrderRun > 20) {
+ if (distanceSquared != 0 && this.nextChunkOrderRun > 20) {
this.nextChunkOrderRun = 20;
}
+ this.needSendRotation = false; // Sent with movement
+
this.resetClientMovement();
}
+ @Override
+ public void recalculateBoundingBox(boolean send) {
+ double height = isSwimming() || isGliding() || isCrawling() ? 0.6 : isSneaking() ? 1.5 : 1.8;
+ this.boundingBox.setBounds(
+ this.x - 0.3,
+ this.y + this.ySize,
+ z - 0.3,
+ x + 0.3,
+ y + height + this.ySize,
+ z + 0.3
+ );
+
+ if (send) {
+ FloatEntityData bbH = new FloatEntityData(DATA_BOUNDING_BOX_HEIGHT, (float) height);
+ FloatEntityData bbW = new FloatEntityData(DATA_BOUNDING_BOX_WIDTH, this.getWidth());
+ this.dataProperties.put(bbH);
+ this.dataProperties.put(bbW);
+ sendData(this.hasSpawned.values().toArray(new Player[0]), new EntityMetadata().put(bbH).put(bbW));
+ }
+ }
+
protected void resetClientMovement() {
this.newPosition = null;
}
@@ -1590,11 +2093,10 @@ protected void revertClientMotion(Location originalPos) {
this.lastZ = originalPos.getZ();
this.lastYaw = originalPos.getYaw();
this.lastPitch = originalPos.getPitch();
-
Vector3 syncPos = originalPos.add(0, 0.00001, 0);
+ this.needSendRotation = false;
this.sendPosition(syncPos, originalPos.getYaw(), originalPos.getPitch(), MovePlayerPacket.MODE_RESET);
this.forceMovement = syncPos;
-
if (this.speed == null) {
this.speed = new Vector3(0, 0, 0);
} else {
@@ -1602,31 +2104,25 @@ protected void revertClientMotion(Location originalPos) {
}
}
- @Override
- public double getStepHeight() {
- return 0.6f;
- }
-
@Override
public void addMovement(double x, double y, double z, double yaw, double pitch, double headYaw) {
- this.sendPosition(new Vector3(x, y, z), yaw, pitch, MovePlayerPacket.MODE_NORMAL, this.getViewers().values().toArray(new Player[0]));
+ this.sendPositionToViewers(x, y, z, yaw, pitch, headYaw);
}
@Override
public boolean setMotion(Vector3 motion) {
if (super.setMotion(motion)) {
- if (this.chunk != null) {
- this.addMotion(this.motionX, this.motionY, this.motionZ); //Send to others
+ if (this.chunk != null && this.spawned) {
+ this.addMotion(this.motionX, this.motionY, this.motionZ); // Send to others
SetEntityMotionPacket pk = new SetEntityMotionPacket();
pk.eid = this.id;
pk.motionX = (float) motion.x;
pk.motionY = (float) motion.y;
pk.motionZ = (float) motion.z;
- this.dataPacket(pk); //Send to self
+ this.dataPacket(pk);
}
if (this.motionY > 0) {
- //todo: check this
this.startAirTicks = (int) ((-(Math.log(this.getGravity() / (this.getGravity() + this.getDrag() * this.motionY))) / this.getDrag()) * 2 + 5);
}
@@ -1636,20 +2132,46 @@ public boolean setMotion(Vector3 motion) {
return false;
}
- public void sendAttributes() {
- UpdateAttributesPacket pk = new UpdateAttributesPacket();
- pk.entityId = this.getId();
- pk.entries = new Attribute[]{
- Attribute.getAttribute(Attribute.MAX_HEALTH).setMaxValue(this.getMaxHealth()).setValue(health > 0 ? (health < getMaxHealth() ? health : getMaxHealth()) : 0),
- Attribute.getAttribute(Attribute.MAX_HUNGER).setValue(this.getFoodData().getLevel()),
- Attribute.getAttribute(Attribute.MOVEMENT_SPEED).setValue(this.getMovementSpeed()),
- Attribute.getAttribute(Attribute.EXPERIENCE_LEVEL).setValue(this.getExperienceLevel()),
- Attribute.getAttribute(Attribute.EXPERIENCE).setValue(((float) this.getExperience()) / calculateRequireExperience(this.getExperienceLevel()))
- };
- this.dataPacket(pk);
- }
-
- @Override
+ /**
+ * Set player's server side motion. Does not send updated motion to client.
+ * @param motion new motion vector
+ */
+ public void setMotionLocally(Vector3 motion) {
+ if (!this.justCreated) {
+ EntityMotionEvent ev = new EntityMotionEvent(this, motion);
+ this.server.getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return;
+ }
+ }
+
+ this.motionX = motion.x;
+ this.motionY = motion.y;
+ this.motionZ = motion.z;
+
+ if (this.motionY > 0) {
+ this.startAirTicks = (int) ((-(Math.log(this.getGravity() / (this.getGravity() + this.getDrag() * this.motionY))) / this.getDrag()) * 2 + 5);
+ }
+ }
+
+ /**
+ * Send all default attributes
+ */
+ public void sendAttributes() {
+ int healthMax = this.getMaxHealth();
+ UpdateAttributesPacket pk = new UpdateAttributesPacket();
+ pk.entityId = this.getId();
+ pk.entries = new Attribute[]{
+ Attribute.getAttribute(Attribute.MAX_HEALTH).setMaxValue(healthMax).setValue(health > 0 ? (health < healthMax ? health : healthMax) : 0),
+ Attribute.getAttribute(Attribute.MAX_HUNGER).setValue(this.foodData.getLevel()).setDefaultValue(this.foodData.getMaxLevel()),
+ Attribute.getAttribute(Attribute.MOVEMENT_SPEED).setValue(this.getMovementSpeed()).setDefaultValue(this.getMovementSpeed()),
+ Attribute.getAttribute(Attribute.EXPERIENCE_LEVEL).setValue(this.expLevel),
+ Attribute.getAttribute(Attribute.EXPERIENCE).setValue(((float) this.exp) / calculateRequireExperience(this.expLevel))
+ };
+ this.dataPacket(pk);
+ }
+
+ @Override
public boolean onUpdate(int currentTick) {
if (!this.loggedIn) {
return false;
@@ -1661,21 +2183,23 @@ public boolean onUpdate(int currentTick) {
return true;
}
- this.messageCounter = 2;
-
this.lastUpdate = currentTick;
- if (this.fishing != null && this.server.getTick() % 20 == 0) {
- if (this.distance(fishing) > 33) {
+ if (this.riptideTicks > 0) {
+ this.riptideTicks -= tickDiff;
+ }
+
+ if (this.fishing != null && this.age % 10 == 0) {
+ if (this.distanceSquared(fishing) > 1089) { // 33 blocks
this.stopFishing(false);
}
}
if (!this.isAlive() && this.spawned) {
- ++this.deadTicks;
- if (this.deadTicks >= 10) {
- this.despawnFromAll();
- }
+ //++this.deadTicks;
+ //if (this.deadTicks >= 10) {
+ this.despawnFromAll(); // HACK: fix "dead" players
+ //}
return true;
}
@@ -1684,96 +2208,152 @@ public boolean onUpdate(int currentTick) {
this.handleMovement(this.clientMovements.poll());
}
- if (!this.isSpectator()) {
+ if (this.needSendRotation) {
+ this.addMovement(this.x, this.y, this.z, this.yaw, this.pitch, this.yaw);
+ this.needSendRotation = false;
+ }
+
+ this.motionX = this.motionY = this.motionZ = 0; // HACK: fix player knockback being messed up
+
+ if (!this.isSpectator() && this.isAlive()) {
this.checkNearEntities();
}
this.entityBaseTick(tickDiff);
if (this.getServer().getDifficulty() == 0 && this.level.getGameRules().getBoolean(GameRule.NATURAL_REGENERATION)) {
- if (this.getHealth() < this.getMaxHealth() && this.ticksLived % 20 == 0) {
+ if (this.getHealth() < this.getRealMaxHealth() && this.age % 20 == 0) {
this.heal(1);
}
- PlayerFood foodData = this.getFoodData();
-
- if (foodData.getLevel() < 20 && this.ticksLived % 10 == 0) {
- foodData.addFoodLevel(1, 0);
+ if (this.foodData.getLevel() < 20 && this.age % 10 == 0) {
+ this.foodData.addFoodLevel(1, 0);
}
}
- if (this.isOnFire() && this.lastUpdate % 10 == 0) {
+ if (this.isOnFire() && this.age % 10 == 0) {
if (this.isCreative() && !this.isInsideOfFire()) {
this.extinguish();
- } else if (this.getLevel().isRaining()) {
- if (this.getLevel().canBlockSeeSky(this)) {
- this.extinguish();
- }
+ } else if (this.getLevel().isRaining() && this.canSeeSky()) {
+ this.extinguish();
}
}
if (!this.isSpectator() && this.speed != null) {
if (this.onGround) {
- if (this.inAirTicks != 0) {
- this.startAirTicks = 5;
+
+ // 1.20.10 doesn't stop it automatically
+ if (this.isGliding()) {
+ this.setGliding(false);
}
- this.inAirTicks = 0;
- this.highestPosition = this.y;
+
+ this.resetFallDistance();
} else {
- if (this.checkMovement && !this.isGliding() && !server.getAllowFlight() && !this.getAdventureSettings().get(Type.ALLOW_FLIGHT) && this.inAirTicks > 20 && !this.isSleeping() && !this.isImmobile() && !this.isSwimming() && this.riding == null && !this.hasEffect(Effect.LEVITATION) && !this.hasEffect(Effect.SLOW_FALLING)) {
+ if (this.checkMovement && this.riptideTicks < 1 && !this.isGliding() && !server.getAllowFlight() && this.inAirTicks > 20 && !this.getAllowFlight() && !this.isSleeping() && !this.isImmobile() && !this.isSwimming() && this.riding == null && !this.hasEffect(Effect.LEVITATION) && !this.hasEffect(Effect.SLOW_FALLING) && this.speed != null && !ZERO_VECTOR3.equals(this.speed)) {
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);
-
- int block = this.getLevelBlock().getId();
- boolean ignore = block == Block.LADDER || block == Block.VINES || block == Block.COBWEB;
-
- if (!this.hasEffect(Effect.JUMP) && diff > 0.6 && expectedVelocity < this.speed.y && !ignore) {
- if (this.inAirTicks < 150) {
- this.setMotion(new Vector3(0, expectedVelocity, 0));
- } else if (this.kick(PlayerKickEvent.Reason.FLYING_DISABLED, "Flying is not enabled on this server")) {
+ double diff = Math.abs(Math.abs(expectedVelocity) - Math.abs(this.speed.y));
+
+ if (diff > 1 && expectedVelocity < 0) {
+ if (this.inAirTicks < 200) {
+ PlayerInvalidMoveEvent ev = new PlayerInvalidMoveEvent(this, true);
+ this.getServer().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ this.startAirTicks = this.inAirTicks - 10;
+ this.setMotion(new Vector3(0, expectedVelocity, 0));
+ }
+ } else if (this.kick(PlayerKickEvent.Reason.FLYING_DISABLED, MSG_FLYING_NOT_ENABLED, true)) {
return false;
}
}
- if (ignore) {
- this.resetFallDistance();
- }
}
if (this.y > highestPosition) {
this.highestPosition = this.y;
}
- if (this.isGliding()) this.resetFallDistance();
-
- ++this.inAirTicks;
-
+ if (this.isSwimming() || this.isOnLadder() || (this.isGliding() && this.getPitch() <= 40 && Math.abs(this.speed.y) < 0.5)) {
+ this.resetFallDistance();
+ } else if (this.isGliding()) {
+ this.resetInAirTicks();
+ } else {
+ ++this.inAirTicks;
+ }
}
- if (this.isSurvival() || this.isAdventure()) {
- if (this.getFoodData() != null) this.getFoodData().update(tickDiff);
+ if (this.foodData != null) {
+ this.foodData.update(tickDiff);
}
}
+ }
- if (!this.isSleeping()) {
- this.timeSinceRest++;
+ if (this.age % 20 == 0) {
+ if (this.isGliding()) {
+ PlayerInventory inv = this.getInventory();
+ if (inv != null) {
+ Item elytra = inv.getChestplate();
+ if (elytra == null || elytra.getId() != ItemID.ELYTRA) {
+ this.setGliding(false);
+ } else if ((this.gamemode & 0x01) == 0 && this.age % (20 * (elytra.getEnchantmentLevel(Enchantment.ID_DURABILITY) + 1)) == 0) {
+ elytra.setDamage(elytra.getDamage() + 1);
+ if (elytra.getDamage() >= elytra.getMaxDurability()) {
+ this.setGliding(false);
+ }
+ inv.setChestplate(elytra);
+ }
+ }
}
}
+ if (this.age % 5 == 0 && this.isBreakingBlock() && !this.isCreative()) {
+ //this.level.addLevelSoundEvent(this.breakingBlock, LevelSoundEventPacket.SOUND_HIT, blockRuntimeId);
+ this.level.addParticle(new PunchBlockParticle(this.breakingBlock, this.breakingBlock, this.breakingBlockFace));
+ }
+
this.checkTeleportPosition();
- if (this.spawned && this.dummyBossBars.size() > 0 && currentTick % 100 == 0) {
+ if (this.spawned && !this.dummyBossBars.isEmpty() && currentTick % 100 == 0) {
this.dummyBossBars.values().forEach(DummyBossBar::updateBossEntityPosition);
}
+ this.tickShield(tickDiff);
+
+ if (!this.isSleeping()) {
+ this.timeSinceRest += tickDiff;
+ }
+
return true;
}
+ /**
+ * Update shield blocking status
+ */
+ private void tickShield(int tickDiff) {
+ if (!this.canTickShield) {
+ return;
+ }
+ if (this.blockingDelay > 0) {
+ this.blockingDelay -= tickDiff;
+ }
+ boolean shieldInHand = this.getInventory().getItemInHandFast().getId() == ItemID.SHIELD;
+ boolean shieldInOffhand = this.getOffhandInventory().getItemFast(0).getId() == ItemID.SHIELD;
+ if (this.isBlocking()) {
+ if (!this.isSneaking() || (!shieldInHand && !shieldInOffhand)) {
+ this.setBlocking(false);
+ }
+ } else if (this.blockingDelay <= 0 && this.isSneaking() && (shieldInHand || shieldInOffhand)) {
+ this.setBlocking(true);
+ }
+ }
+
+ /**
+ * Update interaction button text
+ */
public void checkInteractNearby() {
int interactDistance = isCreative() ? 5 : 3;
if (canInteract(this, interactDistance)) {
- if (getEntityPlayerLookingAt(interactDistance) != null) {
- EntityInteractable onInteract = getEntityPlayerLookingAt(interactDistance);
- String buttonText = onInteract.getInteractButtonText(this);
+ EntityInteractable e = getEntityPlayerLookingAt(interactDistance);
+ if (e != null) {
+ String buttonText = e.getInteractButtonText(this);
if (buttonText == null) {
buttonText = "";
}
@@ -1793,8 +2373,6 @@ public void checkInteractNearby() {
* @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
@@ -1814,17 +2392,13 @@ public EntityInteractable getEntityPlayerLookingAt(int maxDistance) {
}
}
}
- } catch (Exception ex) {
- // nothing to log here!
- }
+ } catch (Exception ignored) {}
}
- timing.stopTiming();
-
return entity;
}
- private EntityInteractable getEntityAtPosition(Entity[] nearbyEntities, int x, int y, int z) {
+ private static 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
@@ -1835,6 +2409,9 @@ private EntityInteractable getEntityAtPosition(Entity[] nearbyEntities, int x, i
return null;
}
+ /**
+ * Internal: Process chunk sending
+ */
public void checkNetwork() {
if (!this.isOnline()) {
return;
@@ -1849,68 +2426,122 @@ public void checkNetwork() {
}
}
+ /**
+ * Check whether target is too far away to be interacted with
+ * @param pos target position
+ * @param maxDistance maximum distance
+ * @return can interact
+ */
public boolean canInteract(Vector3 pos, double maxDistance) {
return this.canInteract(pos, maxDistance, 6.0);
}
+ /**
+ * Check whether target is too far away to be interacted with
+ * @param pos target position
+ * @param maxDistance maximum distance
+ * @param maxDiff maximum diff
+ * @return can interact
+ */
public boolean canInteract(Vector3 pos, double maxDistance, double maxDiff) {
if (this.distanceSquared(pos) > maxDistance * maxDistance) {
return false;
}
Vector2 dV = this.getDirectionPlane();
- double dot = dV.dot(new Vector2(this.x, this.z));
- double dot1 = dV.dot(new Vector2(pos.x, pos.z));
- return (dot1 - dot) >= -maxDiff;
+ return (dV.dot(new Vector2(pos.x, pos.z)) - dV.dot(new Vector2(this.x, this.z))) >= -maxDiff;
+ }
+
+ private boolean canInteractEntity(Vector3 pos, double maxDistanceSquared) {
+ if (this.distanceSquared(pos) > maxDistanceSquared) {
+ return false;
+ }
+
+ Vector2 dV = this.getDirectionPlane();
+ return (dV.dot(new Vector2(pos.x, pos.z)) - dV.dot(new Vector2(this.x, this.z))) >= -0.87;
+ }
+
+ protected void processPreLogin() {
+ this.loginVerified = true;
+ final Player playerInstance = this;
+
+ this.preLoginEventTask = new AsyncTask() {
+ private PlayerAsyncPreLoginEvent event;
+
+ @Override
+ public void onRun() {
+ this.event = new PlayerAsyncPreLoginEvent(username, uuid, loginChainData, skin, playerInstance.getAddress(), playerInstance.getPort());
+ server.getPluginManager().callEvent(this.event);
+ }
+
+ @Override
+ public void onCompletion(Server server) {
+ if (!playerInstance.connected) {
+ return;
+ }
+
+ if (this.event.getLoginResult() == LoginResult.KICK) {
+ playerInstance.close(this.event.getKickMessage(), this.event.getKickMessage());
+ } else if (playerInstance.shouldLogin) {
+ try {
+ playerInstance.setSkin(this.event.getSkin());
+ playerInstance.completeLoginSequence();
+ for (Consumer action : this.event.getScheduledActions()) {
+ action.accept(server);
+ }
+ } catch (Exception ex) {
+ server.getLogger().logException(ex);
+ playerInstance.close("", "Internal Server Error");
+ }
+ }
+ }
+ };
+
+ this.server.getScheduler().scheduleAsyncTask(this.preLoginEventTask);
+
+ try {
+ this.processLogin();
+ } catch (Exception ex) {
+ this.server.getLogger().logException(ex);
+ this.close("", "Internal Server Error");
+ }
}
protected void processLogin() {
- if (!this.server.isWhitelisted((this.getName()).toLowerCase())) {
+ String lowerName = this.iusername;
+ if (!this.server.isWhitelisted(lowerName)) {
this.kick(PlayerKickEvent.Reason.NOT_WHITELISTED, "Server is white-listed");
-
return;
} else if (this.isBanned()) {
- String reason = this.server.getNameBans().getEntires().get(this.getName().toLowerCase()).getReason();
- this.kick(PlayerKickEvent.Reason.NAME_BANNED, !reason.isEmpty() ? "You are banned. Reason: " + reason : "You are banned");
+ String reason = this.server.getNameBans().getEntires().get(lowerName).getReason();
+ this.kick(PlayerKickEvent.Reason.NAME_BANNED, "You are banned!" + (reason.isEmpty() ? "" : (" Reason: " + reason)));
return;
} else if (this.server.getIPBans().isBanned(this.getAddress())) {
- String reason = this.server.getIPBans().getEntires().get(this.getAddress()).getReason();
- this.kick(PlayerKickEvent.Reason.IP_BANNED, !reason.isEmpty() ? "You are banned. Reason: " + reason : "You are banned");
+ this.kick(PlayerKickEvent.Reason.IP_BANNED, "Your IP is banned!");
return;
}
- if (this.hasPermission(Server.BROADCAST_CHANNEL_USERS)) {
- this.server.getPluginManager().subscribeToPermission(Server.BROADCAST_CHANNEL_USERS, this);
- }
- if (this.hasPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE)) {
- this.server.getPluginManager().subscribeToPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE, this);
- }
-
- Player oldPlayer = null;
- for (Player p : new ArrayList<>(this.server.getOnlinePlayers().values())) {
- if (p != this && p.getName() != null && p.getName().equalsIgnoreCase(this.getName()) ||
- this.getUniqueId().equals(p.getUniqueId())) {
- oldPlayer = p;
- break;
+ for (Player p : new ArrayList<>(this.server.playerList.values())) {
+ if (p != this && p.username != null) {
+ if (p.username.equalsIgnoreCase(this.username) || this.getUniqueId().equals(p.getUniqueId())) {
+ p.close("", "disconnectionScreen.loggedinOtherLocation");
+ break;
+ }
}
}
- CompoundTag nbt;
- if (oldPlayer != null) {
- oldPlayer.saveNBT();
- nbt = oldPlayer.namedTag;
- oldPlayer.close("", "disconnectionScreen.loggedinOtherLocation");
- } else {
- File legacyDataFile = new File(server.getDataPath() + "players/" + this.username.toLowerCase() + ".dat");
- File dataFile = new File(server.getDataPath() + "players/" + this.uuid.toString() + ".dat");
- if (legacyDataFile.exists() && !dataFile.exists()) {
- nbt = this.server.getOfflinePlayerData(this.username, false);
- if (!legacyDataFile.delete()) {
- log.warn("Could not delete legacy player data for {}", this.username);
- }
- } else {
- nbt = this.server.getOfflinePlayerData(this.uuid, true);
+ CompoundTag nbt;
+ File legacyDataFile = new File(server.getDataPath() + "players/" + lowerName + ".dat");
+ File dataFile = new File(server.getDataPath() + "players/" + this.uuid.toString() + ".dat");
+
+ boolean dataFound = dataFile.exists();
+ if (!dataFound && legacyDataFile.exists()) {
+ nbt = this.server.getOfflinePlayerData(lowerName, false);
+ if (!legacyDataFile.delete()) {
+ this.server.getLogger().warning("Could not delete legacy player data for " + this.username);
}
+ } else {
+ nbt = this.server.getOfflinePlayerData(this.uuid, !dataFound);
}
if (nbt == null) {
@@ -1918,47 +2549,60 @@ protected void processLogin() {
return;
}
- if (loginChainData.isXboxAuthed() && server.getPropertyBoolean("xbox-auth") || !server.getPropertyBoolean("xbox-auth")) {
+ if (loginChainData.isXboxAuthed() || !server.xboxAuth) {
server.updateName(this.uuid, this.username);
}
this.playedBefore = (nbt.getLong("lastPlayed") - nbt.getLong("firstPlayed")) > 1;
-
nbt.putString("NameTag", this.username);
- int exp = nbt.getInt("EXP");
- int expLevel = nbt.getInt("expLevel");
- this.setExperience(exp, expLevel);
+ this.setExperience(nbt.getInt("EXP"), nbt.getInt("expLevel"));
- this.gamemode = nbt.getInt("playerGameType") & 0x03;
if (this.server.getForceGamemode()) {
this.gamemode = this.server.getGamemode();
nbt.putInt("playerGameType", this.gamemode);
+ } else {
+ this.gamemode = nbt.getInt("playerGameType") & 0x03;
}
this.adventureSettings = new AdventureSettings(this)
.set(Type.WORLD_IMMUTABLE, isAdventure() || isSpectator())
.set(Type.MINE, !isAdventure() && !isSpectator())
.set(Type.BUILD, !isAdventure() && !isSpectator())
- .set(Type.NO_PVM, this.isSpectator())
+ .set(Type.NO_PVM, isSpectator())
.set(Type.AUTO_JUMP, true)
.set(Type.ALLOW_FLIGHT, isCreative() || isSpectator())
.set(Type.NO_CLIP, isSpectator())
.set(Type.FLYING, isSpectator());
Level level;
- if ((level = this.server.getLevelByName(nbt.getString("Level"))) == null) {
+ if ((level = this.server.getLevelByName(nbt.getString("Level"))) == null || nbt.getShort("Health") < 1) {
this.setLevel(this.server.getDefaultLevel());
nbt.putString("Level", this.level.getName());
+ Position sp = this.level.getSpawnLocation();
nbt.getList("Pos", DoubleTag.class)
- .add(new DoubleTag("0", this.level.getSpawnLocation().x))
- .add(new DoubleTag("1", this.level.getSpawnLocation().y))
- .add(new DoubleTag("2", this.level.getSpawnLocation().z));
+ .add(new DoubleTag("0", sp.x))
+ .add(new DoubleTag("1", sp.y))
+ .add(new DoubleTag("2", sp.z));
} else {
this.setLevel(level);
}
+ if (nbt.contains("SpawnLevel")) {
+ Level spawnLevel = server.getLevelByName(nbt.getString("SpawnLevel"));
+ if (spawnLevel != null) {
+ this.spawnPosition = new Position(
+ nbt.getInt("SpawnX"),
+ nbt.getInt("SpawnY"),
+ nbt.getInt("SpawnZ"),
+ spawnLevel
+ );
+ }
+ }
+
+ this.timeSinceRest = nbt.getInt("TimeSinceRest");
+
for (Tag achievement : nbt.getCompound("Achievements").getAllTags()) {
if (!(achievement instanceof ByteTag)) {
continue;
@@ -1980,21 +2624,20 @@ protected void processLogin() {
}
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);
+ super.init(this.level.getChunk(NukkitMath.floorDouble(posList.get(0).data) >> 4, NukkitMath.floorDouble(posList.get(2).data) >> 4, true), nbt);
if (!this.namedTag.contains("foodLevel")) {
this.namedTag.putInt("foodLevel", 20);
}
- int foodLevel = this.namedTag.getInt("foodLevel");
+
if (!this.namedTag.contains("foodSaturationLevel")) {
this.namedTag.putFloat("foodSaturationLevel", 20);
}
- float foodSaturationLevel = this.namedTag.getFloat("foodSaturationLevel");
- this.foodData = new PlayerFood(this, foodLevel, foodSaturationLevel);
+
+ this.foodData = new PlayerFood(this, this.namedTag.getInt("foodLevel"), this.namedTag.getFloat("foodSaturationLevel"));
if (this.isSpectator()) {
this.keepMovement = true;
@@ -2003,11 +2646,6 @@ protected void processLogin() {
this.forceMovement = this.teleportPosition = this.getPosition();
- if (!this.namedTag.contains("TimeSinceRest")) {
- this.namedTag.putInt("TimeSinceRest", 0);
- }
- this.timeSinceRest = this.namedTag.getInt("TimeSinceRest");
-
ResourcePacksInfoPacket infoPacket = new ResourcePacksInfoPacket();
infoPacket.resourcePackEntries = this.server.getResourcePackManager().getResourceStack();
infoPacket.mustAccept = this.server.getForceResources();
@@ -2015,6 +2653,11 @@ protected void processLogin() {
}
protected void completeLoginSequence() {
+ if (this.loggedIn) {
+ this.server.getLogger().warning("Tried to call completeLoginSequence but player is already logged in: " + this.username);
+ return;
+ }
+
PlayerLoginEvent ev;
this.server.getPluginManager().callEvent(ev = new PlayerLoginEvent(this, "Plugin reason"));
if (ev.isCancelled()) {
@@ -2022,15 +2665,10 @@ protected void completeLoginSequence() {
return;
}
- Level level = this.server.getLevelByName(this.namedTag.getString("SpawnLevel"));
- if(level != null){
- this.spawnPosition = new Position(this.namedTag.getInt("SpawnX"), this.namedTag.getInt("SpawnY"), this.namedTag.getInt("SpawnZ"), level);
- }else{
- this.spawnPosition = this.level.getSafeSpawn();
+ if (this.isClosed() || !this.isConnected()) {
+ return; // Player was probably disconnected by a plugin
}
- spawnPosition = this.getSpawn();
-
StartGamePacket startGamePacket = new StartGamePacket();
startGamePacket.entityUniqueId = this.id;
startGamePacket.entityRuntimeId = this.id;
@@ -2040,48 +2678,36 @@ protected void completeLoginSequence() {
startGamePacket.z = (float) this.z;
startGamePacket.yaw = (float) this.yaw;
startGamePacket.pitch = (float) this.pitch;
- startGamePacket.seed = -1;
- startGamePacket.dimension = /*(byte) (this.level.getDimension() & 0xff)*/0;
+ startGamePacket.dimension = (byte) (this.level.getDimension() & 0xff);
startGamePacket.worldGamemode = getClientFriendlyGamemode(this.gamemode);
startGamePacket.difficulty = this.server.getDifficulty();
- startGamePacket.spawnX = spawnPosition.getFloorX();
- startGamePacket.spawnY = spawnPosition.getFloorY();
- startGamePacket.spawnZ = spawnPosition.getFloorZ();
- startGamePacket.hasAchievementsDisabled = true;
- startGamePacket.dayCycleStopTime = -1;
- startGamePacket.rainLevel = 0;
- startGamePacket.lightningLevel = 0;
- startGamePacket.commandsEnabled = this.isEnableClientCommand();
- startGamePacket.gameRules = getLevel().getGameRules();
- startGamePacket.levelId = "";
+ if (this.level.getProvider() == null || this.level.getProvider().getSpawn() == null) {
+ startGamePacket.spawnX = (int) this.x;
+ startGamePacket.spawnY = (int) this.y;
+ startGamePacket.spawnZ = (int) this.z;
+ } else {
+ Vector3 spawn = this.level.getProvider().getSpawn();
+ startGamePacket.spawnX = (int) spawn.x;
+ startGamePacket.spawnY = (int) spawn.y;
+ startGamePacket.spawnZ = (int) spawn.z;
+ }
+ startGamePacket.commandsEnabled = this.enableClientCommand;
+ startGamePacket.gameRules = this.getLevel().getGameRules();
startGamePacket.worldName = this.getServer().getNetwork().getName();
- startGamePacket.generator = 1; //0 old, 1 infinite, 2 flat
- startGamePacket.isMovementServerAuthoritative = true;
- this.dataPacket(startGamePacket);
-
- this.dataPacket(new BiomeDefinitionListPacket());
- this.dataPacket(new AvailableEntityIdentifiersPacket());
- this.inventory.sendCreativeContents();
- this.getAdventureSettings().update();
-
- this.sendAttributes();
-
- this.sendPotionEffects(this);
-
- if (this.isSpectator()) {
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_SILENT, true);
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, false);
+ if (this.getLevel().isRaining()) {
+ startGamePacket.rainLevel = this.getLevel().getRainTime();
+ if (this.getLevel().isThundering()) {
+ startGamePacket.lightningLevel = this.getLevel().getThunderTime();
+ }
}
- this.sendData(this);
- this.loggedIn = true;
+ if (!CustomBlockManager.get().getBlockDefinitions().isEmpty()) {
+ startGamePacket.experiments.add(new ExperimentData("data_driven_items", true));
+ }
- this.level.sendTime(this);
+ this.forceDataPacket(startGamePacket, null);
- this.sendAttributes();
- this.setNameTagVisible(true);
- this.setNameTagAlwaysVisible(true);
- this.setCanClimb(true);
+ this.loggedIn = true;
this.server.getLogger().info(this.getServer().getLanguage().translateString("nukkit.player.logIn",
TextFormat.AQUA + this.username + TextFormat.WHITE,
@@ -2089,116 +2715,191 @@ protected void completeLoginSequence() {
String.valueOf(this.getPort()),
String.valueOf(this.id),
this.level.getName(),
- String.valueOf(NukkitMath.round(this.x, 4)),
- String.valueOf(NukkitMath.round(this.y, 4)),
- String.valueOf(NukkitMath.round(this.z, 4))));
+ String.valueOf(this.getFloorX()),
+ String.valueOf(this.getFloorY()),
+ String.valueOf(this.getFloorZ())));
+
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_CAN_CLIMB, true, false);
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_CAN_SHOW_NAMETAG, true, false);
+ this.setDataProperty(new ByteEntityData(DATA_ALWAYS_SHOW_NAMETAG, 1), false);
+
+ if (this.isSpectator()) {
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_SILENT, true, false);
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, false, false);
+ }
+
+ this.dataPacket(new BiomeDefinitionListPacket());
+ this.dataPacket(new AvailableEntityIdentifiersPacket());
+
+ this.sendSpawnPos((int) this.x, (int) this.y, (int) this.z, this.level.getDimension());
+ this.getLevel().sendTime(this);
+
+ SetDifficultyPacket difficultyPacket = new SetDifficultyPacket();
+ difficultyPacket.difficulty = this.server.getDifficulty();
+ this.dataPacket(difficultyPacket);
+
+ SetCommandsEnabledPacket commandsPacket = new SetCommandsEnabledPacket();
+ commandsPacket.enabled = this.isEnableClientCommand();
+ this.dataPacket(commandsPacket);
+
+ this.adventureSettings.update();
+
+ GameRulesChangedPacket gameRulesPK = new GameRulesChangedPacket();
+ gameRulesPK.gameRulesMap = level.getGameRules().getGameRules();
+ this.dataPacket(gameRulesPK);
+
+ this.server.sendFullPlayerListData(this);
+ this.sendAttributes();
+
+ this.inventory.sendCreativeContents();
+ this.sendAllInventories();
+ this.inventory.sendHeldItem(this);
+ this.server.sendRecipeList(this);
+
+ if (this.isEnableClientCommand()) {
+ this.sendCommandData();
+ }
+
+ this.sendPotionEffects(this);
+ this.sendData(this);
if (this.isOp() || this.hasPermission("nukkit.textcolor")) {
this.setRemoveFormat(false);
}
- this.server.addOnlinePlayer(this);
this.server.onPlayerCompleteLoginSequence(this);
}
+ /**
+ * Handling received data packets
+ * @param packet packet
+ */
public void handleDataPacket(DataPacket packet) {
if (!connected) {
return;
}
byte pid = packet.pid();
- if (!loginVerified && pid != ProtocolInfo.LOGIN_PACKET && pid != ProtocolInfo.BATCH_PACKET && pid != ProtocolInfo.REQUEST_NETWORK_SETTINGS_PACKET && pid != ProtocolInfo.CLIENT_TO_SERVER_HANDSHAKE_PACKET) {
+ if (!loginVerified && pid != ProtocolInfo.LOGIN_PACKET && pid != ProtocolInfo.REQUEST_NETWORK_SETTINGS_PACKET && pid != ProtocolInfo.BATCH_PACKET && pid != ProtocolInfo.CLIENT_TO_SERVER_HANDSHAKE_PACKET) {
server.getLogger().warning("Ignoring " + packet.getClass().getSimpleName() + " from " + getAddress() + " due to player not verified yet");
- if (unverifiedPackets++ > 100) {
- this.close("", "Too many failed login attempts");
- }
return;
}
- try (Timing ignored = Timings.getReceiveDataPacketTiming(packet)) {
- DataPacketReceiveEvent ev = new DataPacketReceiveEvent(this, packet);
- this.server.getPluginManager().callEvent(ev);
- if (ev.isCancelled()) {
- return;
- }
+ if (!loggedIn && !PRE_LOGIN_PACKETS.contains(pid)) {
+ server.getLogger().warning("Ignoring " + packet.getClass().getSimpleName() + " from " + username + " due to player not logged in yet");
+ return;
+ }
- if (log.isTraceEnabled() && !server.isIgnoredPacket(packet.getClass())) {
- log.trace("Inbound {}: {}", this.getName(), packet);
- }
+ DataPacketReceiveEvent ev = new DataPacketReceiveEvent(this, packet);
+ this.server.getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return;
+ }
- BlockFace face;
+ if (Nukkit.DEBUG > 2 /*&& !server.isIgnoredPacket(packet.getClass())*/) {
+ log.trace("Inbound {}: {}", this.getName(), packet);
+ }
- packetswitch:
- switch (pid) {
- case ProtocolInfo.REQUEST_NETWORK_SETTINGS_PACKET:
- if (this.loginPacketReceived) {
- this.getServer().getLogger().debug(username + ": got a RequestNetworkSettingsPacket but player is already logged in");
- return;
- }
+ switch (pid) {
+ case ProtocolInfo.REQUEST_NETWORK_SETTINGS_PACKET:
+ if (this.loginPacketReceived) {
+ this.getServer().getLogger().debug(username + ": got a RequestNetworkSettingsPacket but player is already logged in");
+ return;
+ } else if (this.getNetworkSession().getCompression() != CompressionProvider.NONE) {
+ this.getServer().getLogger().debug(username + ": got a RequestNetworkSettingsPacket but network settings are already updated");
+ return;
+ }
- int protocolVersion = ((RequestNetworkSettingsPacket) packet).protocolVersion;
+ RequestNetworkSettingsPacket networkSettingsRequest = (RequestNetworkSettingsPacket) packet;
- if (!ProtocolInfo.SUPPORTED_PROTOCOLS.contains(protocolVersion)) {
- String message;
- if (protocolVersion < ProtocolInfo.CURRENT_PROTOCOL) {
- message = "disconnectionScreen.outdatedClient";
- } else {
- message = "disconnectionScreen.outdatedServer";
- }
- this.close("", message, true);
- break;
+ if (!ProtocolInfo.SUPPORTED_PROTOCOLS.contains(networkSettingsRequest.protocolVersion)) {
+ String message;
+ if (networkSettingsRequest.protocolVersion < ProtocolInfo.CURRENT_PROTOCOL) {
+ message = "disconnectionScreen.outdatedClient";
+ } else {
+ message = "disconnectionScreen.outdatedServer";
}
+ this.close("", message, true);
+ this.server.getLogger().debug(getAddress() + " disconnected with unsupported protocol " + networkSettingsRequest.protocolVersion);
+ return;
+ }
- NetworkSettingsPacket settingsPacket = new NetworkSettingsPacket();
- settingsPacket.compressionAlgorithm = PacketCompressionAlgorithm.ZLIB;
- settingsPacket.compressionThreshold = 1; // compress everything
- this.forceDataPacket(settingsPacket, () -> {
- this.networkSession.setCompression(CompressionProvider.ZLIB);
- });
- break;
- case ProtocolInfo.LOGIN_PACKET:
- if (this.loginPacketReceived) {
- this.close("", "Invalid login packet");
- return;
- }
+ NetworkSettingsPacket settingsPacket = new NetworkSettingsPacket();
+ settingsPacket.compressionAlgorithm = server.useSnappy ? PacketCompressionAlgorithm.SNAPPY : PacketCompressionAlgorithm.ZLIB;
+ settingsPacket.compressionThreshold = server.networkCompressionThreshold;
+ this.forceDataPacket(settingsPacket, () -> this.networkSession.setCompression(server.useSnappy ? CompressionProvider.SNAPPY : CompressionProvider.ZLIB_RAW));
+ return;
+ case ProtocolInfo.LOGIN_PACKET:
+ if (this.loginPacketReceived) {
+ this.close("", "Invalid login packet");
+ return;
+ }
- this.loginPacketReceived = true;
+ this.loginPacketReceived = true;
- LoginPacket loginPacket = (LoginPacket) packet;
- this.username = TextFormat.clean(loginPacket.username);
- this.displayName = this.username;
- this.iusername = this.username.toLowerCase();
+ LoginPacket loginPacket = (LoginPacket) packet;
+ this.unverifiedUsername = TextFormat.clean(loginPacket.username);
- this.setDataProperty(new StringEntityData(DATA_NAMETAG, this.username), false);
+ if (!ProtocolInfo.SUPPORTED_PROTOCOLS.contains(loginPacket.getProtocol())) {
+ String message;
+ if (loginPacket.getProtocol() < ProtocolInfo.CURRENT_PROTOCOL) {
+ message = "disconnectionScreen.outdatedClient";
+ } else {
+ message = "disconnectionScreen.outdatedServer";
+ }
+ this.close("", message, true);
+ this.server.getLogger().debug(getAddress() + " disconnected with unsupported protocol " + loginPacket.getProtocol());
+ return;
+ }
- this.loginChainData = ClientChainData.read(loginPacket);
+ if (loginPacket.skin == null) {
+ this.close("", "disconnectionScreen.invalidSkin");
+ return;
+ }
- if (!loginChainData.isXboxAuthed() && server.getPropertyBoolean("xbox-auth")) {
- this.close("", "disconnectionScreen.notAuthenticated");
- break;
- }
+ if (this.server.getOnlinePlayersCount() >= this.server.getMaxPlayers() && this.kick(PlayerKickEvent.Reason.SERVER_FULL, "disconnectionScreen.serverFull", false)) {
+ return;
+ }
- if (this.server.getOnlinePlayers().size() >= this.server.getMaxPlayers() && this.kick(PlayerKickEvent.Reason.SERVER_FULL, "disconnectionScreen.serverFull", false)) {
- break;
- }
+ try {
+ // TODO: Why do we read this separately?
+ this.loginChainData = ClientChainData.read(loginPacket);
+ } catch (ClientChainData.TooBigSkinException ex) {
+ this.close("", "disconnectionScreen.invalidSkin");
+ return;
+ }
- this.randomClientId = loginPacket.clientId;
+ server.getLogger().debug("Name: " + this.unverifiedUsername + " Protocol: " + loginPacket.getProtocol() + " Version: " + loginChainData.getGameVersion());
- this.uuid = loginPacket.clientUUID;
- this.rawUUID = Binary.writeUUID(this.uuid);
+ if (!loginChainData.isXboxAuthed() && server.xboxAuth) {
+ this.close("", "disconnectionScreen.notAuthenticated");
+ return;
+ }
- boolean valid = true;
- int len = loginPacket.username.length();
- if (len > 16 || len < 3 || loginPacket.username.trim().isEmpty()) {
- valid = false;
- }
+ // Do not set username before the user is authenticated
+ this.username = this.unverifiedUsername;
+ this.unverifiedUsername = null;
+ this.displayName = this.username;
+ this.iusername = this.username.toLowerCase();
+ this.setDataProperty(new StringEntityData(DATA_NAMETAG, this.username), false);
+
+ this.randomClientId = loginPacket.clientId;
+ this.uuid = loginPacket.clientUUID;
+ this.rawUUID = Binary.writeUUID(this.uuid);
+
+ boolean valid = true;
+ int len = loginPacket.username.length();
+ if (len > 16 || len < 3 || loginPacket.username.trim().isEmpty()) {
+ valid = false;
+ }
- for (int i = 0; i < len && valid; i++) {
+ if (valid) {
+ for (int i = 0; i < len; i++) {
char c = loginPacket.username.charAt(i);
if ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
- c == '_' || c == ' '
+ c == '_' || (c == ' ' && i != 0 && i != len - 1)
) {
continue;
}
@@ -2206,922 +2907,1192 @@ public void handleDataPacket(DataPacket packet) {
valid = false;
break;
}
+ }
- if (!valid || Objects.equals(this.iusername, "rcon") || Objects.equals(this.iusername, "console")) {
- this.close("", "disconnectionScreen.invalidName");
+ if (!valid || Objects.equals(this.iusername, "rcon") || Objects.equals(this.iusername, "console")) {
+ this.close("", "disconnectionScreen.invalidName");
+ return;
+ }
- break;
- }
+ Skin skin = loginPacket.skin;
+ if (!skin.isValid()) {
+ this.close("", "disconnectionScreen.invalidSkin");
+ return;
+ }
+ this.setSkin(skin);
- if (!loginPacket.skin.isValid()) {
- this.close("", "disconnectionScreen.invalidSkin");
- break;
- } else {
- this.setSkin(loginPacket.skin);
- }
+ PlayerPreLoginEvent playerPreLoginEvent;
+ this.server.getPluginManager().callEvent(playerPreLoginEvent = new PlayerPreLoginEvent(this, "Plugin reason"));
+ if (playerPreLoginEvent.isCancelled()) {
+ this.close("", playerPreLoginEvent.getKickMessage());
+ return;
+ }
- PlayerPreLoginEvent playerPreLoginEvent;
- this.server.getPluginManager().callEvent(playerPreLoginEvent = new PlayerPreLoginEvent(this, "Plugin reason"));
- if (playerPreLoginEvent.isCancelled()) {
- this.close("", playerPreLoginEvent.getKickMessage());
+ if (server.encryptionEnabled) {
+ this.getServer().getScheduler().scheduleAsyncTask(new PrepareEncryptionTask(this) {
- break;
- }
+ @Override
+ public void onCompletion(Server server) {
+ if (!Player.this.connected) {
+ return;
+ }
- if (server.encryptionEnabled) {
- this.getServer().getScheduler().scheduleAsyncTask(new PrepareEncryptionTask(this) {
-
- @Override
- public void onCompletion(Server server) {
- if (!Player.this.connected) {
- return;
- }
-
- if (this.getHandshakeJwt() == null || this.getEncryptionKey() == null || this.getEncryptionCipher() == null || this.getDecryptionCipher() == null) {
- Player.this.close("Failed to enable encryption");
- return;
- }
-
- ServerToClientHandshakePacket handshakePacket = new ServerToClientHandshakePacket();
- handshakePacket.jwt = this.getHandshakeJwt();
- Player.this.forceDataPacket(handshakePacket, () -> {
- Player.this.awaitingEncryptionHandshake = true;
- Player.this.networkSession.setEncryption(this.getEncryptionKey(), this.getEncryptionCipher(), this.getDecryptionCipher());
- });
+ if (this.getHandshakeJwt() == null || this.getEncryptionKey() == null || this.getEncryptionCipher() == null || this.getDecryptionCipher() == null) {
+ Player.this.close("Failed to enable encryption");
+ return;
}
- });
- } else {
- this.processPreLogin();
- }
- break;
- case ProtocolInfo.CLIENT_TO_SERVER_HANDSHAKE_PACKET:
- if (!this.awaitingEncryptionHandshake) {
- this.close("Invalid encryption handshake");
- return;
- }
- this.awaitingEncryptionHandshake = false;
+ ServerToClientHandshakePacket handshakePacket = new ServerToClientHandshakePacket();
+ handshakePacket.jwt = this.getHandshakeJwt();
+ Player.this.forceDataPacket(handshakePacket, () -> {
+ Player.this.awaitingEncryptionHandshake = true;
+ Player.this.networkSession.setEncryption(this.getEncryptionKey(), this.getEncryptionCipher(), this.getDecryptionCipher());
+ });
+ }
+ });
+ } else {
this.processPreLogin();
+ }
+ return;
+ case ProtocolInfo.CLIENT_TO_SERVER_HANDSHAKE_PACKET:
+ if (!this.awaitingEncryptionHandshake) {
+ this.close("Invalid encryption handshake");
return;
- case ProtocolInfo.RESOURCE_PACK_CLIENT_RESPONSE_PACKET:
- ResourcePackClientResponsePacket responsePacket = (ResourcePackClientResponsePacket) packet;
- switch (responsePacket.responseStatus) {
- case ResourcePackClientResponsePacket.STATUS_REFUSED:
- this.close("", "disconnectionScreen.noReason");
- break;
- case ResourcePackClientResponsePacket.STATUS_SEND_PACKS:
- for (ResourcePackClientResponsePacket.Entry entry : responsePacket.packEntries) {
- ResourcePack resourcePack = this.server.getResourcePackManager().getPackById(entry.uuid);
- if (resourcePack == null) {
- this.close("", "disconnectionScreen.resourcePack");
- break;
- }
-
- ResourcePackDataInfoPacket dataInfoPacket = new ResourcePackDataInfoPacket();
- dataInfoPacket.packId = resourcePack.getPackId();
- dataInfoPacket.maxChunkSize = RESOURCE_PACK_CHUNK_SIZE;
- dataInfoPacket.chunkCount = MathHelper.ceil(resourcePack.getPackSize() / (float) RESOURCE_PACK_CHUNK_SIZE);
- dataInfoPacket.compressedPackSize = resourcePack.getPackSize();
- dataInfoPacket.sha256 = resourcePack.getSha256();
- this.dataPacket(dataInfoPacket);
- }
- break;
- case ResourcePackClientResponsePacket.STATUS_HAVE_ALL_PACKS:
- ResourcePackStackPacket stackPacket = new ResourcePackStackPacket();
- stackPacket.mustAccept = this.server.getForceResources();
- stackPacket.resourcePackStack = this.server.getResourcePackManager().getResourceStack();
- this.dataPacket(stackPacket);
- break;
- case ResourcePackClientResponsePacket.STATUS_COMPLETED:
- this.shouldLogin = true;
+ }
- if (this.preLoginEventTask.isFinished()) {
- this.preLoginEventTask.onCompletion(server);
+ this.awaitingEncryptionHandshake = false;
+ this.processPreLogin();
+ return;
+ case ProtocolInfo.RESOURCE_PACK_CLIENT_RESPONSE_PACKET:
+ if (this.spawned) {
+ this.getServer().getLogger().debug(username + ": ResourcePackClientResponsePacket after player spawned");
+ return;
+ }
+ ResourcePackClientResponsePacket responsePacket = (ResourcePackClientResponsePacket) packet;
+ switch (responsePacket.responseStatus) {
+ case ResourcePackClientResponsePacket.STATUS_REFUSED:
+ this.close("", "disconnectionScreen.noReason");
+ return;
+ case ResourcePackClientResponsePacket.STATUS_SEND_PACKS:
+ for (ResourcePackClientResponsePacket.Entry entry : responsePacket.packEntries) {
+ ResourcePack resourcePack = this.server.getResourcePackManager().getPackById(entry.uuid);
+ if (resourcePack == null) {
+ this.close("", "disconnectionScreen.resourcePack");
+ return;
}
- break;
- }
- break;
- case ProtocolInfo.RESOURCE_PACK_CHUNK_REQUEST_PACKET:
- ResourcePackChunkRequestPacket requestPacket = (ResourcePackChunkRequestPacket) packet;
- ResourcePack resourcePack = this.server.getResourcePackManager().getPackById(requestPacket.packId);
- if (resourcePack == null) {
- this.close("", "disconnectionScreen.resourcePack");
- break;
- }
-
- ResourcePackChunkDataPacket dataPacket = new ResourcePackChunkDataPacket();
- dataPacket.packId = resourcePack.getPackId();
- dataPacket.chunkIndex = requestPacket.chunkIndex;
- dataPacket.data = resourcePack.getPackChunk(RESOURCE_PACK_CHUNK_SIZE * requestPacket.chunkIndex, RESOURCE_PACK_CHUNK_SIZE);
- dataPacket.progress = (long) RESOURCE_PACK_CHUNK_SIZE * requestPacket.chunkIndex;
- this.dataPacket(dataPacket);
- break;
- case ProtocolInfo.SET_LOCAL_PLAYER_AS_INITIALIZED_PACKET:
- if (this.locallyInitialized) {
- break;
- }
- this.locallyInitialized = true;
- PlayerLocallyInitializedEvent locallyInitializedEvent = new PlayerLocallyInitializedEvent(this);
- this.server.getPluginManager().callEvent(locallyInitializedEvent);
- break;
- case ProtocolInfo.PLAYER_SKIN_PACKET:
- PlayerSkinPacket skinPacket = (PlayerSkinPacket) packet;
- Skin skin = skinPacket.skin;
-
- if (!skin.isValid()) {
- this.getServer().getLogger().debug(username + ": PlayerSkinPacket with invalid skin");
- break;
- }
- PlayerChangeSkinEvent playerChangeSkinEvent = new PlayerChangeSkinEvent(this, skin);
- playerChangeSkinEvent.setCancelled(TimeUnit.SECONDS.toMillis(this.server.getPlayerSkinChangeCooldown()) > System.currentTimeMillis() - this.lastSkinChange);
- this.server.getPluginManager().callEvent(playerChangeSkinEvent);
- if (!playerChangeSkinEvent.isCancelled()) {
- this.lastSkinChange = System.currentTimeMillis();
- this.setSkin(skin);
- }
-
- break;
- case ProtocolInfo.PACKET_VIOLATION_WARNING_PACKET:
- log.warn("Violation warning from {}: {}", this.getName(), packet.toString());
- break;
- case ProtocolInfo.EMOTE_PACKET:
- if (!this.spawned) {
+ ResourcePackDataInfoPacket dataInfoPacket = new ResourcePackDataInfoPacket();
+ dataInfoPacket.packId = resourcePack.getPackId();
+ dataInfoPacket.maxChunkSize = RESOURCE_PACK_CHUNK_SIZE;
+ dataInfoPacket.chunkCount = MathHelper.ceil(resourcePack.getPackSize() / (float) RESOURCE_PACK_CHUNK_SIZE);
+ dataInfoPacket.compressedPackSize = resourcePack.getPackSize();
+ dataInfoPacket.sha256 = resourcePack.getSha256();
+ this.dataPacket(dataInfoPacket);
+ }
return;
- }
- EmotePacket emotePacket = (EmotePacket) packet;
- if (emotePacket.runtimeId != this.id) {
- server.getLogger().warning(this.username + " sent EmotePacket with invalid entity id: " + emotePacket.runtimeId + " != " + this.id);
+ case ResourcePackClientResponsePacket.STATUS_HAVE_ALL_PACKS:
+ ResourcePackStackPacket stackPacket = new ResourcePackStackPacket();
+ stackPacket.mustAccept = this.server.getForceResources() && !this.server.forceResourcesAllowOwnPacks; // Option not to disable client's own packs
+ stackPacket.resourcePackStack = this.server.getResourcePackManager().getResourceStack();
+ if (!CustomBlockManager.get().getBlockDefinitions().isEmpty()) {
+ stackPacket.experiments.add(new ExperimentData("data_driven_items", true));
+ }
+ this.dataPacket(stackPacket);
return;
- }
- for (Player viewer : this.getViewers().values()) {
- viewer.dataPacket(emotePacket);
- }
- return;
- case ProtocolInfo.PLAYER_INPUT_PACKET:
- if (!this.isAlive() || !this.spawned) {
- break;
- }
- PlayerInputPacket ipk = (PlayerInputPacket) packet;
- if (riding instanceof EntityMinecartAbstract) {
- ((EntityMinecartAbstract) riding).setCurrentSpeed(ipk.motionY);
- }
- break;
- case ProtocolInfo.MOVE_PLAYER_PACKET:
- if (this.teleportPosition != null) {
- break;
- }
+ case ResourcePackClientResponsePacket.STATUS_COMPLETED:
+ this.shouldLogin = true;
- MovePlayerPacket movePlayerPacket = (MovePlayerPacket) packet;
- Vector3 newPos = new Vector3(movePlayerPacket.x, movePlayerPacket.y - this.getEyeHeight(), movePlayerPacket.z);
-
- double dis = newPos.distanceSquared(this);
- if (dis == 0 && movePlayerPacket.yaw % 360 == this.yaw && movePlayerPacket.pitch % 360 == this.pitch) {
- break;
- }
+ if (this.preLoginEventTask.isFinished()) {
+ this.preLoginEventTask.onCompletion(server);
+ }
+ return;
+ }
+ return;
+ case ProtocolInfo.RESOURCE_PACK_CHUNK_REQUEST_PACKET:
+ ResourcePackChunkRequestPacket requestPacket = (ResourcePackChunkRequestPacket) packet;
+ ResourcePack resourcePack = this.server.getResourcePackManager().getPackById(requestPacket.packId);
+ if (resourcePack == null) {
+ this.close("", "disconnectionScreen.resourcePack");
+ return;
+ }
- if (dis > 100) {
- this.sendPosition(this, movePlayerPacket.yaw, movePlayerPacket.pitch, MovePlayerPacket.MODE_RESET);
- break;
- }
+ ResourcePackChunkDataPacket dataPacket = new ResourcePackChunkDataPacket();
+ dataPacket.packId = resourcePack.getPackId();
+ dataPacket.chunkIndex = requestPacket.chunkIndex;
+ dataPacket.data = resourcePack.getPackChunk(RESOURCE_PACK_CHUNK_SIZE * requestPacket.chunkIndex, RESOURCE_PACK_CHUNK_SIZE);
+ dataPacket.progress = (long) RESOURCE_PACK_CHUNK_SIZE * requestPacket.chunkIndex;
+ this.dataPacket(dataPacket);
+ return;
+ case ProtocolInfo.PLAYER_SKIN_PACKET:
+ PlayerSkinPacket skinPacket = (PlayerSkinPacket) packet;
+ skin = skinPacket.skin;
- boolean revert = false;
- if (!this.isAlive() || !this.spawned) {
- revert = true;
- this.forceMovement = new Vector3(this.x, this.y, this.z);
- }
+ if (!skin.isValid()) {
+ this.close("", "disconnectionScreen.invalidSkin");
+ return;
+ }
- if (this.forceMovement != null && (newPos.distanceSquared(this.forceMovement) > 0.1 || revert)) {
- this.sendPosition(this.forceMovement, movePlayerPacket.yaw, movePlayerPacket.pitch, MovePlayerPacket.MODE_RESET);
- } else {
+ PlayerChangeSkinEvent playerChangeSkinEvent = new PlayerChangeSkinEvent(this, skin);
+ playerChangeSkinEvent.setCancelled(TimeUnit.SECONDS.toMillis(this.server.getPlayerSkinChangeCooldown()) > System.currentTimeMillis() - this.lastSkinChange);
+ this.server.getPluginManager().callEvent(playerChangeSkinEvent);
+ if (!playerChangeSkinEvent.isCancelled()) {
+ this.lastSkinChange = System.currentTimeMillis();
+ this.setSkin(skin);
+ }
+ return;
+ case ProtocolInfo.PLAYER_AUTH_INPUT_PACKET:
+ if (!this.spawned) {
+ return;
+ }
- movePlayerPacket.yaw %= 360;
- movePlayerPacket.pitch %= 360;
+ PlayerAuthInputPacket authPacket = (PlayerAuthInputPacket) packet;
+ if (!authPacket.getBlockActionData().isEmpty()) {
+ for (PlayerBlockActionData action : authPacket.getBlockActionData().values()) {
+ BlockVector3 blockPos = action.getPosition();
+ BlockFace blockFace = BlockFace.fromIndex(action.getFacing());
+ if (this.lastBlockAction != null && this.lastBlockAction.getAction() == PlayerActionType.PREDICT_DESTROY_BLOCK &&
+ action.getAction() == PlayerActionType.CONTINUE_DESTROY_BLOCK) {
+ this.onBlockBreakStart(blockPos, blockFace);
+ }
- if (movePlayerPacket.yaw < 0) {
- movePlayerPacket.yaw += 360;
+ BlockVector3 lastBreakPos = this.lastBlockAction == null ? null : this.lastBlockAction.getPosition();
+ if (lastBreakPos != null && (lastBreakPos.getX() != blockPos.getX() ||
+ lastBreakPos.getY() != blockPos.getY() || lastBreakPos.getZ() != blockPos.getZ())) {
+ this.onBlockBreakAbort(lastBreakPos, BlockFace.DOWN);
+ this.onBlockBreakStart(blockPos, blockFace);
}
- this.setRotation(movePlayerPacket.yaw, movePlayerPacket.pitch);
- this.newPosition = newPos;
- this.forceMovement = null;
+ switch (action.getAction()) {
+ case START_DESTROY_BLOCK:
+ this.onBlockBreakStart(blockPos, blockFace);
+ break;
+ case ABORT_DESTROY_BLOCK:
+ //case STOP_DESTROY_BLOCK:
+ this.onBlockBreakAbort(blockPos, blockFace);
+ break;
+ case CONTINUE_DESTROY_BLOCK:
+ // When player moves cursor to another block
+ break;
+ case PREDICT_DESTROY_BLOCK:
+ //this.onBlockBreakAbort(blockPos, blockFace);
+ this.onBlockBreakComplete(blockPos, blockFace);
+ break;
+ }
+ this.lastBlockAction = action;
}
- break;
- case ProtocolInfo.PLAYER_AUTH_INPUT_PACKET:
- PlayerAuthInputPacket authPacket = (PlayerAuthInputPacket) packet;
-
- if (!authPacket.getBlockActionData().isEmpty()) {
- for (PlayerBlockActionData action : authPacket.getBlockActionData().values()) {
- BlockVector3 blockPos = action.getPosition();
- BlockFace blockFace = BlockFace.fromIndex(action.getFacing());
- if (this.lastBlockAction != null && this.lastBlockAction.getAction() == PlayerActionType.PREDICT_DESTROY_BLOCK &&
- action.getAction() == PlayerActionType.CONTINUE_DESTROY_BLOCK) {
- this.onBlockBreakStart(blockPos.asVector3(), blockFace);
- }
+ }
- BlockVector3 lastBreakPos = this.lastBlockAction == null ? null : this.lastBlockAction.getPosition();
- if (lastBreakPos != null && (lastBreakPos.getX() != blockPos.getX() ||
- lastBreakPos.getY() != blockPos.getY() || lastBreakPos.getZ() != blockPos.getZ())) {
- this.onBlockBreakAbort(lastBreakPos.asVector3(), BlockFace.DOWN);
- this.onBlockBreakStart(blockPos.asVector3(), blockFace);
- }
+ if (this.teleportPosition != null) {
+ return;
+ }
- switch (action.getAction()) {
- case START_DESTROY_BLOCK:
- this.onBlockBreakStart(blockPos.asVector3(), blockFace);
- break;
- case ABORT_DESTROY_BLOCK:
- case STOP_DESTROY_BLOCK:
- this.onBlockBreakAbort(blockPos.asVector3(), blockFace);
- break;
- case CONTINUE_DESTROY_BLOCK:
- this.onBlockBreakContinue(blockPos.asVector3(), blockFace);
- break;
- case PREDICT_DESTROY_BLOCK:
- this.onBlockBreakAbort(blockPos.asVector3(), blockFace);
- this.onBlockBreakComplete(blockPos, blockFace);
- break;
- }
- this.lastBlockAction = action;
- }
+ if (this.riding instanceof EntityControllable && riding.isControlling(this)) {
+ boolean jumping = authPacket.getInputData().contains(AuthInputAction.JUMPING);
+ if (jumping && this.riderJumpTick <= 0) {
+ this.riderJumpTick = server.getTick();
+ } else if (!jumping && this.riderJumpTick > 0) {
+ ((EntityControllable) riding).onJump(this, server.getTick() - this.riderJumpTick);
+ this.riderJumpTick = 0;
}
-
- if (this.teleportPosition != null) {
- return;
+ double inputX = authPacket.getMotion().getX();
+ double inputY = authPacket.getMotion().getY();
+ if (inputX >= -1.001 && inputX <= 1.001 && inputY >= -1.001 && inputY <= 1.001) {
+ ((EntityControllable) riding).onPlayerInput(this, inputX, inputY);
}
-
- if (this.riding instanceof EntityMinecartAbstract) {
- double inputY = authPacket.getMotion().getY();
- if (inputY >= -1.001 && inputY <= 1.001) {
- ((EntityMinecartAbstract) riding).setCurrentSpeed(inputY);
- }
- } else if (this.riding instanceof EntityBoat && authPacket.getInputData().contains(AuthInputAction.IN_CLIENT_PREDICTED_IN_VEHICLE)) {
- if (this.riding.getId() == authPacket.getPredictedVehicle() && this.riding.isControlling(this)) {
- if (this.temporalVector.setComponents(authPacket.getPosition().getX(), authPacket.getPosition().getY(), authPacket.getPosition().getZ()).distanceSquared(this.riding) < 100) {
- ((EntityBoat) this.riding).onInput(authPacket.getPosition().getX(), authPacket.getPosition().getY(), authPacket.getPosition().getZ(), authPacket.getHeadYaw());
- }
+ } else if (this.riding instanceof EntityBoat && authPacket.getInputData().contains(AuthInputAction.IN_CLIENT_PREDICTED_IN_VEHICLE)) {
+ if (this.riding.getId() == authPacket.getPredictedVehicle() && this.riding.isControlling(this)) {
+ if (this.temporalVector.setComponents(authPacket.getPosition().getX(), authPacket.getPosition().getY(), authPacket.getPosition().getZ()).distanceSquared(this.riding) < 9) {
+ ((EntityBoat) this.riding).onInput(authPacket.getPosition().getX(), authPacket.getPosition().getY(), authPacket.getPosition().getZ(), authPacket.getHeadYaw());
}
}
+ }
- if (!this.isSpectator() && authPacket.getInputData().contains(AuthInputAction.MISSED_SWING)) {
- level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_ATTACK_NODAMAGE, -1, "minecraft:player", false, false);
- }
+ if (!this.isSpectator() && authPacket.getInputData().contains(AuthInputAction.MISSED_SWING)) {
+ level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_ATTACK_NODAMAGE, -1, "minecraft:player", false, false);
+ }
- if (authPacket.getInputData().contains(AuthInputAction.START_SPRINTING)) {
- PlayerToggleSprintEvent event = new PlayerToggleSprintEvent(this, true);
- this.server.getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- this.sendData(this);
- } else {
- this.setSprinting(true);
- }
+ if (authPacket.getInputData().contains(AuthInputAction.START_SPRINTING)) {
+ PlayerToggleSprintEvent playerToggleSprintEvent = new PlayerToggleSprintEvent(this, true);
+ if ((this.foodData.getLevel() <= 6 && !this.getAdventureSettings().get(Type.FLYING)) || this.hasEffect(Effect.BLINDNESS)) {
+ playerToggleSprintEvent.setCancelled(true);
}
-
- if (authPacket.getInputData().contains(AuthInputAction.STOP_SPRINTING)) {
- PlayerToggleSprintEvent event = new PlayerToggleSprintEvent(this, false);
- this.server.getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- this.sendData(this);
- } else {
- this.setSprinting(false);
- }
+ this.server.getPluginManager().callEvent(playerToggleSprintEvent);
+ if (playerToggleSprintEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSprinting(true, false);
}
+ this.setUsingItem(false);
+ }
- if (authPacket.getInputData().contains(AuthInputAction.START_SNEAKING)) {
- PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this, true);
- this.server.getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- this.sendData(this);
- } else {
- this.setSneaking(true);
- }
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_SPRINTING)) {
+ PlayerToggleSprintEvent playerToggleSprintEvent = new PlayerToggleSprintEvent(this, false);
+ this.server.getPluginManager().callEvent(playerToggleSprintEvent);
+ if (playerToggleSprintEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSprinting(false, false);
}
+ }
- if (authPacket.getInputData().contains(AuthInputAction.STOP_SNEAKING)) {
- PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this, false);
- this.server.getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- this.sendData(this);
- } else {
- this.setSneaking(false);
- }
+ if (authPacket.getInputData().contains(AuthInputAction.START_SNEAKING)) {
+ PlayerToggleSneakEvent playerToggleSneakEvent = new PlayerToggleSneakEvent(this, true);
+ this.server.getPluginManager().callEvent(playerToggleSneakEvent);
+ if (playerToggleSneakEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSneaking(true);
}
+ }
- if (authPacket.getInputData().contains(AuthInputAction.START_JUMPING)) {
- PlayerJumpEvent playerJumpEvent = new PlayerJumpEvent(this);
- this.server.getPluginManager().callEvent(playerJumpEvent);
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_SNEAKING)) {
+ PlayerToggleSneakEvent playerToggleSneakEvent = new PlayerToggleSneakEvent(this, false);
+ this.server.getPluginManager().callEvent(playerToggleSneakEvent);
+ if (playerToggleSneakEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSneaking(false);
}
+ }
- if (authPacket.getInputData().contains(AuthInputAction.START_GLIDING)) {
- PlayerToggleGlideEvent playerToggleGlideEvent = new PlayerToggleGlideEvent(this, true);
- this.server.getPluginManager().callEvent(playerToggleGlideEvent);
- if (playerToggleGlideEvent.isCancelled()) {
- this.sendData(this);
- } else {
- this.setGliding(true);
- }
+ if (authPacket.getInputData().contains(AuthInputAction.START_CRAWLING)) {
+ PlayerToggleCrawlEvent playerToggleCrawlEvent = new PlayerToggleCrawlEvent(this, true);
+ this.server.getPluginManager().callEvent(playerToggleCrawlEvent);
+ if (playerToggleCrawlEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setCrawling(true);
}
+ }
- if (authPacket.getInputData().contains(AuthInputAction.STOP_GLIDING)) {
- PlayerToggleGlideEvent playerToggleGlideEvent = new PlayerToggleGlideEvent(this, false);
- this.server.getPluginManager().callEvent(playerToggleGlideEvent);
- if (playerToggleGlideEvent.isCancelled()) {
- this.sendData(this);
- } else {
- this.setGliding(false);
- }
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_CRAWLING)) {
+ PlayerToggleCrawlEvent playerToggleCrawlEvent = new PlayerToggleCrawlEvent(this, false);
+ this.server.getPluginManager().callEvent(playerToggleCrawlEvent);
+ if (playerToggleCrawlEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setCrawling(false);
}
+ }
- if (authPacket.getInputData().contains(AuthInputAction.START_SWIMMING)) {
- PlayerToggleSwimEvent ptse = new PlayerToggleSwimEvent(this, true);
- this.server.getPluginManager().callEvent(ptse);
- if (ptse.isCancelled()) {
- this.sendData(this);
- } else {
- this.setSwimming(true);
- }
- }
+ if (authPacket.getInputData().contains(AuthInputAction.START_JUMPING)) {
+ this.server.getPluginManager().callEvent(new PlayerJumpEvent(this));
+ }
- if (authPacket.getInputData().contains(AuthInputAction.STOP_SWIMMING)) {
- PlayerToggleSwimEvent ptse = new PlayerToggleSwimEvent(this, false);
- this.server.getPluginManager().callEvent(ptse);
- if (ptse.isCancelled()) {
- this.sendData(this);
- } else {
- this.setSwimming(false);
- }
+ if (authPacket.getInputData().contains(AuthInputAction.START_GLIDING)) {
+ boolean withoutElytra = false;
+ Item chestplate = this.getInventory().getChestplateFast();
+ if (chestplate == null || chestplate.getId() != ItemID.ELYTRA || chestplate.getDamage() >= chestplate.getMaxDurability()) {
+ withoutElytra = true;
}
-
- if (authPacket.getInputData().contains(AuthInputAction.START_FLYING)) {
- if (!server.getAllowFlight() && !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, true);
- if (this.isSpectator()) {
- playerToggleFlightEvent.setCancelled();
- }
- this.getServer().getPluginManager().callEvent(playerToggleFlightEvent);
- if (playerToggleFlightEvent.isCancelled()) {
- this.getAdventureSettings().update();
- } else {
- this.getAdventureSettings().set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
- }
+ if (withoutElytra && !server.getAllowFlight()) {
+ this.kick(PlayerKickEvent.Reason.FLYING_DISABLED, MSG_FLYING_NOT_ENABLED, true);
+ return;
}
-
- if (authPacket.getInputData().contains(AuthInputAction.STOP_FLYING)) {
- PlayerToggleFlightEvent playerToggleFlightEvent = new PlayerToggleFlightEvent(this, false);
- if (this.isSpectator()) {
- playerToggleFlightEvent.setCancelled();
- }
- this.getServer().getPluginManager().callEvent(playerToggleFlightEvent);
- if (playerToggleFlightEvent.isCancelled()) {
- this.getAdventureSettings().update();
- } else {
- this.getAdventureSettings().set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
- }
+ PlayerToggleGlideEvent playerToggleGlideEvent = new PlayerToggleGlideEvent(this, true);
+ if (withoutElytra) {
+ playerToggleGlideEvent.setCancelled(true);
}
+ this.server.getPluginManager().callEvent(playerToggleGlideEvent);
+ if (playerToggleGlideEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setGliding(true);
+ }
+ }
- Vector3 clientPosition = authPacket.getPosition().subtract(0, this.getEyeHeight(), 0).asVector3();
-
- double distSqrt = clientPosition.distanceSquared(this);
- if (distSqrt == 0.0 && authPacket.getYaw() % 360 == this.yaw && authPacket.getPitch() % 360 == this.pitch) {
- break;
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_GLIDING)) {
+ PlayerToggleGlideEvent playerToggleGlideEvent = new PlayerToggleGlideEvent(this, false);
+ this.server.getPluginManager().callEvent(playerToggleGlideEvent);
+ if (playerToggleGlideEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setGliding(false);
}
+ }
- if (distSqrt > 100) {
- this.sendPosition(this, authPacket.getYaw(), authPacket.getPitch(), MovePlayerPacket.MODE_RESET);
- break;
+ if (authPacket.getInputData().contains(AuthInputAction.START_SWIMMING)) {
+ PlayerToggleSwimEvent ptse = new PlayerToggleSwimEvent(this, true);
+ if (!this.isInsideOfWater()) {
+ ptse.setCancelled(true);
}
+ this.server.getPluginManager().callEvent(ptse);
+ if (ptse.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSwimming(true);
+ }
+ this.setUsingItem(false);
+ }
- boolean revertMotion = false;
- if (!this.isAlive() || !this.spawned) {
- revertMotion = true;
- this.forceMovement = new Vector3(this.x, this.y, this.z);
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_SWIMMING)) {
+ PlayerToggleSwimEvent ptse = new PlayerToggleSwimEvent(this, false);
+ this.server.getPluginManager().callEvent(ptse);
+ if (ptse.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSwimming(false);
}
+ }
- if (this.forceMovement != null && (clientPosition.distanceSquared(this.forceMovement) > 0.1 || revertMotion)) {
- this.sendPosition(this.forceMovement, authPacket.getYaw(), authPacket.getPitch(), MovePlayerPacket.MODE_RESET);
+ if (authPacket.getInputData().contains(AuthInputAction.START_FLYING)) {
+ if (!server.getAllowFlight() && !this.adventureSettings.get(Type.ALLOW_FLIGHT)) {
+ this.kick(PlayerKickEvent.Reason.FLYING_DISABLED, MSG_FLYING_NOT_ENABLED, true);
+ break;
+ }
+ PlayerToggleFlightEvent playerToggleFlightEvent = new PlayerToggleFlightEvent(this, true);
+ server.getPluginManager().callEvent(playerToggleFlightEvent);
+ if (playerToggleFlightEvent.isCancelled()) {
+ this.needSendAdventureSettings = true;
} else {
- float yaw = authPacket.getYaw() % 360;
- float pitch = authPacket.getPitch() % 360;
- if (yaw < 0) {
- yaw += 360;
- }
+ this.adventureSettings.set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
+ }
+ }
- this.setRotation(yaw, pitch);
- this.newPosition = clientPosition;
- this.clientMovements.offer(clientPosition);
- this.forceMovement = null;
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_FLYING)) {
+ PlayerToggleFlightEvent playerToggleFlightEvent = new PlayerToggleFlightEvent(this, false);
+ if (this.isSpectator()) {
+ playerToggleFlightEvent.setCancelled(true);
}
- break;
- case ProtocolInfo.MOB_EQUIPMENT_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
+ server.getPluginManager().callEvent(playerToggleFlightEvent);
+ if (playerToggleFlightEvent.isCancelled()) {
+ this.needSendAdventureSettings = true;
+ } else {
+ this.adventureSettings.set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
}
+ }
- MobEquipmentPacket mobEquipmentPacket = (MobEquipmentPacket) packet;
+ Vector3 clientPosition = authPacket.getPosition().subtract(0, this.getBaseOffset(), 0).asVector3();
- Inventory inv = this.getWindowById(mobEquipmentPacket.windowId);
+ double distSqrt = clientPosition.distanceSquared(this);
+ if (distSqrt > 100) { // Notice: This is the distance to player's position on server side. There are likely still unhandled previous movements when next move packet is received.
+ this.sendPosition(this, authPacket.getYaw(), authPacket.getPitch(), MovePlayerPacket.MODE_RESET);
+ server.getLogger().debug(username + ": move " + distSqrt + " > 100");
+ return;
+ }
- if (inv == null) {
- this.server.getLogger().debug(this.getName() + " has no open container with window ID " + mobEquipmentPacket.windowId);
- return;
+ boolean revertMotion = false;
+ if (!this.isAlive() || !this.spawned) {
+ revertMotion = true;
+ this.forceMovement = this;
+ }
+
+ if (this.forceMovement != null && (revertMotion || clientPosition.distanceSquared(this.forceMovement) > 0.1)) {
+ this.sendPosition(this.forceMovement, authPacket.getYaw(), authPacket.getPitch(), MovePlayerPacket.MODE_RESET);
+ } else {
+ float yaw = authPacket.getYaw() % 360;
+ float pitch = authPacket.getPitch() % 360;
+ if (yaw < 0) {
+ yaw += 360;
}
- Item item = inv.getItem(mobEquipmentPacket.hotbarSlot);
+ this.setRotation(yaw, pitch);
+ this.newPosition = clientPosition;
+ this.clientMovements.offer(clientPosition);
+ this.forceMovement = null;
+ }
+ return;
+ case ProtocolInfo.MOB_EQUIPMENT_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- if (!item.equals(mobEquipmentPacket.item)) {
- this.server.getLogger().debug(this.getName() + " tried to equip " + mobEquipmentPacket.item + " but have " + item + " in target slot");
- inv.sendContents(this);
- return;
- }
+ MobEquipmentPacket mobEquipmentPacket = (MobEquipmentPacket) packet;
- if (inv instanceof PlayerInventory) {
- ((PlayerInventory) inv).equipItem(mobEquipmentPacket.hotbarSlot);
- }
+ Inventory inv = this.getWindowById(mobEquipmentPacket.windowId);
- this.setDataFlag(Player.DATA_FLAGS, Player.DATA_FLAG_ACTION, false);
+ if (inv == null) {
+ this.server.getLogger().debug("Player " + this.getName() + " has no open container with window ID " + mobEquipmentPacket.windowId);
+ return;
+ }
- break;
- case ProtocolInfo.PLAYER_ACTION_PACKET:
- PlayerActionPacket playerActionPacket = (PlayerActionPacket) packet;
- if (!this.spawned || !this.isAlive() && playerActionPacket.action != PlayerActionPacket.ACTION_RESPAWN) {
- break;
- }
+ /*Item item = inv.getItem(mobEquipmentPacket.hotbarSlot);
- switch (playerActionPacket.action) {
- case PlayerActionPacket.ACTION_STOP_SLEEPING:
- this.stopSleep();
- break;
- case PlayerActionPacket.ACTION_RESPAWN:
- if (!this.spawned || this.isAlive() || !this.isOnline()) {
- break;
- }
- this.respawn();
- break;
- case PlayerActionPacket.ACTION_DIMENSION_CHANGE_ACK:
- this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_RESET);
- break;
+ if (!item.equals(mobEquipmentPacket.item)) {
+ if (Nukkit.DEBUG > 1) {
+ this.server.getLogger().debug("Tried to equip " + mobEquipmentPacket.item + " but have " + item + " in target slot");
}
+ inv.sendSlot(mobEquipmentPacket.hotbarSlot, this);
+ return;
+ }*/
- this.setUsingItem(false);
- break;
- case ProtocolInfo.MOB_ARMOR_EQUIPMENT_PACKET:
- break;
- case ProtocolInfo.MODAL_FORM_RESPONSE_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
- }
+ if (inv instanceof PlayerInventory) {
+ ((PlayerInventory) inv).equipItem(mobEquipmentPacket.hotbarSlot);
+ }
- ModalFormResponsePacket modalFormPacket = (ModalFormResponsePacket) packet;
+ this.setUsingItem(false);
+ return;
+ case ProtocolInfo.PLAYER_ACTION_PACKET:
+ PlayerActionPacket playerActionPacket = (PlayerActionPacket) packet;
+ if (!this.spawned || (!this.isAlive() && playerActionPacket.action != PlayerActionPacket.ACTION_RESPAWN)) {
+ return;
+ }
- if (formWindows.containsKey(modalFormPacket.formId)) {
- FormWindow window = formWindows.remove(modalFormPacket.formId);
- window.setResponse(modalFormPacket.data.trim());
+ playerActionPacket.entityId = this.id;
- for (FormResponseHandler handler : window.getHandlers()) {
- handler.handle(this, modalFormPacket.formId);
+ stopItemHold:
+ switch (playerActionPacket.action) {
+ case PlayerActionPacket.ACTION_START_BREAK:
+ break stopItemHold;
+ case PlayerActionPacket.ACTION_ABORT_BREAK:
+ //case PlayerActionPacket.ACTION_STOP_BREAK: // This could be used instead of inventory transaction when the breaking is done?
+ return;
+ case PlayerActionPacket.ACTION_STOP_SLEEPING:
+ this.stopSleep();
+ return;
+ case PlayerActionPacket.ACTION_RESPAWN:
+ if (!this.spawned || this.isAlive() || !this.isOnline()) {
+ return;
+ }
+ this.respawn();
+ break stopItemHold;
+ case PlayerActionPacket.ACTION_JUMP:
+ return;
+ case PlayerActionPacket.ACTION_START_SPRINT:
+ break stopItemHold;
+ case PlayerActionPacket.ACTION_STOP_SPRINT:
+ return;
+ case PlayerActionPacket.ACTION_START_SNEAK:
+ return;
+ case PlayerActionPacket.ACTION_STOP_SNEAK:
+ return;
+ case PlayerActionPacket.ACTION_DIMENSION_CHANGE_ACK:
+ if (this.awaitingDimensionAck) {
+ this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_RESET);
+ this.dummyBossBars.values().forEach(DummyBossBar::reshow);
+ this.awaitingDimensionAck = false;
+ } else {
+ this.getServer().getLogger().debug(username + ": got a dimension change ack but no dimension change is in progress");
+ }
+ return;
+ case PlayerActionPacket.ACTION_START_GLIDE:
+ return;
+ case PlayerActionPacket.ACTION_STOP_GLIDE:
+ return;
+ case PlayerActionPacket.ACTION_CONTINUE_BREAK:
+ // When player moves cursor to another block
+ return;
+ case PlayerActionPacket.ACTION_START_SWIMMING:
+ break stopItemHold;
+ case PlayerActionPacket.ACTION_STOP_SWIMMING:
+ return;
+ case PlayerActionPacket.ACTION_START_SPIN_ATTACK:
+ if (this.inventory == null) {
+ this.getServer().getLogger().debug(username + ": got ACTION_START_SPIN_ATTACK but inventory was null");
+ break stopItemHold;
}
- PlayerFormRespondedEvent event = new PlayerFormRespondedEvent(this, modalFormPacket.formId, window);
- getServer().getPluginManager().callEvent(event);
- } else if (serverSettings.containsKey(modalFormPacket.formId)) {
- FormWindow window = serverSettings.get(modalFormPacket.formId);
- window.setResponse(modalFormPacket.data.trim());
+ PlayerToggleSpinAttackEvent playerToggleSpinAttackEvent = new PlayerToggleSpinAttackEvent(this, true);
- for (FormResponseHandler handler : window.getHandlers()) {
- handler.handle(this, modalFormPacket.formId);
+ int riptideLevel = 0;
+ Item hand;
+ if ((hand = this.inventory.getItemInHandFast()).getId() != ItemID.TRIDENT) {
+ playerToggleSpinAttackEvent.setCancelled(true);
+ this.getServer().getLogger().debug(username + ": got ACTION_START_SPIN_ATTACK but hand item is not a trident");
+ } else {
+ Enchantment riptide = hand.getEnchantment(Enchantment.ID_TRIDENT_RIPTIDE);
+ if (riptide == null) {
+ playerToggleSpinAttackEvent.setCancelled(true);
+ } else {
+ riptideLevel = riptide.getLevel();
+ if (riptideLevel < 1) {
+ playerToggleSpinAttackEvent.setCancelled(true);
+ } else {
+ boolean inWater = false;
+ for (Block block : this.getCollisionBlocks()) {
+ if (block instanceof BlockWater) {
+ inWater = true;
+ break;
+ }
+ }
+ if (!(inWater || (this.getLevel().isRaining() && this.canSeeSky()))) {
+ playerToggleSpinAttackEvent.setCancelled(true);
+ }
+ }
+ }
}
- PlayerSettingsRespondedEvent event = new PlayerSettingsRespondedEvent(this, modalFormPacket.formId, window);
- getServer().getPluginManager().callEvent(event);
+ this.server.getPluginManager().callEvent(playerToggleSpinAttackEvent);
- //Set back new settings if not been cancelled
- if (!event.isCancelled() && window instanceof FormWindowCustom)
- ((FormWindowCustom) window).setElementsFromResponse();
- }
+ if (playerToggleSpinAttackEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSpinAttack(true);
+ this.resetFallDistance();
- break;
+ this.riptideTicks = 50 + (riptideLevel << 5);
- case ProtocolInfo.INTERACT_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
- }
+ int riptideSound;
+ if (riptideLevel >= 3) {
+ riptideSound = LevelSoundEventPacket.SOUND_ITEM_TRIDENT_RIPTIDE_3;
+ } else if (riptideLevel == 2) {
+ riptideSound = LevelSoundEventPacket.SOUND_ITEM_TRIDENT_RIPTIDE_2;
+ } else {
+ riptideSound = LevelSoundEventPacket.SOUND_ITEM_TRIDENT_RIPTIDE_1;
+ }
+ this.level.addLevelSoundEvent(this, riptideSound);
+ }
+ break stopItemHold;
+ case PlayerActionPacket.ACTION_STOP_SPIN_ATTACK:
+ playerToggleSpinAttackEvent = new PlayerToggleSpinAttackEvent(this, false);
+ this.server.getPluginManager().callEvent(playerToggleSpinAttackEvent);
+ if (playerToggleSpinAttackEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSpinAttack(false);
+ }
+ return;
+ }
- InteractPacket interactPacket = (InteractPacket) packet;
+ this.setUsingItem(false);
+ return;
+ case ProtocolInfo.MODAL_FORM_RESPONSE_PACKET:
+ this.formOpen = false;
- if (interactPacket.target == 0 && interactPacket.action == InteractPacket.ACTION_MOUSEOVER) {
- this.setButtonText("");
- break;
- }
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- Entity targetEntity = interactPacket.target == this.getId() ? this : this.level.getEntity(interactPacket.target);
+ ModalFormResponsePacket modalFormPacket = (ModalFormResponsePacket) packet;
- if (targetEntity == null || !this.isAlive() || !targetEntity.isAlive()) {
- break;
- }
+ if (formWindows.containsKey(modalFormPacket.formId)) {
+ FormWindow window = formWindows.remove(modalFormPacket.formId);
+ window.setResponse(modalFormPacket.data.trim());
- if (targetEntity instanceof EntityItem || targetEntity instanceof EntityArrow || targetEntity instanceof EntityXPOrb) {
- 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;
+ for (FormResponseHandler handler : window.getHandlers()) {
+ handler.handle(this, modalFormPacket.formId);
}
- switch (interactPacket.action) {
- case InteractPacket.ACTION_MOUSEOVER:
- String buttonText = "";
- if (targetEntity instanceof EntityInteractable) {
- buttonText = ((EntityInteractable) targetEntity).getInteractButtonText(this);
- if (buttonText == null) {
- buttonText = "";
- }
- }
- this.setButtonText(buttonText);
+ PlayerFormRespondedEvent event = new PlayerFormRespondedEvent(this, modalFormPacket.formId, window);
+ getServer().getPluginManager().callEvent(event);
+ } else if (serverSettings.containsKey(modalFormPacket.formId)) {
+ FormWindow window = serverSettings.get(modalFormPacket.formId);
+ window.setResponse(modalFormPacket.data.trim());
- this.getServer().getPluginManager().callEvent(new PlayerMouseOverEntityEvent(this, targetEntity));
- break;
- case InteractPacket.ACTION_VEHICLE_EXIT:
- if (!(targetEntity instanceof EntityRideable) || this.riding != targetEntity) {
- break;
- }
+ for (FormResponseHandler handler : window.getHandlers()) {
+ handler.handle(this, modalFormPacket.formId);
+ }
- ((EntityRideable) riding).dismountEntity(this);
- break;
- case InteractPacket.ACTION_OPEN_INVENTORY:
- if (targetEntity != this) {
- break;
- }
- if (!this.inventoryOpen && this.inventory.open(this)) {
+ PlayerSettingsRespondedEvent event = new PlayerSettingsRespondedEvent(this, modalFormPacket.formId, window);
+ getServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled() && window instanceof FormWindowCustom)
+ ((FormWindowCustom) window).setElementsFromResponse();
+ }
+
+ return;
+ case ProtocolInfo.INTERACT_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
+
+ InteractPacket interactPacket = (InteractPacket) packet;
+
+ if (interactPacket.target == 0 && interactPacket.action == InteractPacket.ACTION_MOUSEOVER) {
+ this.setButtonText("");
+ return;
+ }
+
+ Entity targetEntity = interactPacket.target == this.getId() ? this : this.level.getEntity(interactPacket.target);
+
+ if (targetEntity == null || !this.isAlive() || !targetEntity.isAlive()) {
+ if (targetEntity != null || interactPacket.action != InteractPacket.ACTION_OPEN_INVENTORY) {
+ return;
+ }
+ }
+
+ if (targetEntity instanceof EntityItem || targetEntity instanceof EntityArrow || targetEntity instanceof EntityXPOrb) {
+ this.kick(PlayerKickEvent.Reason.INVALID_PVE);
+ return;
+ }
+
+ switch (interactPacket.action) {
+ case InteractPacket.ACTION_OPEN_INVENTORY:
+ if (!this.inventoryOpen) {
+ if (this.riding instanceof EntityChestBoat && this.riding == targetEntity) {
+ this.addWindow(((InventoryHolder) targetEntity).getInventory());
+ } else if (this.inventory.open(this)) {
this.inventoryOpen = true;
+ this.awardAchievement("openInventory");
}
- break;
- }
- break;
- case ProtocolInfo.BLOCK_PICK_REQUEST_PACKET:
- BlockPickRequestPacket pickRequestPacket = (BlockPickRequestPacket) packet;
- Block block = this.level.getBlock(pickRequestPacket.x, pickRequestPacket.y, pickRequestPacket.z, false);
- if (block.distanceSquared(this) > 1000) {
- this.getServer().getLogger().debug(username + ": Block pick request for a block too far away");
+ }
return;
- }
- 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.setCustomBlockData(nbt);
- item.setLore("+(DATA)");
+ case InteractPacket.ACTION_MOUSEOVER:
+ String buttonText = "";
+ if (targetEntity instanceof EntityInteractable) {
+ buttonText = ((EntityInteractable) targetEntity).getInteractButtonText(this);
+ if (buttonText == null) {
+ buttonText = "";
}
}
- }
+ this.setButtonText(buttonText);
+ this.getServer().getPluginManager().callEvent(new PlayerMouseOverEntityEvent(this, targetEntity));
+ return;
+ case InteractPacket.ACTION_VEHICLE_EXIT:
+ if (!(targetEntity instanceof EntityRideable) || this.riding != targetEntity) {
+ return;
+ }
- PlayerBlockPickEvent pickEvent = new PlayerBlockPickEvent(this, block, item);
- if (this.isSpectator()) {
- log.debug("Got block-pick request from " + this.getName() + " when in spectator mode");
- pickEvent.setCancelled();
+ this.riderJumpTick = 0;
+ ((EntityRideable) riding).dismountEntity(this);
+ return;
+ }
+ return;
+ case ProtocolInfo.BLOCK_PICK_REQUEST_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
+
+ if (this.inventory == null) {
+ this.getServer().getLogger().debug(username + ": got block pick request but inventory was null");
+ return;
+ }
+
+ BlockPickRequestPacket pickRequestPacket = (BlockPickRequestPacket) packet;
+ Block block = this.level.getBlock(chunk, pickRequestPacket.x, pickRequestPacket.y, pickRequestPacket.z, false);
+ if (block.distanceSquared(this) > 1000) {
+ this.getServer().getLogger().debug(username + ": block pick request for a block too far away");
+ return;
+ }
+ Item item = block.toItem();
+ if (pickRequestPacket.addUserData) {
+ BlockEntity blockEntity = this.getLevel().getBlockEntityIfLoaded(this.chunk, this.temporalVector.setComponents(pickRequestPacket.x, pickRequestPacket.y, pickRequestPacket.z));
+ if (blockEntity != null) {
+ CompoundTag nbt = blockEntity.getCleanedNBT();
+ if (nbt != null) {
+ item.setCustomBlockData(nbt);
+ item.setLore("+(DATA)");
+ }
}
+ }
- this.server.getPluginManager().callEvent(pickEvent);
+ PlayerBlockPickEvent pickEvent = new PlayerBlockPickEvent(this, block, item);
+ if (this.isSpectator()) {
+ pickEvent.setCancelled();
+ }
- if (!pickEvent.isCancelled()) {
- boolean itemExists = false;
- int itemSlot = -1;
- for (int slot = 0; slot < this.inventory.getSize(); slot++) {
- if (this.inventory.getItem(slot).equals(pickEvent.getItem())) {
- if (slot < this.inventory.getHotbarSize()) {
- this.inventory.setHeldItemSlot(slot);
- } else {
- itemSlot = slot;
- }
- itemExists = true;
- break;
+ this.server.getPluginManager().callEvent(pickEvent);
+
+ if (!pickEvent.isCancelled()) {
+ boolean itemExists = false;
+ int itemSlot = -1;
+ for (int slot = 0; slot < this.inventory.getSize(); slot++) {
+ if (this.inventory.getItem(slot).equals(pickEvent.getItem())) {
+ if (slot < this.inventory.getHotbarSize()) {
+ this.inventory.setHeldItemSlot(slot);
+ } else {
+ itemSlot = slot;
}
+ itemExists = true;
+ break;
}
+ }
- for (int slot = 0; slot < this.inventory.getHotbarSize(); slot++) {
- if (this.inventory.getItem(slot).isNull()) {
- if (!itemExists && this.isCreative()) {
- this.inventory.setHeldItemSlot(slot);
- this.inventory.setItemInHand(pickEvent.getItem());
- break packetswitch;
- } else if (itemSlot > -1) {
- this.inventory.setHeldItemSlot(slot);
- this.inventory.setItemInHand(this.inventory.getItem(itemSlot));
- this.inventory.clear(itemSlot, true);
- break packetswitch;
- }
+ for (int slot = 0; slot < this.inventory.getHotbarSize(); slot++) {
+ if (this.inventory.getItem(slot).isNull()) {
+ if (!itemExists && this.isCreative()) {
+ this.inventory.setHeldItemSlot(slot);
+ this.inventory.setItemInHand(pickEvent.getItem());
+ return;
+ } else if (itemSlot > -1) {
+ this.inventory.setHeldItemSlot(slot);
+ this.inventory.setItemInHand(this.inventory.getItem(itemSlot));
+ this.inventory.clear(itemSlot, true);
+ return;
}
}
+ }
- if (!itemExists && this.isCreative()) {
- Item itemInHand = this.inventory.getItemInHand();
- this.inventory.setItemInHand(pickEvent.getItem());
- if (!this.inventory.isFull()) {
- for (int slot = 0; slot < this.inventory.getSize(); slot++) {
- if (this.inventory.getItem(slot).isNull()) {
- this.inventory.setItem(slot, itemInHand);
- break;
- }
+ if (!itemExists && this.isCreative()) {
+ Item itemInHand = this.inventory.getItemInHand();
+ this.inventory.setItemInHand(pickEvent.getItem());
+ if (!this.inventory.isFull()) {
+ for (int slot = 0; slot < this.inventory.getSize(); slot++) {
+ if (this.inventory.getItem(slot).isNull()) {
+ this.inventory.setItem(slot, itemInHand);
+ return;
}
}
- } else if (itemSlot > -1) {
- Item itemInHand = this.inventory.getItemInHand();
- this.inventory.setItemInHand(this.inventory.getItem(itemSlot));
- this.inventory.setItem(itemSlot, itemInHand);
}
+ } else if (itemSlot > -1) {
+ Item itemInHand = this.inventory.getItemInHand();
+ this.inventory.setItemInHand(this.inventory.getItem(itemSlot));
+ this.inventory.setItem(itemSlot, itemInHand);
}
- break;
- case ProtocolInfo.ANIMATE_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
- }
+ }
+ return;
+ case ProtocolInfo.ANIMATE_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- AnimatePacket animatePacket = (AnimatePacket) packet;
- PlayerAnimationEvent animationEvent = new PlayerAnimationEvent(this, animatePacket.action);
+ AnimatePacket animatePacket = (AnimatePacket) packet;
- // prevent client send illegal packet to server and broadcast to other client and make other client crash
- if(animatePacket.action == null // illegal action id
- || animatePacket.action == AnimatePacket.Action.WAKE_UP // these actions are only for server to client
- || animatePacket.action == AnimatePacket.Action.CRITICAL_HIT
- || animatePacket.action == AnimatePacket.Action.MAGIC_CRITICAL_HIT) {
- break; // maybe we should cancel the event here? but if client send too many packets, server will lag
- }
+ if (animatePacket.action == null // Illegal action ID
+ || animatePacket.action == AnimatePacket.Action.WAKE_UP // These actions are server to client only
+ || animatePacket.action == AnimatePacket.Action.CRITICAL_HIT
+ || animatePacket.action == AnimatePacket.Action.MAGIC_CRITICAL_HIT) {
+ return;
+ }
- this.server.getPluginManager().callEvent(animationEvent);
- if (animationEvent.isCancelled()) {
- break;
- }
+ PlayerAnimationEvent animationEvent = new PlayerAnimationEvent(this, animatePacket.action);
+ this.server.getPluginManager().callEvent(animationEvent);
+ if (animationEvent.isCancelled()) {
+ return;
+ }
- AnimatePacket.Action animation = animationEvent.getAnimationType();
+ AnimatePacket.Action animation = animationEvent.getAnimationType();
- switch (animation) {
- case ROW_RIGHT:
- case ROW_LEFT:
- if (this.riding instanceof EntityBoat) {
- ((EntityBoat) this.riding).onPaddle(animation, ((AnimatePacket) packet).rowingTime);
- }
- break;
- }
+ switch (animation) {
+ case ROW_RIGHT:
+ case ROW_LEFT:
+ if (this.riding instanceof EntityBoat) {
+ ((EntityBoat) this.riding).onPaddle(animation, animatePacket.rowingTime);
+ }
+ break;
+ }
- animatePacket.eid = this.getId();
- animatePacket.action = animationEvent.getAnimationType();
- Server.broadcastPacket(this.getViewers().values(), animatePacket);
- break;
- case ProtocolInfo.SET_HEALTH_PACKET:
- //use UpdateAttributePacket instead
- break;
+ animatePacket = new AnimatePacket();
+ animatePacket.eid = this.getId();
+ animatePacket.action = animationEvent.getAnimationType();
+ Server.broadcastPacket(this.getViewers().values(), animatePacket);
+ return;
+ case ProtocolInfo.ENTITY_EVENT_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- case ProtocolInfo.ENTITY_EVENT_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
- }
- EntityEventPacket entityEventPacket = (EntityEventPacket) packet;
- if (entityEventPacket.event != EntityEventPacket.ENCHANT)
- this.craftingType = CRAFTING_SMALL;
- //this.resetCraftingGridType();
+ EntityEventPacket entityEventPacket = (EntityEventPacket) packet;
+ if (entityEventPacket.event != EntityEventPacket.ENCHANT) {
+ this.craftingType = CRAFTING_SMALL;
+ }
- if (entityEventPacket.event == EntityEventPacket.EATING_ITEM) {
+ switch (entityEventPacket.event) {
+ case EntityEventPacket.EATING_ITEM:
if (entityEventPacket.data == 0 || entityEventPacket.eid != this.id) {
- break;
+ this.getServer().getLogger().debug(username + ": entity event eid mismatch");
+ return;
}
entityEventPacket.eid = this.id;
entityEventPacket.isEncoded = false;
-
this.dataPacket(entityEventPacket);
Server.broadcastPacket(this.getViewers().values(), entityEventPacket);
- } else if (entityEventPacket.event == EntityEventPacket.ENCHANT) {
+ return;
+ case EntityEventPacket.ENCHANT:
if (entityEventPacket.eid != this.id) {
- break;
+ this.getServer().getLogger().debug(username + ": entity event eid mismatch");
+ return;
}
Inventory inventory = this.getWindowById(ANVIL_WINDOW_ID);
if (inventory instanceof AnvilInventory) {
((AnvilInventory) inventory).setCost(-entityEventPacket.data);
}
- }
- break;
- case ProtocolInfo.COMMAND_REQUEST_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
- }
- this.craftingType = CRAFTING_SMALL;
- CommandRequestPacket commandRequestPacket = (CommandRequestPacket) packet;
- PlayerCommandPreprocessEvent playerCommandPreprocessEvent = new PlayerCommandPreprocessEvent(this, commandRequestPacket.command);
- this.server.getPluginManager().callEvent(playerCommandPreprocessEvent);
- if (playerCommandPreprocessEvent.isCancelled()) {
- break;
- }
+ return;
+ }
+ return;
+ case ProtocolInfo.COMMAND_REQUEST_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- Timings.playerCommandTimer.startTiming();
- this.server.dispatchCommand(playerCommandPreprocessEvent.getPlayer(), playerCommandPreprocessEvent.getMessage().substring(1));
- Timings.playerCommandTimer.stopTiming();
- break;
- case ProtocolInfo.TEXT_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
- }
+ this.resetCraftingGridType();
+
+ CommandRequestPacket commandRequestPacket = (CommandRequestPacket) packet;
+ PlayerCommandPreprocessEvent playerCommandPreprocessEvent = new PlayerCommandPreprocessEvent(this, commandRequestPacket.command + ' ');
+ this.server.getPluginManager().callEvent(playerCommandPreprocessEvent);
+ if (playerCommandPreprocessEvent.isCancelled()) {
+ return;
+ }
- TextPacket textPacket = (TextPacket) packet;
+ this.server.dispatchCommand(playerCommandPreprocessEvent.getPlayer(), playerCommandPreprocessEvent.getMessage().substring(1));
+ return;
+ case ProtocolInfo.TEXT_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- if (textPacket.type == TextPacket.TYPE_CHAT) {
- String chatMessage = textPacket.message;
- int breakLine = chatMessage.indexOf('\n');
- // Chat messages shouldn't contain break lines so ignore text afterwards
- if (breakLine != -1) {
- chatMessage = chatMessage.substring(0, breakLine);
- }
- this.chat(chatMessage);
- }
- break;
- case ProtocolInfo.CONTAINER_CLOSE_PACKET:
- ContainerClosePacket containerClosePacket = (ContainerClosePacket) packet;
- if (!this.spawned || containerClosePacket.windowId == ContainerIds.INVENTORY && !inventoryOpen) {
- break;
- }
+ TextPacket textPacket = (TextPacket) packet;
- if (this.windowIndex.containsKey(containerClosePacket.windowId)) {
- this.server.getPluginManager().callEvent(new InventoryCloseEvent(this.windowIndex.get(containerClosePacket.windowId), this));
- if (containerClosePacket.windowId == ContainerIds.INVENTORY) this.inventoryOpen = false;
- this.closingWindowId = containerClosePacket.windowId;
- this.removeWindow(this.windowIndex.get(containerClosePacket.windowId), true);
- this.closingWindowId = Integer.MIN_VALUE;
- }
- if (containerClosePacket.windowId == -1) {
- this.craftingType = CRAFTING_SMALL;
- this.resetCraftingGridType();
- this.addWindow(this.craftingGrid, ContainerIds.NONE);
- ContainerClosePacket pk = new ContainerClosePacket();
- pk.wasServerInitiated = false;
- pk.windowId = -1;
- this.dataPacket(pk);
- }
- break;
- case ProtocolInfo.CRAFTING_EVENT_PACKET:
- break;
- case ProtocolInfo.BLOCK_ENTITY_DATA_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
+ if (textPacket.type == TextPacket.TYPE_CHAT && textPacket.message.length() < 512) {
+ String chatMessage = textPacket.message;
+ int breakLine = chatMessage.indexOf('\n');
+ // Chat messages shouldn't contain break lines so ignore text afterwards
+ if (breakLine != -1) {
+ chatMessage = chatMessage.substring(0, breakLine);
}
+ this.chat(chatMessage);
+ }
+ return;
+ case ProtocolInfo.CONTAINER_CLOSE_PACKET:
+ ContainerClosePacket containerClosePacket = (ContainerClosePacket) packet;
+ if (!this.spawned || (containerClosePacket.windowId == ContainerIds.INVENTORY && !inventoryOpen)) {
+ return;
+ }
- BlockEntityDataPacket blockEntityDataPacket = (BlockEntityDataPacket) packet;
- this.craftingType = CRAFTING_SMALL;
+ if (this.windowIndex.containsKey(containerClosePacket.windowId)) {
+ this.server.getPluginManager().callEvent(new InventoryCloseEvent(this.windowIndex.get(containerClosePacket.windowId), this));
+ if (containerClosePacket.windowId == ContainerIds.INVENTORY) this.inventoryOpen = false;
+ this.closingWindowId = containerClosePacket.windowId;
+ this.removeWindow(this.windowIndex.get(containerClosePacket.windowId), true);
+ this.closingWindowId = Integer.MIN_VALUE;
+ }
+
+ if (containerClosePacket.windowId == -1) {
this.resetCraftingGridType();
+ this.addWindow(this.craftingGrid, ContainerIds.NONE);
+ ContainerClosePacket pk = new ContainerClosePacket();
+ pk.windowId = -1;
+ pk.wasServerInitiated = false;
+ this.dataPacket(pk);
+ }
+ return;
+ case ProtocolInfo.BLOCK_ENTITY_DATA_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- Vector3 pos = new Vector3(blockEntityDataPacket.x, blockEntityDataPacket.y, blockEntityDataPacket.z);
- if (pos.distanceSquared(this) > 10000) {
- break;
+ BlockEntityDataPacket blockEntityDataPacket = (BlockEntityDataPacket) packet;
+ this.resetCraftingGridType();
+
+ Vector3 pos = this.temporalVector.setComponents(blockEntityDataPacket.x, blockEntityDataPacket.y, blockEntityDataPacket.z);
+ if (pos.distanceSquared(this) > 10000) {
+ if (Nukkit.DEBUG > 1) {
+ server.getLogger().debug(username + ": BlockEntityDataPacket target too far " + pos);
}
+ return;
+ }
- BlockEntity t = this.level.getBlockEntity(pos);
- if (t instanceof BlockEntitySpawnable) {
- CompoundTag nbt;
- try {
- nbt = NBTIO.read(blockEntityDataPacket.namedTag, ByteOrder.LITTLE_ENDIAN, true);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ BlockEntity t = this.level.getBlockEntity(pos);
+ if (t instanceof BlockEntitySpawnable) {
+ CompoundTag nbt;
+ try {
+ nbt = NBTIO.read(blockEntityDataPacket.namedTag, ByteOrder.LITTLE_ENDIAN, true);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
- if (!((BlockEntitySpawnable) t).updateCompoundTag(nbt, this)) {
- ((BlockEntitySpawnable) 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(3, Math.min(requestChunkRadiusPacket.radius, this.viewDistance));
- chunkRadiusUpdatePacket.radius = this.chunkRadius;
- this.dataPacket(chunkRadiusUpdatePacket);
- break;
- case ProtocolInfo.SET_PLAYER_GAME_TYPE_PACKET:
- SetPlayerGameTypePacket setPlayerGameTypePacket = (SetPlayerGameTypePacket) packet;
- if (setPlayerGameTypePacket.gamemode != this.gamemode) {
- if (!this.hasPermission("nukkit.command.gamemode")) {
- SetPlayerGameTypePacket setPlayerGameTypePacket1 = new SetPlayerGameTypePacket();
- setPlayerGameTypePacket1.gamemode = this.gamemode & 0x01;
- this.dataPacket(setPlayerGameTypePacket1);
- this.getAdventureSettings().update();
- break;
+ }
+ return;
+ case ProtocolInfo.REQUEST_CHUNK_RADIUS_PACKET:
+ RequestChunkRadiusPacket requestChunkRadiusPacket = (RequestChunkRadiusPacket) packet;
+ ChunkRadiusUpdatedPacket chunkRadiusUpdatePacket = new ChunkRadiusUpdatedPacket();
+ this.chunkRadius = Math.max(3, Math.min(requestChunkRadiusPacket.radius, this.viewDistance));
+ chunkRadiusUpdatePacket.radius = this.chunkRadius;
+ this.dataPacket(chunkRadiusUpdatePacket);
+ return;
+ case ProtocolInfo.SET_PLAYER_GAME_TYPE_PACKET:
+ if (!this.spawned) {
+ return;
+ }
+
+ SetPlayerGameTypePacket setPlayerGameTypePacket = (SetPlayerGameTypePacket) packet;
+ if (setPlayerGameTypePacket.gamemode != this.gamemode) {
+ if (!this.hasPermission("nukkit.command.gamemode")) {
+ if (!this.isOp()) {
+ this.kick(PlayerKickEvent.Reason.INVALID_PACKET, "Invalid SetPlayerGameTypePacket", true);
}
- this.setGamemode(setPlayerGameTypePacket.gamemode, true);
- Command.broadcastCommandMessage(this, new TranslationContainer("commands.gamemode.success.self", Server.getGamemodeString(this.gamemode)));
+ return;
}
- break;
- case ProtocolInfo.ITEM_FRAME_DROP_ITEM_PACKET:
- ItemFrameDropItemPacket itemFrameDropItemPacket = (ItemFrameDropItemPacket) packet;
- Vector3 vector3 = this.temporalVector.setComponents(itemFrameDropItemPacket.x, itemFrameDropItemPacket.y, itemFrameDropItemPacket.z);
- if (vector3.distanceSquared(this) < 1000) {
- BlockEntity itemFrame = this.level.getBlockEntity(vector3);
- if (itemFrame instanceof BlockEntityItemFrame) {
- ((BlockEntityItemFrame) itemFrame).dropItem(this);
- }
+ this.setGamemode(setPlayerGameTypePacket.gamemode, true);
+ Command.broadcastCommandMessage(this, new TranslationContainer("commands.gamemode.success.self", Server.getGamemodeString(this.gamemode)));
+ }
+ return;
+ case ProtocolInfo.ITEM_FRAME_DROP_ITEM_PACKET:
+ if (!this.spawned) {
+ return;
+ }
+
+ ItemFrameDropItemPacket itemFrameDropItemPacket = (ItemFrameDropItemPacket) packet;
+ Vector3 frame = this.temporalVector.setComponents(itemFrameDropItemPacket.x, itemFrameDropItemPacket.y, itemFrameDropItemPacket.z);
+ if (frame.distanceSquared(this) < 1000) {
+ BlockEntity itemFrame = this.level.getBlockEntityIfLoaded(this.chunk, frame);
+ if (itemFrame instanceof BlockEntityItemFrame) {
+ ((BlockEntityItemFrame) itemFrame).dropItem(this);
}
- break;
- case ProtocolInfo.MAP_INFO_REQUEST_PACKET:
- MapInfoRequestPacket pk = (MapInfoRequestPacket) packet;
- Item mapItem = null;
+ }
+ return;
+ case ProtocolInfo.MAP_INFO_REQUEST_PACKET:
+ if (this.inventory == null) {
+ this.getServer().getLogger().debug(username + ": got map info request but inventory was null");
+ return;
+ }
+
+ MapInfoRequestPacket pk = (MapInfoRequestPacket) packet;
+
+ Item mapItem = null;
+
+ for (Item item1 : this.offhandInventory.getContents().values()) {
+ if (item1 instanceof ItemMap && ((ItemMap) item1).getMapId() == pk.mapId) {
+ mapItem = item1;
+ }
+ }
- for (Item item1 : this.offhandInventory.getContents().values()) {
+ if (mapItem == null) {
+ for (Item item1 : this.inventory.getContents().values()) {
if (item1 instanceof ItemMap && ((ItemMap) item1).getMapId() == pk.mapId) {
mapItem = item1;
}
}
+ }
+
+ if (mapItem == null) {
+ for (BlockEntity be : this.level.getBlockEntities().values()) {
+ if (be instanceof BlockEntityItemFrame) {
+ BlockEntityItemFrame itemFrame1 = (BlockEntityItemFrame) be;
- if (mapItem == null) {
- for (Item item1 : this.inventory.getContents().values()) {
- if (item1 instanceof ItemMap && ((ItemMap) item1).getMapId() == pk.mapId) {
- mapItem = item1;
+ if (itemFrame1.getItem() instanceof ItemMap && ((ItemMap) itemFrame1.getItem()).getMapId() == pk.mapId) {
+ ((ItemMap) itemFrame1.getItem()).sendImage(this);
+ return;
}
}
}
+ } else {
+ PlayerMapInfoRequestEvent event = new PlayerMapInfoRequestEvent(this, mapItem);
+ getServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ ItemMap map = (ItemMap) mapItem;
+ if (map.trySendImage(this)) {
+ return;
+ }
+ try {
+ BufferedImage image = new BufferedImage(128, 128, BufferedImage.TYPE_INT_RGB);
+ Graphics2D graphics = image.createGraphics();
+
+ int worldX = (Math.floorDiv(this.getFloorX(), 128)) << 7;
+ int worldZ = (Math.floorDiv(this.getFloorZ(), 128)) << 7;
+ for (int x = 0; x < 128; x++) {
+ int avgY = 0;
+ for (int y = -1; y < 128; y++) {
+ if (this.getLevel().getDimension() == Level.DIMENSION_NETHER) {
+ if (y == -1) {
+ continue;
+ }
+ graphics.setColor(colorizeMapColor(new SplittableRandom((((long) (worldZ + y) & 0x3ffffff) << 26) + ((long) (worldX + x) & 0x3ffffff)).nextBoolean() ? BlockColor.STONE_BLOCK_COLOR : BlockColor.DIRT_BLOCK_COLOR, 1));
+ } else {
+ if (y == -1) { // Hack: Make sure we have average world height for the first row
+ avgY = this.getLevel().getHighestBlockAt(worldX + x, worldZ, false);
+ continue;
+ }
- if (mapItem == null) {
- for (BlockEntity be : this.level.getBlockEntities().values()) {
- if (be instanceof BlockEntityItemFrame) {
- BlockEntityItemFrame itemFrame1 = (BlockEntityItemFrame) be;
+ int worldY = this.getLevel().getHighestBlockAt(worldX + x, worldZ + y, false);
+ double avgYDifference = (worldY - avgY) * 4 / 5 + ((x + y & 1) - 0.5) * 0.4; // 4d / 5d would provide more detail but is that better?
+ int colorDepth = 1;
+ if (avgYDifference > 0.6) {
+ colorDepth = 2;
+ }
+ if (avgYDifference < -0.6) {
+ colorDepth = 0;
+ }
+ avgY = worldY;
+ graphics.setColor(colorizeMapColor(this.getLevel().getMapColorAt(worldX + x, worldY, worldZ + y), colorDepth));
+ }
- if (itemFrame1.getItem() instanceof ItemMap && ((ItemMap) itemFrame1.getItem()).getMapId() == pk.mapId) {
- ((ItemMap) itemFrame1.getItem()).sendImage(this);
- break;
+ graphics.fillRect(x, y, x + 1, y + 1);
}
}
+
+ map.setImage(image);
+ map.sendImage(this);
+ } catch (Exception ex) {
+ this.getServer().getLogger().debug(username + ": there was an error while generating map image", ex);
}
}
+ }
+
+ return;
+ case ProtocolInfo.INVENTORY_TRANSACTION_PACKET:
+ if (this.isSpectator()) {
+ this.needSendInventory = true;
+ return;
+ }
- if (mapItem != null) {
- PlayerMapInfoRequestEvent event;
- getServer().getPluginManager().callEvent(event = new PlayerMapInfoRequestEvent(this, mapItem));
+ InventoryTransactionPacket transactionPacket = (InventoryTransactionPacket) packet;
- if (!event.isCancelled()) {
- ((ItemMap) mapItem).sendImage(this);
- }
- }
+ if ((transactionPacket.transactionType == InventoryTransactionPacket.TYPE_MISMATCH ||
+ (transactionPacket.transactionType == InventoryTransactionPacket.TYPE_NORMAL && this.isCreative() && Arrays.stream(transactionPacket.actions).anyMatch(action -> action.sourceType == NetworkInventoryAction.SOURCE_TODO)))
+ && (inv = getWindowById(SMITHING_WINDOW_ID)) instanceof SmithingInventory) {
- break;
- case ProtocolInfo.LEVEL_SOUND_EVENT_PACKET_V1:
- case ProtocolInfo.LEVEL_SOUND_EVENT_PACKET_V2:
- case ProtocolInfo.LEVEL_SOUND_EVENT_PACKET:
- if (!this.isSpectator()) {
- this.level.addChunkPacket(this.getChunkX(), this.getChunkZ(), packet);
- }
- break;
- case ProtocolInfo.INVENTORY_TRANSACTION_PACKET:
- if (this.isSpectator()) {
- this.sendAllInventories();
- break;
- }
+ SmithingInventory smithingInventory = (SmithingInventory) inv;
+ if (!smithingInventory.getResult().isNull()) {
+ InventoryTransactionPacket fixedPacket = new InventoryTransactionPacket();
+ fixedPacket.isRepairItemPart = true;
+ fixedPacket.actions = new NetworkInventoryAction[6];
+
+ Item fromIngredient = smithingInventory.getIngredient().clone();
+ Item toIngredient = fromIngredient.decrement(1);
+
+ Item fromEquipment = smithingInventory.getEquipment().clone();
+ Item toEquipment = fromEquipment.decrement(1);
+
+ Item fromResult = Item.get(Item.AIR);
+ Item toResult = smithingInventory.getResult().clone();
+
+ NetworkInventoryAction action = new NetworkInventoryAction();
+ action.windowId = ContainerIds.UI;
+ action.inventorySlot = SmithingInventory.SMITHING_INGREDIENT_UI_SLOT;
+ action.oldItem = fromIngredient.clone();
+ action.newItem = toIngredient.clone();
+ fixedPacket.actions[0] = action;
+
+ action = new NetworkInventoryAction();
+ action.windowId = ContainerIds.UI;
+ action.inventorySlot = SmithingInventory.SMITHING_EQUIPMENT_UI_SLOT;
+ action.oldItem = fromEquipment.clone();
+ action.newItem = toEquipment.clone();
+ fixedPacket.actions[1] = action;
- InventoryTransactionPacket transactionPacket = (InventoryTransactionPacket) packet;
+ if (this.getLoginChainData().getUIProfile() == 0) {
+ // We can't know whether shift click was used so we must make sure we won't overwrite item in cursor inventory
+ Item[] drops = this.inventory.addItem(this.playerUIInventory.getItemFast(0)); // Cloned in addItem
+ this.playerUIInventory.getCursorInventory().clear(0);
- List actions = new ArrayList<>();
- for (NetworkInventoryAction networkInventoryAction : transactionPacket.actions) {
- InventoryAction a = networkInventoryAction.createInventoryAction(this);
+ for (Item drop : drops) {
+ this.level.dropItem(this, drop);
+ }
- if (a == null) {
- this.getServer().getLogger().debug("Unmatched inventory action from " + this.getName() + ": " + networkInventoryAction);
- this.sendAllInventories();
- break packetswitch;
+ action = new NetworkInventoryAction();
+ action.windowId = ContainerIds.UI;
+ action.inventorySlot = 0; // cursor
+ action.oldItem = Item.get(Item.AIR);
+ action.newItem = toResult.clone();
+ fixedPacket.actions[2] = action;
+ } else {
+ int emptyPlayerSlot = -1;
+ for (int slot = 0; slot < inventory.getSize(); slot++) {
+ if (inventory.getItemFast(slot).isNull()) {
+ emptyPlayerSlot = slot;
+ break;
+ }
+ }
+ if (emptyPlayerSlot == -1) {
+ this.needSendInventory = true;
+ return;
+ } else {
+ action = new NetworkInventoryAction();
+ action.windowId = ContainerIds.INVENTORY;
+ action.inventorySlot = emptyPlayerSlot;
+ action.oldItem = Item.get(Item.AIR);
+ action.newItem = toResult.clone();
+ fixedPacket.actions[2] = action;
+ }
}
- actions.add(a);
+ action = new NetworkInventoryAction();
+ action.sourceType = NetworkInventoryAction.SOURCE_TODO;
+ action.windowId = NetworkInventoryAction.SOURCE_TYPE_ANVIL_RESULT;
+ action.inventorySlot = 2; // result
+ action.oldItem = toResult.clone();
+ action.newItem = fromResult.clone();
+ fixedPacket.actions[3] = action;
+
+ action = new NetworkInventoryAction();
+ action.sourceType = NetworkInventoryAction.SOURCE_TODO;
+ action.windowId = NetworkInventoryAction.SOURCE_TYPE_ANVIL_INPUT;
+ action.inventorySlot = 0; // equipment
+ action.oldItem = toEquipment.clone();
+ action.newItem = fromEquipment.clone();
+ fixedPacket.actions[4] = action;
+
+ action = new NetworkInventoryAction();
+ action.sourceType = NetworkInventoryAction.SOURCE_TODO;
+ action.windowId = NetworkInventoryAction.SOURCE_TYPE_ANVIL_MATERIAL;
+ action.inventorySlot = 1; // material
+ action.oldItem = toIngredient.clone();
+ action.newItem = fromIngredient.clone();
+ fixedPacket.actions[5] = action;
+
+ transactionPacket = fixedPacket;
+ }
+ }
+
+ List actions = new ArrayList<>();
+ for (NetworkInventoryAction networkInventoryAction : transactionPacket.actions) {
+ InventoryAction a = networkInventoryAction.createInventoryAction(this);
+
+ if (a == null) {
+ this.getServer().getLogger().debug("Unmatched inventory action from " + this.username + ": " + networkInventoryAction);
+ this.needSendInventory = true;
+ return;
}
- if (transactionPacket.isCraftingPart) {
- if (this.craftingTransaction == null) {
- this.craftingTransaction = new CraftingTransaction(this, actions);
+ actions.add(a);
+ }
+
+ if (transactionPacket.isCraftingPart) {
+ if (LoomTransaction.checkForItemPart(actions)) {
+ if (this.loomTransaction == null) {
+ this.loomTransaction = new LoomTransaction(this, actions);
} else {
for (InventoryAction action : actions) {
- this.craftingTransaction.addAction(action);
+ this.loomTransaction.addAction(action);
+ }
+ }
+ if (this.loomTransaction.canExecute()) {
+ if (this.loomTransaction.execute()) {
+ level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_LOOM_USE);
}
}
+ this.loomTransaction = null;
+ return;
+ }
- if (this.craftingTransaction.getPrimaryOutput() != null && this.craftingTransaction.canExecute()) {
- //we get the actions for this in several packets, so we can't execute it until we get the result
+ if (this.craftingTransaction == null) {
+ this.craftingTransaction = new CraftingTransaction(this, actions);
+ } else {
+ for (InventoryAction action : actions) {
+ this.craftingTransaction.addAction(action);
+ }
+ }
+ if (this.craftingTransaction.getPrimaryOutput() != null && this.craftingTransaction.canExecute()) {
+ try {
this.craftingTransaction.execute();
- this.craftingTransaction = null;
+ } catch (Exception e) {
+ this.server.getLogger().debug(username + ": executing crafting transaction failed");
}
-
- return;
- } else if (transactionPacket.isEnchantingPart) {
- if (this.enchantTransaction == null) {
- this.enchantTransaction = new EnchantTransaction(this, actions);
+ this.craftingTransaction = null;
+ }
+ return;
+ } else if (transactionPacket.isEnchantingPart) {
+ if (this.enchantTransaction == null) {
+ this.enchantTransaction = new EnchantTransaction(this, actions);
+ } else {
+ for (InventoryAction action : actions) {
+ this.enchantTransaction.addAction(action);
+ }
+ }
+ if (this.enchantTransaction.canExecute()) {
+ this.enchantTransaction.execute();
+ this.enchantTransaction = null;
+ }
+ return;
+ } else if (transactionPacket.isRepairItemPart) {
+ if (SmithingTransaction.checkForItemPart(actions)) {
+ if (this.smithingTransaction == null) {
+ this.smithingTransaction = new SmithingTransaction(this, actions);
} else {
for (InventoryAction action : actions) {
- this.enchantTransaction.addAction(action);
+ this.smithingTransaction.addAction(action);
}
}
- if (this.enchantTransaction.canExecute()) {
- this.enchantTransaction.execute();
- this.enchantTransaction = null;
+ if (this.smithingTransaction.canExecute()) {
+ if (this.smithingTransaction.execute()) {
+ Collection players = level.getChunkPlayers(getChunkX(), getChunkZ()).values();
+ players.remove(this);
+ if (!players.isEmpty()) {
+ level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_SMITHING_TABLE_USE);
+ }
+ }
+ this.smithingTransaction = null;
}
- return;
- } else if (transactionPacket.isRepairItemPart) {
+ } else {
if (this.repairItemTransaction == null) {
this.repairItemTransaction = new RepairItemTransaction(this, actions);
} else {
@@ -3133,493 +4104,740 @@ public void onCompletion(Server server) {
this.repairItemTransaction.execute();
this.repairItemTransaction = null;
}
- return;
- } else if (this.craftingTransaction != null) {
- if (craftingTransaction.checkForCraftingPart(actions)) {
- for (InventoryAction action : actions) {
- craftingTransaction.addAction(action);
- }
+ }
+ return;
+ } else if (this.craftingTransaction != null) {
+ if (craftingTransaction.checkForCraftingPart(actions)) {
+ if (craftingType == CRAFTING_LOOM) {
+ craftingTransaction = null;
return;
- } else {
- this.server.getLogger().debug("Got unexpected normal inventory action with incomplete crafting transaction from " + this.getName() + ", refusing to execute crafting");
- this.removeAllWindows(false);
- this.sendAllInventories();
- this.craftingTransaction = null;
}
- } else if (this.enchantTransaction != null) {
- if (enchantTransaction.checkForEnchantPart(actions)) {
- for (InventoryAction action : actions) {
- enchantTransaction.addAction(action);
- }
- return;
- } else {
- this.server.getLogger().debug("Got unexpected normal inventory action with incomplete enchanting transaction from " + this.getName() + ", refusing to execute enchant " + transactionPacket.toString());
- this.removeAllWindows(false);
- this.sendAllInventories();
- this.enchantTransaction = null;
+ for (InventoryAction action : actions) {
+ craftingTransaction.addAction(action);
}
- } else if (this.repairItemTransaction != null) {
- if (RepairItemTransaction.checkForRepairItemPart(actions)) {
- for (InventoryAction action : actions) {
- this.repairItemTransaction.addAction(action);
- }
- return;
- } else {
- this.server.getLogger().debug("Got unexpected normal inventory action with incomplete repair item transaction from " + this.getName() + ", refusing to execute repair item " + transactionPacket.toString());
- this.removeAllWindows(false);
- this.sendAllInventories();
- this.repairItemTransaction = null;
+ return;
+ } else {
+ this.server.getLogger().debug("Got unexpected normal inventory action with incomplete crafting transaction from " + this.username + ", refusing to execute crafting");
+ this.removeAllWindows(false);
+ this.needSendInventory = true;
+ this.craftingTransaction = null;
+ }
+ } else if (this.enchantTransaction != null) {
+ if (enchantTransaction.checkForEnchantPart(actions)) {
+ for (InventoryAction action : actions) {
+ enchantTransaction.addAction(action);
+ }
+ return;
+ } else {
+ this.server.getLogger().debug("Got unexpected normal inventory action with incomplete enchanting transaction from " + this.username + ", refusing to execute enchant " + transactionPacket.toString());
+ this.removeAllWindows(false);
+ this.enchantTransaction = null;
+ this.needSendInventory = true;
+ }
+ } else if (this.repairItemTransaction != null) {
+ if (RepairItemTransaction.checkForRepairItemPart(actions)) {
+ for (InventoryAction action : actions) {
+ this.repairItemTransaction.addAction(action);
}
+ return;
+ } else {
+ this.server.getLogger().debug("Got unexpected normal inventory action with incomplete repair item transaction from " + this.username + ", refusing to execute repair item " + transactionPacket.toString());
+ this.removeAllWindows(false);
+ this.repairItemTransaction = null;
+ this.needSendInventory = true;
}
+ } else if (this.smithingTransaction != null) {
+ if (SmithingTransaction.checkForItemPart(actions)) {
+ for (InventoryAction action : actions) {
+ this.smithingTransaction.addAction(action);
+ }
+ return;
+ } else {
+ this.server.getLogger().debug("Got unexpected normal inventory action with incomplete repair item transaction from " + this.username + ", refusing to execute smithing " + transactionPacket.toString());
+ this.removeAllWindows(false);
+ this.smithingTransaction = null;
+ this.needSendInventory = true;
+ }
+ }
- switch (transactionPacket.transactionType) {
- case InventoryTransactionPacket.TYPE_NORMAL:
- InventoryTransaction transaction = new InventoryTransaction(this, actions);
+ switch (transactionPacket.transactionType) {
+ case InventoryTransactionPacket.TYPE_NORMAL:
+ InventoryTransaction transaction = new InventoryTransaction(this, actions);
- if (!transaction.execute()) {
- this.server.getLogger().debug("Failed to execute inventory transaction from " + this.getName() + " with actions: " + Arrays.toString(transactionPacket.actions));
- break packetswitch; //oops!
- }
+ if (!transaction.execute()) {
+ this.server.getLogger().debug("Failed to execute inventory transaction from " + this.username + " with actions: " + Arrays.toString(transactionPacket.actions));
+ return;
+ }
- //TODO: fix achievement for getting iron from furnace
+ return;
+ 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.needSendInventory = true;
+ return;
+ case InventoryTransactionPacket.TYPE_USE_ITEM:
+ UseItemData useItemData = (UseItemData) transactionPacket.transactionData;
+ BlockVector3 blockVector = useItemData.blockPos;
+ BlockFace face = useItemData.face;
+ int type = useItemData.actionType;
- 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();
+ this.setShieldBlockingDelay(5);
- break packetswitch;
- case InventoryTransactionPacket.TYPE_USE_ITEM:
- UseItemData useItemData = (UseItemData) transactionPacket.transactionData;
+ boolean itemSent = false; // Fix inventory desync but only send the slot once
- BlockVector3 blockVector = useItemData.blockPos;
- face = useItemData.face;
+ if (inventory.getHeldItemIndex() != useItemData.hotbarSlot) {
+ inventory.equipItem(useItemData.hotbarSlot);
- int type = useItemData.actionType;
- switch (type) {
- case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_BLOCK:
- // Remove if client bug is ever fixed
- boolean spamBug = (lastRightClickPos != null && System.currentTimeMillis() - lastRightClickTime < 100.0 && blockVector.distanceSquared(lastRightClickPos) < 0.00001);
- lastRightClickPos = blockVector.asVector3();
- lastRightClickTime = System.currentTimeMillis();
- if (spamBug) {
- return;
- }
+ itemSent = true; // Assume that the item is still correct even if the selected slot is not
+ }
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false);
+ switch (type) {
+ case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_BLOCK:
+ // Hack: Fix client spamming right clicks
+ if ((lastRightClickPos != null && this.getInventory().getItemInHandFast().getBlockId() == BlockID.AIR && System.currentTimeMillis() - lastRightClickTime < 200.0 && blockVector.distanceSquared(lastRightClickPos) < 0.00001)) {
+ return;
+ }
- 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()) {
- if (oldItem.getId() == i.getId() || i.getId() == 0) {
- inventory.setItemInHand(i);
- } else {
- server.getLogger().debug("Tried to set item " + i.getId() + " but " + this.username + " had item " + oldItem.getId() + " in their hand slot");
- }
- inventory.sendHeldItem(this.getViewers().values());
+ lastRightClickPos = blockVector.asVector3();
+ lastRightClickTime = System.currentTimeMillis();
+
+ this.breakingBlock = null;
+
+ this.setUsingItem(false);
+
+ // We don't seem to verify useItemData.clickPos so don't use it for anything important
+ if (this.canInteract(blockVector.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7)) {
+ Item i = inventory.getItemInHand();
+ if (this.isCreative()) {
+ if (this.level.useItemOn(blockVector.asVector3(), i, face, useItemData.clickPos.x, useItemData.clickPos.y, useItemData.clickPos.z, this) != null) {
+ return;
+ }
+ } else {
+ Item oldItem = i.clone(); // This must be cloned
+
+ if ((i = this.level.useItemOn(blockVector.asVector3(), i, face, useItemData.clickPos.x, useItemData.clickPos.y, useItemData.clickPos.z, this)) != null) {
+ if (i.getCount() != oldItem.getCount() || i.getDamage() != oldItem.getDamage() || !i.equals(oldItem)) { // Quick checks first
+ if (oldItem.getId() == i.getId() || i.getId() == 0) {
+ inventory.setItemInHand(i);
+
+ itemSent = true;
+ } else if (Nukkit.DEBUG > 1) {
+ server.getLogger().debug("Tried to set item " + i.getId() + " but " + this.username + " had item " + oldItem.getId() + " in their hand slot");
}
- break packetswitch;
}
+
+ if (!itemSent && !oldItem.equals(useItemData.itemInHand)) {
+ this.needSendHeldItem = true;
+ }
+ return;
}
}
+ }
- inventory.sendHeldItem(this);
+ this.needSendHeldItem = true;
- if (blockVector.distanceSquared(this) > 10000) {
- break packetswitch;
- }
+ if (blockVector.distanceSquared(this) > 10000) {
+ return;
+ }
- Block target = this.level.getBlock(blockVector.asVector3());
- block = target.getSide(face);
+ 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);
- break packetswitch;
- case InventoryTransactionPacket.USE_ITEM_ACTION_BREAK_BLOCK:
- if (!this.spawned || !this.isAlive()) {
- break packetswitch;
- }
+ this.level.sendBlocks(this, new Block[]{target, block}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
- System.out.println("USE_ITEM_ACTION_BREAK_BLOCK");
+ if (target instanceof BlockDoor) {
+ BlockDoor door = (BlockDoor) target;
- this.resetCraftingGridType();
+ Block part;
- Item i = this.getInventory().getItemInHand();
+ if ((door.getDamage() & 0x08) > 0) {
+ part = target.down();
- Item oldItem = i.clone();
+ if (part.getId() == target.getId()) {
+ target = part;
- if (this.canInteract(blockVector.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7) && (i = this.level.useBreakOn(blockVector.asVector3(), face, i, this, true)) != null) {
- if (this.isSurvival()) {
- this.getFoodData().updateFoodExpLevel(0.005);
- if (!i.equals(oldItem) || i.getCount() != oldItem.getCount()) {
- if (oldItem.getId() == i.getId() || i.getId() == 0) {
- inventory.setItemInHand(i);
- } else {
- server.getLogger().debug("Tried to set item " + i.getId() + " but " + this.username + " had item " + oldItem.getId() + " in their hand slot");
- }
- inventory.sendHeldItem(this.getViewers().values());
- }
+ this.level.sendBlocks(this, new Block[]{target}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
}
- break packetswitch;
}
+ }
+ return;
+ case InventoryTransactionPacket.USE_ITEM_ACTION_BREAK_BLOCK:
+ return;
+ case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_AIR:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- inventory.sendContents(this);
- inventory.sendHeldItem(this);
-
- if (blockVector.distanceSquared(this) < 10000) {
- target = this.level.getBlock(blockVector.asVector3());
- this.level.sendBlocks(new Player[]{this}, new Block[]{target}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ if (inventory.getHeldItemIndex() != useItemData.hotbarSlot) {
+ this.inventory.equipItem(useItemData.hotbarSlot);
- BlockEntity blockEntity = this.level.getBlockEntity(blockVector.asVector3());
- if (blockEntity instanceof BlockEntitySpawnable) {
- ((BlockEntitySpawnable) blockEntity).spawnTo(this);
- }
- }
+ this.crossbowLoadTick = 0;
+ }
- break packetswitch;
- case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_AIR:
- Vector3 directionVector = this.getDirectionVector();
+ item = this.inventory.getItemInHand();
- 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();
- }
+ this.breakingBlock = null;
- PlayerInteractEvent interactEvent = new PlayerInteractEvent(this, item, directionVector, face, Action.RIGHT_CLICK_AIR);
+ Vector3 directionVector = this.getDirectionVector();
+ PlayerInteractEvent interactEvent = new PlayerInteractEvent(this, item, directionVector, face, Action.RIGHT_CLICK_AIR);
+ this.server.getPluginManager().callEvent(interactEvent);
- this.server.getPluginManager().callEvent(interactEvent);
+ if (interactEvent.isCancelled()) {
+ this.needSendHeldItem = true;
+ return;
+ }
- if (interactEvent.isCancelled()) {
- this.inventory.sendHeldItem(this);
- break packetswitch;
+ if (item instanceof ItemCrossbow) {
+ ItemCrossbow crossbow = ((ItemCrossbow) item);
+ if (crossbow.isLoaded()) {
+ if (this.crossbowLoadTick + 5 < this.server.getTick()) {
+ crossbow.launchArrow(this);
+ }
+ } else {
+ if (this.isUsingItem()) {
+ // Used item
+ int ticksUsed = this.server.getTick() - this.startAction;
+ this.crossbowLoadTick = this.server.getTick();
+ this.setUsingItem(false);
+ item.onUse(this, ticksUsed); // Load crossbow
+ } else {
+ this.setUsingItem(true);
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_CROSSBOW_LOADING_START);
+ }
}
+ return;
+ }
- if (item.onClickAir(this, directionVector)) {
- if (!this.isCreative()) {
- if (item.getId() == 0 || this.inventory.getItemInHand().getId() == item.getId()) {
- this.inventory.setItemInHand(item);
- } else {
- server.getLogger().debug("Tried to set item " + item.getId() + " but " + this.username + " had item " + this.inventory.getItemInHand().getId() + " in their hand slot");
+ int oldCount = item.getCount();
+ int oldDamage = item.getDamage();
+ if (item.onClickAir(this, directionVector)) {
+ if (this.isSurvival() || this.isAdventure()) {
+ // Don't set the item if not changed
+ // Update this to use equals() if NBT is ever modified in onClickAir
+ if (item.getId() == 0 || ((item.getCount() != oldCount || item.getDamage() != oldDamage) && this.inventory.getItemInHandFast().getId() == item.getId())) {
+ if (item instanceof ItemFishingRod && item.getDamage() >= item.getMaxDurability()) {
+ this.level.addSound(this, Sound.RANDOM_BREAK);
+ this.level.addParticle(new ItemBreakParticle(this, item));
+ item = Item.get(Item.AIR);
}
- }
- if (!this.isUsingItem()) {
- this.setUsingItem(true);
- break packetswitch;
+ this.inventory.setItemInHand(item);
}
+ }
+ if (this.isUsingItem()) {
// Used item
int ticksUsed = this.server.getTick() - this.startAction;
this.setUsingItem(false);
-
if (!item.onUse(this, ticksUsed)) {
- this.inventory.sendContents(this);
+ this.needSendHeldItem = true;
}
+ } else {
+ this.setUsingItem(true);
}
+ }
- 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;
- }
+ }
+ return;
+ case InventoryTransactionPacket.TYPE_USE_ITEM_ON_ENTITY:
+ UseItemOnEntityData useItemOnEntityData = (UseItemOnEntityData) transactionPacket.transactionData;
- type = useItemOnEntityData.actionType;
+ Entity target = this.level.getEntity(useItemOnEntityData.entityRuntimeId);
+ if (target == null) {
+ return;
+ }
- if (!useItemOnEntityData.itemInHand.equalsExact(this.inventory.getItemInHand())) {
- this.inventory.sendHeldItem(this);
- }
+ type = useItemOnEntityData.actionType;
- item = this.inventory.getItemInHand();
+ if (inventory.getHeldItemIndex() != useItemOnEntityData.hotbarSlot) {
+ inventory.equipItem(useItemOnEntityData.hotbarSlot);
+ }
- switch (type) {
- case InventoryTransactionPacket.USE_ITEM_ON_ENTITY_ACTION_INTERACT:
- PlayerInteractEntityEvent playerInteractEntityEvent = new PlayerInteractEntityEvent(this, target, item, useItemOnEntityData.clickPos);
- if (this.isSpectator()) playerInteractEntityEvent.setCancelled();
- getServer().getPluginManager().callEvent(playerInteractEntityEvent);
+ item = this.inventory.getItemInHand();
- if (playerInteractEntityEvent.isCancelled()) {
- break;
- }
- if (target.onInteract(this, item, useItemOnEntityData.clickPos) && this.isSurvival()) {
- if (item.isTool()) {
- if (item.useOn(target) && item.getDamage() >= item.getMaxDurability()) {
- item = new ItemBlock(Block.get(BlockID.AIR));
- }
- } else {
- if (item.count > 1) {
- item.count--;
- } else {
- item = new ItemBlock(Block.get(BlockID.AIR));
- }
- }
+ switch (type) {
+ case InventoryTransactionPacket.USE_ITEM_ON_ENTITY_ACTION_INTERACT:
+ if (this.distanceSquared(target) > 1000) {
+ this.getServer().getLogger().debug(username + ": target entity is too far away");
+ return;
+ }
- if (item.getId() == 0 || this.inventory.getItemInHand().getId() == item.getId()) {
- this.inventory.setItemInHand(item);
- } else {
- server.getLogger().debug("Tried to set item " + item.getId() + " but " + this.username + " had item " + this.inventory.getItemInHand().getId() + " in their hand slot");
+ this.breakingBlock = null;
+
+ this.setUsingItem(false);
+
+ PlayerInteractEntityEvent playerInteractEntityEvent = new PlayerInteractEntityEvent(this, target, item, useItemOnEntityData.clickPos);
+ if (this.isSpectator()) playerInteractEntityEvent.setCancelled();
+ getServer().getPluginManager().callEvent(playerInteractEntityEvent);
+
+ if (playerInteractEntityEvent.isCancelled()) {
+ return;
+ }
+
+ if (target.onInteract(this, item, useItemOnEntityData.clickPos) && (this.isSurvival() || this.isAdventure())) {
+ if (item.isTool()) {
+ if (item.useOn(target) && item.getDamage() >= item.getMaxDurability()) {
+ level.addSound(this, Sound.RANDOM_BREAK);
+ level.addParticle(new ItemBreakParticle(this, item));
+ item = Item.get(Item.AIR);
}
- }
- break;
- case InventoryTransactionPacket.USE_ITEM_ON_ENTITY_ACTION_ATTACK:
- 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")) {
- break;
+ } else {
+ if (item.count > 1) {
+ item.count--;
+ } else {
+ item = Item.get(Item.AIR);
}
}
- Enchantment[] enchantments = item.getEnchantments();
+ if (item.getId() == 0 || this.inventory.getItemInHandFast().getId() == item.getId()) {
+ this.inventory.setItemInHand(item);
+ } else if (Nukkit.DEBUG > 1) {
+ server.getLogger().debug("Tried to set item " + item.getId() + " but " + this.username + " had item " + this.inventory.getItemInHandFast().getId() + " in their hand slot");
+ }
+ }
+ return;
+ case InventoryTransactionPacket.USE_ITEM_ON_ENTITY_ACTION_ATTACK:
+ if (target.getId() == this.getId()) {
+ this.kick(PlayerKickEvent.Reason.INVALID_PVP, "Tried to attack invalid player");
+ return;
+ }
- float itemDamage = item.getAttackDamage();
- for (Enchantment enchantment : enchantments) {
- itemDamage += enchantment.getDamageBonus(target);
+ if (!this.canInteractEntity(target, isCreative() ? 64 : 25)) { // 8 : 5
+ return;
+ } else if (target instanceof Player) {
+ if ((((Player) target).gamemode & 0x01) > 0) {
+ return;
+ } else if (!this.server.pvpEnabled) {
+ return;
}
+ }
- Map damage = new EnumMap<>(DamageModifier.class);
- damage.put(DamageModifier.BASE, itemDamage);
+ this.breakingBlock = null;
- float knockBack = 0.3f;
- Enchantment knockBackEnchantment = item.getEnchantment(Enchantment.ID_KNOCKBACK);
- if (knockBackEnchantment != null) {
- knockBack += knockBackEnchantment.getLevel() * 0.1f;
- }
+ this.setUsingItem(false);
+
+ this.setShieldBlockingDelay(5);
+
+ if (server.attackStopSprint) {
+ this.setSprinting(false);
+ }
+
+ Enchantment[] enchantments = item.getEnchantments();
- EntityDamageByEntityEvent entityDamageByEntityEvent = new EntityDamageByEntityEvent(this, target, DamageCause.ENTITY_ATTACK, damage, knockBack, enchantments);
- if (this.isSpectator()) entityDamageByEntityEvent.setCancelled();
- if ((target instanceof Player) && !this.level.getGameRules().getBoolean(GameRule.PVP)) {
- entityDamageByEntityEvent.setCancelled();
+ float itemDamage = item.getAttackDamage();
+ for (Enchantment enchantment : enchantments) {
+ itemDamage += enchantment.getDamageBonus(target);
+ }
+
+ Map damage = new EnumMap<>(DamageModifier.class);
+ damage.put(DamageModifier.BASE, itemDamage);
+
+ float knockBack = 0.3f;
+ Enchantment knockBackEnchantment = item.getEnchantment(Enchantment.ID_KNOCKBACK);
+ if (knockBackEnchantment != null) {
+ knockBack += knockBackEnchantment.getLevel() * 0.1f;
+ }
+
+ EntityDamageByEntityEvent entityDamageByEntityEvent = new EntityDamageByEntityEvent(this, target, DamageCause.ENTITY_ATTACK, damage, knockBack, enchantments);
+ entityDamageByEntityEvent.setWeapon(item);
+
+ if (this.isSpectator()) {
+ entityDamageByEntityEvent.setCancelled();
+ }
+ if ((target instanceof Player) && !this.level.getGameRules().getBoolean(GameRule.PVP)) {
+ entityDamageByEntityEvent.setCancelled();
+ }
+
+ if (!target.attack(entityDamageByEntityEvent)) {
+ if (item.isTool() && !this.isCreative()) {
+ this.needSendHeldItem = true;
}
+ return;
+ }
- if (!target.attack(entityDamageByEntityEvent)) {
- if (item.isTool() && this.isSurvival()) {
- this.inventory.sendContents(this);
+ for (Enchantment enchantment : item.getEnchantments()) {
+ enchantment.doPostAttack(this, target);
+ }
+
+ if (item.isTool() && !this.isCreative()) {
+ if (item.useOn(target) && item.getDamage() >= item.getMaxDurability()) {
+ level.addSound(this, Sound.RANDOM_BREAK);
+ level.addParticle(new ItemBreakParticle(this, item));
+ this.inventory.clear(this.inventory.getHeldItemIndex(), true);
+ } else {
+ if (item.getId() == 0 || this.inventory.getItemInHandFast().getId() == item.getId()) {
+ this.inventory.setItemInHand(item);
+ } else if (Nukkit.DEBUG > 1) {
+ server.getLogger().debug("Tried to set item " + item.getId() + " but " + this.username + " had item " + this.inventory.getItemInHandFast().getId() + " in their hand slot");
}
- break;
}
+ }
+ return;
+ }
- for (Enchantment enchantment : item.getEnchantments()) {
- enchantment.doPostAttack(this, target);
- }
+ return;
+ case InventoryTransactionPacket.TYPE_RELEASE_ITEM:
+ if (this.isSpectator()) {
+ this.needSendInventory = true;
+ return;
+ }
+ ReleaseItemData releaseItemData = (ReleaseItemData) transactionPacket.transactionData;
- if (item.isTool() && (this.isSurvival() || this.isAdventure())) {
- if (item.useOn(target) && item.getDamage() >= item.getMaxDurability()) {
- this.inventory.setItemInHand(Item.get(0));
- } else {
- if (item.getId() == 0 || this.inventory.getItemInHand().getId() == item.getId()) {
- this.inventory.setItemInHand(item);
- } else {
- server.getLogger().debug("Tried to set item " + item.getId() + " but " + this.username + " had item " + this.inventory.getItemInHand().getId() + " in their hand slot");
- }
+ try {
+ type = releaseItemData.actionType;
+ switch (type) {
+ case InventoryTransactionPacket.RELEASE_ITEM_ACTION_RELEASE:
+ if (this.isUsingItem()) {
+ int ticksUsed = this.server.getTick() - this.startAction;
+ if (!this.inventory.getItemInHand().onRelease(this, ticksUsed)) {
+ this.needSendHeldItem = true;
}
+ this.setUsingItem(false);
+ } else {
+ this.needSendHeldItem = true;
}
return;
+ case InventoryTransactionPacket.RELEASE_ITEM_ACTION_CONSUME:
+ return;
default:
- break; //unknown
+ this.getServer().getLogger().debug(username + ": unknown release item action type: " + type);
}
+ } finally {
+ this.setUsingItem(false);
+ }
+ return;
+ default:
+ this.needSendHeldItem = true;
+ }
+ return;
+ case ProtocolInfo.PLAYER_HOTBAR_PACKET:
+ if (this.inventory == null) {
+ this.getServer().getLogger().debug(username + ": got PlayerHotbarPacket but inventory was null");
+ return;
+ }
- break;
- case InventoryTransactionPacket.TYPE_RELEASE_ITEM:
- if (this.isSpectator()) {
- this.sendAllInventories();
- break packetswitch;
- }
- ReleaseItemData releaseItemData = (ReleaseItemData) transactionPacket.transactionData;
+ PlayerHotbarPacket hotbarPacket = (PlayerHotbarPacket) packet;
- try {
- type = releaseItemData.actionType;
- switch (type) {
- case InventoryTransactionPacket.RELEASE_ITEM_ACTION_RELEASE:
- if (this.isUsingItem()) {
- item = this.inventory.getItemInHand();
+ if (hotbarPacket.windowId != ContainerIds.INVENTORY) {
+ return;
+ }
- int ticksUsed = this.server.getTick() - this.startAction;
- if (!item.onRelease(this, ticksUsed)) {
- this.inventory.sendContents(this);
- }
+ this.inventory.equipItem(hotbarPacket.selectedHotbarSlot);
+ this.setUsingItem(false);
+ return;
+ 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);
+ });
+ }
+ return;
+ case ProtocolInfo.SET_LOCAL_PLAYER_AS_INITIALIZED_PACKET:
+ if (this.locallyInitialized) {
+ return;
+ }
+ this.doFirstSpawn();
+ return;
+ case ProtocolInfo.RESPAWN_PACKET:
+ if (this.isAlive()) {
+ return;
+ }
- this.setUsingItem(false);
- } else {
- this.inventory.sendContents(this);
- }
- return;
- case InventoryTransactionPacket.RELEASE_ITEM_ACTION_CONSUME:
- log.debug("Unexpected release item action consume from {}", this::getName);
- return;
- default:
- break;
- }
- } finally {
- this.setUsingItem(false);
- }
- break;
- default:
- this.inventory.sendContents(this);
- break;
- }
- break;
- case ProtocolInfo.PLAYER_HOTBAR_PACKET:
- PlayerHotbarPacket hotbarPacket = (PlayerHotbarPacket) packet;
+ RespawnPacket respawnPacket = (RespawnPacket) packet;
+ if (respawnPacket.respawnState == RespawnPacket.STATE_CLIENT_READY_TO_SPAWN) {
+ RespawnPacket respawn1 = new RespawnPacket();
+ respawn1.x = (float) this.getX();
+ respawn1.y = (float) this.getY();
+ respawn1.z = (float) this.getZ();
+ respawn1.respawnState = RespawnPacket.STATE_READY_TO_SPAWN;
+ this.dataPacket(respawn1);
+ }
+ return;
+ case ProtocolInfo.BOOK_EDIT_PACKET:
+ if (!this.spawned) {
+ return;
+ }
- if (hotbarPacket.windowId != ContainerIds.INVENTORY) {
- return; //In PE this should never happen
- }
+ if (this.inventory == null) {
+ this.getServer().getLogger().debug(username + ": got BookEditPacket but inventory was null");
+ return;
+ }
- 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;
- case ProtocolInfo.RESPAWN_PACKET:
- if (this.isAlive()) {
+ BookEditPacket bookEditPacket = (BookEditPacket) packet;
+ Item oldBook = this.inventory.getItem(bookEditPacket.inventorySlot);
+ if (oldBook.getId() != Item.BOOK_AND_QUILL) {
+ this.getServer().getLogger().debug(username + ": BookEditPacket for invalid item: expected Book & Quill (386), got " + oldBook.getId());
+ return;
+ }
+
+ if (bookEditPacket.text != null && bookEditPacket.text.length() > 256) {
+ this.getServer().getLogger().debug(username + ": BookEditPacket with too long text");
+ return;
+ }
+
+ Item newBook = oldBook.clone();
+ boolean success;
+ switch (bookEditPacket.action) {
+ case REPLACE_PAGE:
+ success = ((ItemBookAndQuill) newBook).setPageText(bookEditPacket.pageNumber, bookEditPacket.text);
+ break;
+ case ADD_PAGE:
+ success = ((ItemBookAndQuill) newBook).insertPage(bookEditPacket.pageNumber, bookEditPacket.text);
+ break;
+ case DELETE_PAGE:
+ success = ((ItemBookAndQuill) newBook).deletePage(bookEditPacket.pageNumber);
+ break;
+ case SWAP_PAGES:
+ success = ((ItemBookAndQuill) newBook).swapPages(bookEditPacket.pageNumber, bookEditPacket.secondaryPageNumber);
break;
+ case SIGN_BOOK:
+ newBook = Item.get(Item.WRITTEN_BOOK, 0, 1, oldBook.getCompoundTag());
+ if (bookEditPacket.title == null || bookEditPacket.author == null || bookEditPacket.xuid == null || bookEditPacket.title.length() > 64 || bookEditPacket.author.length() > 64 || bookEditPacket.xuid.length() > 64) {
+ this.getServer().getLogger().debug(username + ": invalid BookEditPacket action SIGN_BOOK: title/author/xuid is too long");
+ return;
+ }
+ success = ((ItemBookWritten) newBook).signBook(bookEditPacket.title, bookEditPacket.author, bookEditPacket.xuid, ItemBookWritten.GENERATION_ORIGINAL);
+ break;
+ default:
+ this.getServer().getLogger().debug(username + ": BookEditPacket unknown action: " + bookEditPacket.action);
+ return;
+ }
+
+ if (success) {
+ PlayerEditBookEvent editBookEvent = new PlayerEditBookEvent(this, oldBook, newBook, bookEditPacket.action);
+ this.server.getPluginManager().callEvent(editBookEvent);
+ if (!editBookEvent.isCancelled()) {
+ this.inventory.setItem(bookEditPacket.inventorySlot, editBookEvent.getNewBook());
+ }
+ }
+ return;
+ case ProtocolInfo.FILTER_TEXT_PACKET:
+ if (!this.spawned) {
+ return;
+ }
+
+ FilterTextPacket filterTextPacket = (FilterTextPacket) packet;
+ if (filterTextPacket.text == null || filterTextPacket.text.length() > 64) {
+ this.getServer().getLogger().debug(username + ": FilterTextPacket with too long text");
+ return;
+ }
+ FilterTextPacket textResponsePacket = new FilterTextPacket();
+ textResponsePacket.text = filterTextPacket.text;
+ textResponsePacket.fromServer = true;
+ this.dataPacket(textResponsePacket);
+ return;
+ case ProtocolInfo.PACKET_VIOLATION_WARNING_PACKET:
+ PacketViolationWarningPacket PVWpk = (PacketViolationWarningPacket) packet;
+ if (pkIDs == null) {
+ pkIDs = Arrays.stream(ProtocolInfo.class.getDeclaredFields()).filter(field -> field.getType() == Byte.TYPE);
+ }
+ Optional PVWpkName = pkIDs
+ .filter(field -> {
+ try {
+ return field.getByte(null) == ((PacketViolationWarningPacket) packet).packetId;
+ } catch (IllegalAccessException e) {
+ return false;
+ }
+ }).map(Field::getName).findFirst();
+ this.getServer().getLogger().warning("PacketViolationWarningPacket" + PVWpkName.map(name -> " for " + name).orElse(" UNKNOWN") + " from " + this.username + ": " + PVWpk.toString());
+ return;
+ case ProtocolInfo.EMOTE_PACKET:
+ if (!this.spawned || server.getTick() - this.lastEmote < 20 || this.isSpectator()) {
+ return;
+ }
+ this.lastEmote = server.getTick();
+ EmotePacket emotePacket = (EmotePacket) packet;
+ if (emotePacket.runtimeId != this.id) {
+ this.getServer().getLogger().debug(username + ": EmotePacket eid mismatch");
+ return;
+ } else if (emotePacket.emoteID == null || emotePacket.emoteID.isEmpty() || emotePacket.emoteID.length() > 100) {
+ this.getServer().getLogger().debug(username + " EmotePacket invalid emote id: " + emotePacket.emoteID);
+ return;
+ }
+ for (Player player : this.getViewers().values()) {
+ player.dataPacket(emotePacket);
+ }
+ return;
+ case ProtocolInfo.LECTERN_UPDATE_PACKET:
+ if (!this.spawned) {
+ return;
+ }
+
+ LecternUpdatePacket lecternUpdatePacket = (LecternUpdatePacket) packet;
+ Vector3 lecternPos = lecternUpdatePacket.blockPosition.asVector3();
+ if (lecternPos.distanceSquared(this) > 4096) {
+ return;
+ }
+ if (lecternUpdatePacket.dropBook) {
+ Block blockLectern = this.getLevel().getBlock(chunk, lecternPos.getFloorX(), lecternPos.getFloorY(), lecternPos.getFloorZ(), false);
+ if (blockLectern instanceof BlockLectern) {
+ PlayerInteractEvent interactEvent = new PlayerInteractEvent(this, this.getInventory().getItemInHand(), blockLectern, BlockFace.UP, Action.RIGHT_CLICK_BLOCK);
+ server.getPluginManager().callEvent(interactEvent);
+ if (!interactEvent.isCancelled()) {
+ ((BlockLectern) blockLectern).dropBook();
+ }
}
- RespawnPacket respawnPacket = (RespawnPacket) packet;
- if (respawnPacket.respawnState == RespawnPacket.STATE_CLIENT_READY_TO_SPAWN) {
- RespawnPacket respawn1 = new RespawnPacket();
- respawn1.x = (float) this.getX();
- respawn1.y = (float) this.getY();
- respawn1.z = (float) this.getZ();
- respawn1.respawnState = RespawnPacket.STATE_READY_TO_SPAWN;
- this.dataPacket(respawn1);
+ } else {
+ BlockEntity blockEntityLectern = this.level.getBlockEntityIfLoaded(this.chunk, lecternPos);
+ if (blockEntityLectern instanceof BlockEntityLectern) {
+ BlockEntityLectern lectern = (BlockEntityLectern) blockEntityLectern;
+ if (lectern.getRawPage() != lecternUpdatePacket.page) {
+ lectern.setRawPage(lecternUpdatePacket.page);
+ lectern.spawnToAll();
+ }
}
- break;
- case ProtocolInfo.BOOK_EDIT_PACKET:
- BookEditPacket bookEditPacket = (BookEditPacket) packet;
- Item oldBook = this.inventory.getItem(bookEditPacket.inventorySlot);
- if (oldBook.getId() != Item.BOOK_AND_QUILL) {
- return;
+ }
+ return;
+ case ProtocolInfo.SET_DIFFICULTY_PACKET:
+ if (!this.spawned) {
+ return;
+ }
+
+ if (!this.hasPermission("nukkit.command.difficulty")) {
+ if (!this.isOp()) {
+ this.kick(PlayerKickEvent.Reason.INVALID_PACKET, "Invalid SetDifficultyPacket", true);
}
+ return;
+ }
+ server.setDifficulty(((SetDifficultyPacket) packet).difficulty);
+ Command.broadcastCommandMessage(this, new TranslationContainer("commands.difficulty.success", String.valueOf(server.getDifficulty())));
+
+ SetDifficultyPacket difficultyPacket = new SetDifficultyPacket();
+ difficultyPacket.difficulty = server.getDifficulty();
+ Server.broadcastPacket(server.getOnlinePlayers().values(), difficultyPacket);
+ return;
+ case ProtocolInfo.REQUEST_PERMISSIONS_PACKET:
+ if (!this.spawned) {
+ return;
+ }
+
+ if (!this.isOp()) {
+ this.kick(PlayerKickEvent.Reason.INVALID_PACKET, "Invalid RequestPermissionsPacket", true);
+ return;
+ }
+ this.sendMessage(TextFormat.RED + "Unimplemented feature: REQUEST_PERMISSIONS_PACKET"); // TODO
+ return;
+ case ProtocolInfo.SET_DEFAULT_GAME_TYPE_PACKET:
+ if (!this.spawned) {
+ return;
+ }
- if (bookEditPacket.text != null && bookEditPacket.text.length() > 256) {
- this.getServer().getLogger().debug(username + ": BookEditPacket with too long text");
- return;
+ if (!this.hasPermission("nukkit.command.defaultgamemode")) {
+ if (!this.isOp()) {
+ this.kick(PlayerKickEvent.Reason.INVALID_PACKET, "Invalid SetDefaultGameTypePacket", true);
}
+ return;
+ }
+ int gamemode = ((SetDefaultGameTypePacket) packet).gamemode & 0b11;
+ server.gamemode = gamemode;
+ server.setPropertyInt("gamemode", gamemode);
+ Command.broadcastCommandMessage(this, new TranslationContainer("commands.defaultgamemode.success", new String[]{Server.getGamemodeString(server.getDefaultGamemode())}));
+
+ SetDefaultGameTypePacket gameTypePacket = new SetDefaultGameTypePacket();
+ gameTypePacket.gamemode = server.getDefaultGamemode();
+ Server.broadcastPacket(server.getOnlinePlayers().values(), gameTypePacket);
+ return;
+ case ProtocolInfo.SETTINGS_COMMAND_PACKET:
+ if (!this.spawned) {
+ return;
+ }
- Item newBook = oldBook.clone();
- boolean success;
- switch (bookEditPacket.action) {
- case REPLACE_PAGE:
- success = ((ItemBookAndQuill) newBook).setPageText(bookEditPacket.pageNumber, bookEditPacket.text);
- break;
- case ADD_PAGE:
- success = ((ItemBookAndQuill) newBook).insertPage(bookEditPacket.pageNumber, bookEditPacket.text);
- break;
- case DELETE_PAGE:
- success = ((ItemBookAndQuill) newBook).deletePage(bookEditPacket.pageNumber);
- break;
- case SWAP_PAGES:
- success = ((ItemBookAndQuill) newBook).swapPages(bookEditPacket.pageNumber, bookEditPacket.secondaryPageNumber);
- break;
- case SIGN_BOOK:
- if (bookEditPacket.title == null || bookEditPacket.author == null || bookEditPacket.xuid == null || bookEditPacket.title.length() > 64 || bookEditPacket.author.length() > 64 || bookEditPacket.xuid.length() > 64) {
- this.getServer().getLogger().debug(username + ": Invalid BookEditPacket action SIGN_BOOK: title/author/xuid is too long");
- return;
- }
- newBook = Item.get(Item.WRITTEN_BOOK, 0, 1, oldBook.getCompoundTag());
- success = ((ItemBookWritten) newBook).signBook(bookEditPacket.title, bookEditPacket.author, bookEditPacket.xuid, ItemBookWritten.GENERATION_ORIGINAL);
- break;
- default:
- return;
+ if (!this.hasPermission("nukkit.command.gamerule")) {
+ if (!this.isOp()) {
+ this.kick(PlayerKickEvent.Reason.INVALID_PACKET, "Invalid SettingsCommandPacket", true);
}
+ return;
+ }
+ String command = ((SettingsCommandPacket) packet).command;
+ if (command.startsWith("/gamerule")) {
+ server.dispatchCommand(this, command.substring(1));
+ } else {
+ this.getServer().getLogger().debug(username + ": SettingsCommandPacket unsupported command: " + command);
+ }
+ return;
+ }
+ }
- if (success) {
- PlayerEditBookEvent editBookEvent = new PlayerEditBookEvent(this, oldBook, newBook, bookEditPacket.action);
- this.server.getPluginManager().callEvent(editBookEvent);
- if (!editBookEvent.isCancelled()) {
- this.inventory.setItem(bookEditPacket.inventorySlot, editBookEvent.getNewBook());
- }
- }
- break;
- case ProtocolInfo.FILTER_TEXT_PACKET:
- FilterTextPacket filterTextPacket = (FilterTextPacket) packet;
- if (filterTextPacket.text == null || filterTextPacket.text.length() > 64) {
- this.getServer().getLogger().debug(username + ": FilterTextPacket with too long text");
- return;
- }
- FilterTextPacket textResponsePacket = new FilterTextPacket();
- textResponsePacket.text = filterTextPacket.text;
- textResponsePacket.fromServer = true;
- this.dataPacket(textResponsePacket);
- break;
- case ProtocolInfo.SET_DIFFICULTY_PACKET:
- if (!this.spawned || !this.hasPermission("nukkit.command.difficulty")) {
- return;
- }
- server.setDifficulty(((SetDifficultyPacket) packet).difficulty);
- SetDifficultyPacket difficultyPacket = new SetDifficultyPacket();
- difficultyPacket.difficulty = server.getDifficulty();
- Server.broadcastPacket(server.getOnlinePlayers().values(), difficultyPacket);
- Command.broadcastCommandMessage(this, new TranslationContainer("commands.difficulty.success", String.valueOf(server.getDifficulty())));
- break;
- default:
- break;
- }
+ private void setShieldBlockingDelay(int delay) {
+ if (this.isBlocking()) {
+ this.setBlocking(false);
+ this.blockingDelay = delay;
+ }
+ }
+
+ @Override
+ protected void onBlock(Entity damager, EntityDamageBlockedEvent event, EntityDamageEvent source) {
+ super.onBlock(damager, event, source);
+
+ if (source.getWeapon() != null && source.getWeapon().isAxe()) {
+ this.setShieldBlockingDelay(100);
+ this.startItemCooldown(100, "shield");
}
}
- private void onBlockBreakContinue(Vector3 pos, BlockFace face) {
+ public void startItemCooldown(int cooldownDuration, String itemCategory) {
+ PlayerStartItemCooldownPacket pk = new PlayerStartItemCooldownPacket();
+ pk.itemCategory = itemCategory;
+ pk.cooldownDuration = cooldownDuration;
+ this.dataPacket(pk);
+ }
+
+ private void onBlockBreakAbort(BlockVector3 blockPos, BlockFace face) {
if (this.isBreakingBlock()) {
- Block block = this.level.getBlock(pos, false);
- this.level.addParticle(new PunchBlockParticle(pos, block, face));
+ LevelEventPacket pk = new LevelEventPacket();
+ pk.evid = LevelEventPacket.EVENT_BLOCK_STOP_BREAK;
+ pk.x = (float) breakingBlock.x;
+ pk.y = (float) breakingBlock.y;
+ pk.z = (float) breakingBlock.z;
+ pk.data = 0;
+ this.getLevel().addChunkPacket((int) breakingBlock.x >> 4, (int) breakingBlock.z >> 4, pk);
}
+ this.breakingBlock = null;
}
- private void onBlockBreakStart(Vector3 pos, BlockFace face) {
- BlockVector3 blockPos = pos.asBlockVector3();
+ private void onBlockBreakStart(BlockVector3 blockPos, BlockFace face) {
+ if (this.isSpectator()) {
+ return;
+ }
+
+ boolean posEquals = lastBreakPosition.equals(blockPos);
+ this.lastBreakPosition = blockPos;
long currentBreak = System.currentTimeMillis();
// HACK: Client spams multiple left clicks so we need to skip them.
- if ((this.lastBreakPosition.equals(blockPos) && (currentBreak - this.lastBreak) < 10) || pos.distanceSquared(this) > 100) {
+ if (posEquals && (currentBreak - this.lastBreak) < 10) {
+ return;
+ } else if (blockPos.distanceSquared(this) > 100) {
+ this.breakingBlock = null;
return;
}
- 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);
+ // Reset current block break
+ this.breakingBlock = null;
+
+ this.setUsingItem(false);
+
+ Block target = this.level.getBlock(chunk, blockPos.x, blockPos.y, blockPos.z, false);
+ 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);
+ this.needSendHeldItem = true;
return;
}
switch (target.getId()) {
+ case Block.AIR:
+ return;
case Block.NOTEBLOCK:
((BlockNoteblock) target).emitSound();
- return;
+ break; // note blocks can be broken
case Block.DRAGON_EGG:
if (!this.isCreative()) {
((BlockDragonEgg) target).teleport();
@@ -3627,90 +4845,72 @@ private void onBlockBreakStart(Vector3 pos, BlockFace face) {
}
break;
case Block.ITEM_FRAME_BLOCK:
- BlockEntity itemFrame = this.level.getBlockEntity(pos);
+ BlockEntity itemFrame = this.level.getBlockEntityIfLoaded(this.chunk, this.temporalVector.setComponents(blockPos.x, blockPos.y, blockPos.z));
if (itemFrame instanceof BlockEntityItemFrame && ((BlockEntityItemFrame) itemFrame).dropItem(this)) {
return;
}
break;
+ case Block.LECTERN: // 1.20.70+
+ if (target instanceof BlockLectern) {
+ ((BlockLectern) target).dropBook();
+ }
+ break;
}
- Block block = target.getSide(face);
- if (block.getId() == Block.FIRE) {
+ int bid = this.level.getBlockIdAt(blockPos.x + face.getXOffset(), blockPos.y + face.getYOffset(), blockPos.z + face.getZOffset());
+ if (bid == Block.FIRE || bid == Block.SOUL_FIRE) {
+ Vector3 block = this.temporalVector.setComponents(blockPos.x + face.getXOffset(), blockPos.y + face.getYOffset(), blockPos.z + face.getZOffset());
this.level.setBlock(block, Block.get(BlockID.AIR), true);
this.level.addLevelSoundEvent(block, LevelSoundEventPacket.SOUND_EXTINGUISH_FIRE);
return;
}
if (!this.isCreative()) {
- double breakTime = Math.ceil(target.getBreakTime(this.inventory.getItemInHand(), this) * 20);
- if (breakTime > 0) {
+ double breakTime = target.getBreakTime(this.inventory.getItemInHandFast(), this);
+ int breakTimeTicks = (int) (breakTime * 20 + 0.5);
+ if (breakTimeTicks > 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);
+ pk.x = (float) blockPos.x;
+ pk.y = (float) blockPos.y;
+ pk.z = (float) blockPos.z;
+ pk.data = 65535 / breakTimeTicks;
+ this.getLevel().addChunkPacket(blockPos.x >> 4, blockPos.z >> 4, pk);
}
}
this.breakingBlock = target;
+ this.breakingBlockFace = face;
this.lastBreak = currentBreak;
- this.lastBreakPosition = blockPos;
- }
-
- private void onBlockBreakAbort(Vector3 pos, BlockFace face) {
- if (pos.distanceSquared(this) < 100) {
- 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.data = 0;
- this.getLevel().addChunkPacket(pos.getFloorX() >> 4, pos.getFloorZ() >> 4, pk);
- }
- this.breakingBlock = null;
}
- private void onBlockBreakComplete(BlockVector3 blockPos, BlockFace face) {
+ private void onBlockBreakComplete(BlockVector3 blockPos, BlockFace face) { // From InventoryTransactionPacket.USE_ITEM_ACTION_BREAK_BLOCK
if (!this.spawned || !this.isAlive()) {
return;
}
-
this.resetCraftingGridType();
-
- Item handItem = this.getInventory().getItemInHand();
- Item clone = handItem.clone();
-
- boolean canInteract = this.canInteract(blockPos.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7);
- if (canInteract) {
- handItem = this.level.useBreakOn(blockPos.asVector3(), face, handItem, this, true);
- if (handItem == null) {
- this.level.sendBlocks(new Player[]{this}, new Block[]{this.level.getBlock(blockPos.asVector3())}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
- } else if (this.isSurvival()) {
- this.getFoodData().updateFoodExpLevel(0.005);
- if (handItem.equals(clone) && handItem.getCount() == clone.getCount()) {
- return;
- }
-
- if (clone.getId() == handItem.getId() || handItem.getId() == 0) {
- inventory.setItemInHand(handItem);
- } else {
- server.getLogger().debug("Tried to set item " + handItem.getId() + " but " + this.username + " had item " + clone.getId() + " in their hand slot");
+ Item i = this.getInventory().getItemInHand();
+ Item oldItem = i.clone();
+ if (this.canInteract(blockPos.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7) && (i = this.level.useBreakOn(blockPos.asVector3(), face, i, this, true)) != null) {
+ if (this.isSurvival() || this.isAdventure()) {
+ this.foodData.updateFoodExpLevel(0.005);
+ if (i.getCount() != oldItem.getCount() || i.getDamage() != oldItem.getDamage() || !i.equals(oldItem)) {
+ if (i.getId() == 0 || oldItem.getId() == i.getId()) {
+ inventory.setItemInHand(i);
+
+ // setItem can only send armor to others, I wonder why this isn't needed at other places though
+ inventory.sendHeldItem(this.getViewers().values());
+ } else if (Nukkit.DEBUG > 1) {
+ server.getLogger().debug("Tried to set item " + i.getId() + " but " + this.username + " had item " + oldItem.getId() + " in their hand slot");
+ }
}
- inventory.sendHeldItem(this.getViewers().values());
}
return;
}
-
- inventory.sendContents(this);
- inventory.sendHeldItem(this);
-
- if (blockPos.distanceSquared(this) < 100) {
- Block target = this.level.getBlock(blockPos.asVector3());
- this.level.sendBlocks(new Player[]{this}, new Block[]{target}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
-
- BlockEntity blockEntity = this.level.getBlockEntity(blockPos.asVector3());
+ this.needSendHeldItem = true;
+ if (blockPos.distanceSquared(this) < 10000) {
+ this.level.sendBlocks(this, new Block[]{this.level.getBlock(blockPos.asVector3(), false)}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
+ BlockEntity blockEntity = this.level.getBlockEntityIfLoaded(this.chunk, blockPos.asVector3());
if (blockEntity instanceof BlockEntitySpawnable) {
((BlockEntitySpawnable) blockEntity).spawnTo(this);
}
@@ -3718,29 +4918,51 @@ private void onBlockBreakComplete(BlockVector3 blockPos, BlockFace face) {
}
/**
- * Sends a chat message as this player. If the message begins with a / (forward-slash) it will be treated
- * as a command.
+ * Adjust map color to height map
+ *
+ * @param color block color
+ * @param colorLevel color level
+ * @return adjusted Color
+ */
+ private static Color colorizeMapColor(BlockColor color, int colorLevel) {
+ int colorDepth;
+
+ if (colorLevel == 2) {
+ colorDepth = 255;
+ } else if (colorLevel == 1) {
+ colorDepth = 220;
+ } else if (colorLevel == 0) {
+ colorDepth = 180;
+ } else {
+ throw new IllegalArgumentException("Invalid colorLevel: " + colorLevel);
+ }
+
+ int r = color.getRed() * colorDepth / 255;
+ int g = color.getGreen() * colorDepth / 255;
+ int b = color.getBlue() * colorDepth / 255;
+
+ return new Color(r, g, b);
+ }
+
+ /**
+ * Sends a chat message as this player
+ *
* @param message message to send
* @return successful
*/
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, true);
}
for (String msg : message.split("\n")) {
- if (!msg.trim().isEmpty() && msg.length() <= 512 && this.messageCounter-- > 0) {
+ if (!msg.trim().isEmpty() && msg.length() < 512) {
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.server.broadcastMessage(this.getServer().getLanguage().translateString(chatEvent.getFormat(), new String[]{chatEvent.getPlayer().displayName, chatEvent.getMessage()}), chatEvent.getRecipients());
}
}
}
@@ -3772,6 +4994,13 @@ public boolean kick(PlayerKickEvent.Reason reason, boolean isAdmin) {
return this.kick(reason, reason.toString(), isAdmin);
}
+ /**
+ * Kick the player
+ * @param reason reason
+ * @param reasonString reason string
+ * @param isAdmin display "kicked" or only reason string
+ * @return PlayerKickEvent not cancelled
+ */
public boolean kick(PlayerKickEvent.Reason reason, String reasonString, boolean isAdmin) {
PlayerKickEvent ev;
this.server.getPluginManager().callEvent(ev = new PlayerKickEvent(this, reason, reasonString, this.getLeaveMessage()));
@@ -3779,7 +5008,7 @@ public boolean kick(PlayerKickEvent.Reason reason, String reasonString, boolean
String message;
if (isAdmin) {
if (!this.isBanned()) {
- message = "Kicked by admin." + (!reasonString.isEmpty() ? " Reason: " + reasonString : "");
+ message = "Kicked!" + (!reasonString.isEmpty() ? " Reason: " + reasonString : "");
} else {
message = reasonString;
}
@@ -3799,7 +5028,12 @@ public boolean kick(PlayerKickEvent.Reason reason, String reasonString, boolean
return false;
}
+ /**
+ * Set view distance
+ * @param distance view distance
+ */
public void setViewDistance(int distance) {
+ this.viewDistance = distance;
this.chunkRadius = distance;
ChunkRadiusUpdatedPacket pk = new ChunkRadiusUpdatedPacket();
@@ -3808,15 +5042,37 @@ public void setViewDistance(int distance) {
this.dataPacket(pk);
}
+ /**
+ * Get view distance (client may have updated this within the limits)
+ * @return view distance
+ */
public int getViewDistance() {
return this.chunkRadius;
}
+ /**
+ * Get maximum view distance. Use getViewDistance() to get the view distance possibly updated by client.
+ * @return view distance
+ */
+ public int getMaximumViewDistance() {
+ return this.viewDistance;
+ }
+
@Override
public void sendMessage(String message) {
+ this.sendMessage(message, false);
+ }
+
+ /**
+ * Send a message
+ * @param message message
+ * @param isLocalized message has a translation
+ */
+ public void sendMessage(String message, boolean isLocalized) {
TextPacket pk = new TextPacket();
pk.type = TextPacket.TYPE_RAW;
pk.message = this.server.getLanguage().translateString(message);
+ pk.isLocalized = isLocalized;
this.dataPacket(pk);
}
@@ -3826,7 +5082,7 @@ public void sendMessage(TextContainer message) {
this.sendTranslation(message.getText(), ((TranslationContainer) message).getParameters());
return;
}
- this.sendMessage(message.getText());
+ this.sendMessage(message.getText(), false);
}
public void sendTranslation(String message) {
@@ -3835,17 +5091,16 @@ public void sendTranslation(String message) {
public void sendTranslation(String message, String[] parameters) {
TextPacket pk = new TextPacket();
- if (!this.server.isLanguageForced()) {
+ if (this.server.isLanguageForced()) {
+ pk.type = TextPacket.TYPE_RAW;
+ pk.message = this.server.getLanguage().translateString(message, parameters);
+ } else {
pk.type = TextPacket.TYPE_TRANSLATION;
pk.message = this.server.getLanguage().translateString(message, parameters, "nukkit.");
for (int i = 0; i < parameters.length; i++) {
parameters[i] = this.server.getLanguage().translateString(parameters[i], parameters, "nukkit.");
-
}
pk.parameters = parameters;
- } else {
- pk.type = TextPacket.TYPE_RAW;
- pk.message = this.server.getLanguage().translateString(message, parameters);
}
this.dataPacket(pk);
}
@@ -3863,16 +5118,16 @@ public void sendChat(String source, String message) {
}
public void sendPopup(String message) {
- this.sendPopup(message, "");
- }
-
- public void sendPopup(String message, String subtitle) {
TextPacket pk = new TextPacket();
pk.type = TextPacket.TYPE_POPUP;
pk.message = message;
this.dataPacket(pk);
}
+ public void sendPopup(String message, String subtitle) {
+ this.sendPopup(message);
+ }
+
public void sendTip(String message) {
TextPacket pk = new TextPacket();
pk.type = TextPacket.TYPE_TIP;
@@ -3880,6 +5135,9 @@ public void sendTip(String message) {
this.dataPacket(pk);
}
+ /**
+ * Remove currently playing title
+ */
public void clearTitle() {
SetTitlePacket pk = new SetTitlePacket();
pk.type = SetTitlePacket.TYPE_CLEAR;
@@ -3911,7 +5169,6 @@ public void setTitleAnimationTimes(int fadein, int duration, int fadeout) {
this.dataPacket(pk);
}
-
private void setTitle(String text) {
SetTitlePacket packet = new SetTitlePacket();
packet.text = text;
@@ -3932,7 +5189,7 @@ public void sendTitle(String title, String subtitle, int fadeIn, int stay, int f
if (!Strings.isNullOrEmpty(subtitle)) {
this.setSubtitle(subtitle);
}
- // title won't send if an empty string is used.
+ // Title won't send if an empty string is used
this.setTitle(Strings.isNullOrEmpty(title) ? " " : title);
}
@@ -3950,6 +5207,11 @@ public void sendActionBar(String title, int fadein, int duration, int fadeout) {
this.dataPacket(pk);
}
+ /**
+ * Send toast notification for 1.19+ client
+ * @param title toast title
+ * @param content toast text
+ */
public void sendToast(String title, String content) {
ToastRequestPacket pk = new ToastRequestPacket();
pk.title = title;
@@ -3982,27 +5244,39 @@ public void close(TextContainer message, String reason) {
this.close(message, reason, true);
}
+ /**
+ * Close and disconnect the player
+ * @param message message
+ * @param reason reason
+ * @param notify send disconnection screen
+ */
public void close(TextContainer message, String reason, boolean notify) {
if (this.connected && !this.closed) {
- if (notify && reason.length() > 0) {
+ if (notify && !reason.isEmpty()) {
DisconnectPacket pk = new DisconnectPacket();
pk.message = reason;
this.forceDataPacket(pk, null);
}
this.connected = false;
+
+ // Do all inventory changes before the last save
+ this.resetCraftingGridType();
+ this.removeAllWindows(true);
+
+ if (this.fishing != null) {
+ this.stopFishing(false);
+ }
+
PlayerQuitEvent ev = null;
- if (this.getName() != null && this.getName().length() > 0) {
+ if (this.username != null && !this.username.isEmpty()) {
this.server.getPluginManager().callEvent(ev = new PlayerQuitEvent(this, message, true, reason));
if (this.loggedIn && ev.getAutoSave()) {
this.save();
}
- if (this.fishing != null) {
- this.stopFishing(false);
- }
}
- for (Player player : new ArrayList<>(this.server.getOnlinePlayers().values())) {
+ for (Player player : new ArrayList<>(this.server.playerList.values())) {
if (!player.canSee(this)) {
player.showPlayer(this);
}
@@ -4010,31 +5284,18 @@ public void close(TextContainer message, String reason, boolean notify) {
this.hiddenPlayers.clear();
- this.removeAllWindows(true);
-
- for (long index : new ArrayList<>(this.usedChunks.keySet())) {
- int chunkX = Level.getHashX(index);
- int chunkZ = Level.getHashZ(index);
- this.level.unregisterChunkLoader(this, chunkX, chunkZ);
- this.usedChunks.remove(index);
-
- for (Entity entity : level.getChunkEntities(chunkX, chunkZ).values()) {
- if (entity != this) {
- entity.getViewers().remove(getLoaderId());
- }
- }
- }
+ this.unloadChunks(false);
super.close();
this.interfaz.close(this, notify ? reason : "");
+ this.server.removeOnlinePlayer(this);
+
if (this.loggedIn) {
- this.server.removeOnlinePlayer(this);
+ this.loggedIn = false;
}
- this.loggedIn = false;
-
if (ev != null && !Objects.equals(this.username, "") && this.spawned && !Objects.equals(ev.getQuitMessage().toString(), "")) {
this.server.broadcastMessage(ev.getQuitMessage());
}
@@ -4042,15 +5303,13 @@ public void close(TextContainer message, String reason, boolean notify) {
this.server.getPluginManager().unsubscribeFromPermission(Server.BROADCAST_CHANNEL_USERS, this);
this.spawned = false;
this.server.getLogger().info(this.getServer().getLanguage().translateString("nukkit.player.logOut",
- TextFormat.AQUA + (this.getName() == null ? "" : this.getName()) + TextFormat.WHITE,
+ TextFormat.AQUA + (this.username == null ? this.unverifiedUsername : this.username) + TextFormat.WHITE,
this.getAddress(),
String.valueOf(this.getPort()),
this.getServer().getLanguage().translateString(reason)));
+
this.windows.clear();
- this.usedChunks.clear();
- this.loadQueue.clear();
this.hasSpawned.clear();
- this.spawnPosition = null;
if (this.riding instanceof EntityRideable) {
this.riding.passengers.remove(this);
@@ -4064,33 +5323,50 @@ public void close(TextContainer message, String reason, boolean notify) {
this.perm = null;
}
- if (this.inventory != null) {
- this.inventory = null;
- }
-
+ this.inventory = null;
this.chunk = null;
this.server.removePlayer(this);
+
+ if (this.loggedIn) {
+ this.server.getLogger().warning("Player is still logged in: " + (this.username == null ? this.unverifiedUsername : this.username));
+ this.interfaz.close(this, notify ? reason : "");
+ this.server.removeOnlinePlayer(this);
+ this.loggedIn = false;
+ }
+
+ this.clientMovements.clear();
}
+ /**
+ * Save player data to disk
+ */
public void save() {
this.save(false);
}
+ /**
+ * Save player data to disk
+ * @param async save asynchronously
+ */
public void save(boolean async) {
if (this.closed) {
throw new IllegalStateException("Tried to save closed player");
}
+ if (!this.server.shouldSavePlayerData) {
+ return;
+ }
+
super.saveNBT();
if (this.level != null) {
this.namedTag.putString("Level", this.level.getFolderName());
if (this.spawnPosition != null && this.spawnPosition.getLevel() != null) {
this.namedTag.putString("SpawnLevel", this.spawnPosition.getLevel().getFolderName());
- this.namedTag.putInt("SpawnX", (int) this.spawnPosition.x);
- this.namedTag.putInt("SpawnY", (int) this.spawnPosition.y);
- this.namedTag.putInt("SpawnZ", (int) this.spawnPosition.z);
+ this.namedTag.putInt("SpawnX", this.spawnPosition.getFloorX());
+ this.namedTag.putInt("SpawnY", this.spawnPosition.getFloorY());
+ this.namedTag.putInt("SpawnZ", this.spawnPosition.getFloorZ());
}
CompoundTag achievements = new CompoundTag();
@@ -4105,11 +5381,11 @@ public void save(boolean async) {
this.namedTag.putString("lastIP", this.getAddress());
- this.namedTag.putInt("EXP", this.getExperience());
- this.namedTag.putInt("expLevel", this.getExperienceLevel());
+ this.namedTag.putInt("EXP", this.exp);
+ this.namedTag.putInt("expLevel", this.expLevel);
- this.namedTag.putInt("foodLevel", this.getFoodData().getLevel());
- this.namedTag.putFloat("foodSaturationLevel", this.getFoodData().getFoodSaturationLevel());
+ this.namedTag.putInt("foodLevel", this.foodData.getLevel());
+ this.namedTag.putFloat("foodSaturationLevel", this.foodData.getFoodSaturationLevel());
this.namedTag.putInt("TimeSinceRest", this.timeSinceRest);
@@ -4119,6 +5395,10 @@ public void save(boolean async) {
}
}
+ /**
+ * Get player's username
+ * @return username
+ */
public String getName() {
return this.username;
}
@@ -4135,24 +5415,27 @@ public void kill() {
EntityDamageEvent cause = this.getLastDamageCause();
if (showMessages) {
- params.add(this.getDisplayName());
+ params.add(this.displayName);
switch (cause == null ? DamageCause.CUSTOM : cause.getCause()) {
case ENTITY_ATTACK:
+ case THORNS:
if (cause instanceof EntityDamageByEntityEvent) {
Entity e = ((EntityDamageByEntityEvent) cause).getDamager();
killer = e;
if (e instanceof Player) {
message = "death.attack.player";
- params.add(((Player) e).getDisplayName());
+ params.add(((Player) e).displayName);
break;
} else if (e instanceof EntityLiving) {
message = "death.attack.mob";
params.add(!Objects.equals(e.getNameTag(), "") ? e.getNameTag() : e.getName());
break;
} else {
- params.add("Unknown");
+ message = "death.attack.generic";
}
+ } else {
+ message = "death.attack.generic";
}
break;
case PROJECTILE:
@@ -4161,14 +5444,16 @@ public void kill() {
killer = e;
if (e instanceof Player) {
message = "death.attack.arrow";
- params.add(((Player) e).getDisplayName());
+ params.add(((Player) e).displayName);
} else if (e instanceof EntityLiving) {
message = "death.attack.arrow";
params.add(!Objects.equals(e.getNameTag(), "") ? e.getNameTag() : e.getName());
break;
} else {
- params.add("Unknown");
+ message = "death.attack.generic";
}
+ } else {
+ message = "death.attack.generic";
}
break;
case VOID:
@@ -4187,14 +5472,13 @@ public void kill() {
break;
case LAVA:
- Block block = this.level.getBlock(new Vector3(this.x, this.y - 1, this.z));
- if (block.getId() == Block.MAGMA) {
- message = "death.attack.lava.magma";
- break;
- }
message = "death.attack.lava";
break;
+ case MAGMA:
+ message = "death.attack.magma";
+ break;
+
case FIRE:
message = "death.attack.onFire";
break;
@@ -4214,7 +5498,13 @@ public void kill() {
message = "death.attack.cactus";
} else if (id == Block.ANVIL) {
message = "death.attack.anvil";
+ } else if (id == Block.SWEET_BERRY_BUSH) {
+ message = "death.attack.sweetBerry";
+ } else {
+ message = "death.attack.generic";
}
+ } else {
+ message = "death.attack.generic";
}
break;
@@ -4225,11 +5515,14 @@ public void kill() {
killer = e;
if (e instanceof Player) {
message = "death.attack.explosion.player";
- params.add(((Player) e).getDisplayName());
+ params.add(((Player) e).displayName);
} else if (e instanceof EntityLiving) {
message = "death.attack.explosion.player";
params.add(!Objects.equals(e.getNameTag(), "") ? e.getNameTag() : e.getName());
break;
+ } else if (e instanceof EntityFirework && cause.getEntity() instanceof Player) {
+ params.add(((Player) cause.getEntity()).displayName);
+ message = "death.attack.fireworks";
} else {
message = "death.attack.explosion";
}
@@ -4252,10 +5545,11 @@ public void kill() {
}
}
- PlayerDeathEvent ev = new PlayerDeathEvent(this, this.getDrops(), new TranslationContainer(message, params.toArray(new String[0])), this.expLevel);
- ev.setKeepExperience(this.level.gameRules.getBoolean(GameRule.KEEP_INVENTORY));
- ev.setKeepInventory(ev.getKeepExperience());
+ this.resetCraftingGridType(); // This must be called before getDrops() for UI inventories to be handled properly
+ PlayerDeathEvent ev = new PlayerDeathEvent(this, this.getDrops(), new TranslationContainer(message, params.toArray(new String[0])), this.expLevel);
+ ev.setKeepInventory(this.level.gameRules.getBoolean(GameRule.KEEP_INVENTORY));
+ ev.setKeepExperience(ev.getKeepInventory()); // Same as above
this.server.getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
@@ -4264,9 +5558,10 @@ public void kill() {
}
this.health = 0;
- this.extinguish();
this.scheduleUpdate();
+ //this.resetCraftingGridType();
+
if (!ev.getKeepInventory() && this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) {
for (Item item : ev.getDrops()) {
if (!item.hasEnchantment(Enchantment.ID_VANISHING_CURSE)) {
@@ -4277,9 +5572,9 @@ public void kill() {
if (this.inventory != null) {
this.inventory.clearAll();
}
- if (this.offhandInventory != null) {
- this.offhandInventory.clearAll();
- }
+
+ // Offhand inventory is already cleared in inventory.clearAll()
+ // UI inventories are handled in resetCraftingGridType()
}
if (!ev.getKeepExperience() && this.level.getGameRules().getBoolean(GameRule.DO_ENTITY_DROPS)) {
@@ -4291,28 +5586,29 @@ public void kill() {
this.setExperience(0, 0);
}
- this.timeSinceRest = 0;
-
- if (showMessages && !ev.getDeathMessage().toString().isEmpty()) {
- this.server.broadcast(ev.getDeathMessage(), Server.BROADCAST_CHANNEL_USERS);
+ if (level.getGameRules().getBoolean(GameRule.DO_IMMEDIATE_RESPAWN)) {
+ this.respawn();
+ } else {
+ if (showMessages && !ev.getDeathMessage().toString().isEmpty()) {
+ this.server.broadcast(ev.getDeathMessage(), Server.BROADCAST_CHANNEL_USERS);
- DeathInfoPacket pk = new DeathInfoPacket();
- if (ev.getDeathMessage() instanceof TranslationContainer) {
- pk.messageTranslationKey = this.server.getLanguage().translateString(ev.getDeathMessage().getText(), ((TranslationContainer) ev.getDeathMessage()).getParameters(), null);
- } else {
- pk.messageTranslationKey = ev.getDeathMessage().getText();
+ DeathInfoPacket pk = new DeathInfoPacket();
+ if (ev.getDeathMessage() instanceof TranslationContainer) {
+ pk.messageTranslationKey = this.server.getLanguage().translateString(ev.getDeathMessage().getText(), ((TranslationContainer) ev.getDeathMessage()).getParameters(), null);
+ } else {
+ pk.messageTranslationKey = ev.getDeathMessage().getText();
+ }
+ this.dataPacket(pk);
}
+
+ RespawnPacket pk = new RespawnPacket();
+ Position pos = this.getSpawn();
+ pk.x = (float) pos.x;
+ pk.y = (float) pos.y;
+ pk.z = (float) pos.z;
+ pk.respawnState = RespawnPacket.STATE_SEARCHING_FOR_SPAWN;
this.dataPacket(pk);
}
-
- RespawnPacket pk = new RespawnPacket();
- Position pos = this.getSpawn();
- pk.x = (float) pos.x;
- pk.y = (float) pos.y;
- pk.z = (float) pos.z;
- pk.respawnState = RespawnPacket.STATE_SEARCHING_FOR_SPAWN;
-
- this.dataPacket(pk);
}
}
@@ -4322,7 +5618,6 @@ protected void respawn() {
return;
}
- this.craftingType = CRAFTING_SMALL;
this.resetCraftingGridType();
PlayerRespawnEvent playerRespawnEvent = new PlayerRespawnEvent(this, this.getSpawn());
@@ -4330,32 +5625,56 @@ protected void respawn() {
Position respawnPos = playerRespawnEvent.getRespawnPosition();
- this.sendExperience();
- this.sendExperienceLevel();
+ this.teleport(respawnPos, null);
+
+ this.sendBothExperience(this.exp, this.expLevel);
- this.setSprinting(false);
+ this.setSprinting(false, false);
this.setSneaking(false);
+ this.extinguish();
this.setDataProperty(new ShortEntityData(Player.DATA_AIR, 400), false);
+ this.airTicks = 400;
this.deadTicks = 0;
this.noDamageTicks = 60;
+ this.timeSinceRest = 0;
this.removeAllEffects();
this.setHealth(this.getMaxHealth());
- this.getFoodData().setLevel(20, 20);
+ this.foodData.setLevel(20, 20);
this.sendData(this);
this.setMovementSpeed(DEFAULT_SPEED);
- this.getAdventureSettings().update();
+ this.adventureSettings.update();
this.inventory.sendContents(this);
this.inventory.sendArmorContents(this);
this.offhandInventory.sendContents(this);
- this.teleport(respawnPos, null);
this.spawnToAll();
this.scheduleUpdate();
+
+ if (this.spawnPosition instanceof BlockRespawnAnchor && this.spawnPosition.level.getProvider() != null) {
+ Block anchor = this.spawnPosition.level.getBlock(this.spawnPosition);
+ if (anchor instanceof BlockRespawnAnchor) {
+ int chargeLevel = anchor.getDamage();
+
+ if (chargeLevel > 0) {
+ anchor.setDamage(chargeLevel - 1);
+ anchor.level.setBlock(anchor, anchor);
+
+ anchor.level.addLevelSoundEvent(anchor, LevelSoundEventPacket.SOUND_RESPAWN_ANCHOR_DEPLETE);
+ }
+
+ if (chargeLevel <= 1) {
+ this.setSpawn(server.getDefaultLevel().getSafeSpawn());
+ }
+
+ } else {
+ this.setSpawn(server.getDefaultLevel().getSafeSpawn());
+ }
+ }
}
@Override
@@ -4365,11 +5684,12 @@ public void setHealth(float health) {
}
super.setHealth(health);
- //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);
+
+ // HACK: solve the client-side absorption bug
if (this.spawned) {
UpdateAttributesPacket pk = new UpdateAttributesPacket();
- pk.entries = new Attribute[]{attr};
+ int max = this.getMaxHealth();
+ pk.entries = new Attribute[]{Attribute.getAttribute(Attribute.MAX_HEALTH).setMaxValue(max).setValue(this.health > 0 ? (this.health < max ? this.health : max) : 0)};
pk.entityId = this.id;
this.dataPacket(pk);
}
@@ -4379,53 +5699,76 @@ public void setHealth(float health) {
public void setMaxHealth(int maxHealth) {
super.setMaxHealth(maxHealth);
- 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};
+ int max = this.getMaxHealth();
+ pk.entries = new Attribute[]{Attribute.getAttribute(Attribute.MAX_HEALTH).setMaxValue(max).setValue(this.health > 0 ? (this.health < max ? this.health : max) : 0)};
pk.entityId = this.id;
this.dataPacket(pk);
}
}
+ /**
+ * Get experience
+ * @return experience (non-full levels)
+ */
public int getExperience() {
return this.exp;
}
+ /**
+ * Get experience level
+ * @return experience level
+ */
public int getExperienceLevel() {
return this.expLevel;
}
+ /**
+ * Give the player more experience
+ * @param add experience to add
+ */
public void addExperience(int add) {
if (add == 0) return;
- int now = this.getExperience();
- int added = now + add;
- int level = this.getExperienceLevel();
+ int added = this.exp + add;
+ int level = this.expLevel;
int most = calculateRequireExperience(level);
- while (added >= most) { //Level Up!
- added = added - most;
+ while (added >= most) {
+ added -= most;
level++;
most = calculateRequireExperience(level);
}
this.setExperience(added, level);
}
+ /**
+ * Calculate experience required for the level
+ * @param level level
+ * @return required experience
+ */
public static int calculateRequireExperience(int level) {
if (level >= 30) {
return 112 + (level - 30) * 9;
} else if (level >= 15) {
return 37 + (level - 15) * 5;
} else {
- return 7 + level * 2;
+ return 7 + (level << 1);
}
}
+ /**
+ * Set player's experience
+ * @param exp experience (non-full levels)
+ */
public void setExperience(int exp) {
- setExperience(exp, this.getExperienceLevel());
+ setExperience(exp, this.expLevel);
}
- //todo something on performance, lots of exp orbs then lots of packets, could crash client
-
+ /**
+ * Set player's experience and experience level
+ * @param exp experience (non-full levels)
+ * @param level experience level
+ */
public void setExperience(int exp, int level) {
PlayerExperienceChangeEvent ev = new PlayerExperienceChangeEvent(this, this.exp, this.expLevel, exp, level);
this.server.getPluginManager().callEvent(ev);
@@ -4437,32 +5780,61 @@ public void setExperience(int exp, int level) {
this.exp = ev.getNewExperience();
this.expLevel = ev.getNewExperienceLevel();
- this.sendExperienceLevel(this.expLevel);
- this.sendExperience(this.exp);
+ this.sendBothExperience(this.exp, this.expLevel);
}
+ /**
+ * Send experience (non-full levels)
+ */
public void sendExperience() {
- sendExperience(this.getExperience());
+ sendExperience(this.exp);
}
+ /**
+ * Send experience (non-full levels)
+ * @param exp experience
+ */
public void sendExperience(int exp) {
if (this.spawned) {
- float percent = ((float) exp) / calculateRequireExperience(this.getExperienceLevel());
- percent = Math.max(0f, Math.min(1f, percent));
- this.setAttribute(Attribute.getAttribute(Attribute.EXPERIENCE).setValue(percent));
+ this.setAttribute(Attribute.getAttribute(Attribute.EXPERIENCE).setValue(Math.max(0f, Math.min(1f, ((float) exp) / calculateRequireExperience(this.expLevel)))));
}
}
+ /**
+ * Send experience level
+ */
public void sendExperienceLevel() {
- sendExperienceLevel(this.getExperienceLevel());
+ sendExperienceLevel(this.expLevel);
}
+ /**
+ * Send experience level
+ * @param level experience level
+ */
public void sendExperienceLevel(int level) {
if (this.spawned) {
this.setAttribute(Attribute.getAttribute(Attribute.EXPERIENCE_LEVEL).setValue(level));
}
}
+ /**
+ * Send both player's experience and experience level in one packet
+ * @param exp experience (non-full levels)
+ * @param level experience level
+ */
+ private void sendBothExperience(int exp, int level) {
+ if (this.spawned) {
+ UpdateAttributesPacket pk = new UpdateAttributesPacket();
+ pk.entries = new Attribute[]{Attribute.getAttribute(Attribute.EXPERIENCE_LEVEL).setValue(level), Attribute.getAttribute(Attribute.EXPERIENCE).setValue(Math.max(0f, Math.min(1f, ((float) exp) / calculateRequireExperience(this.expLevel))))};
+ pk.entityId = this.id;
+ this.dataPacket(pk);
+ }
+ }
+
+ /**
+ * Send updated attribute
+ * @param attribute attribute
+ */
public void setAttribute(Attribute attribute) {
UpdateAttributesPacket pk = new UpdateAttributesPacket();
pk.entries = new Attribute[]{attribute};
@@ -4475,50 +5847,59 @@ public void setMovementSpeed(float speed) {
setMovementSpeed(speed, true);
}
+ /**
+ * Set player's movement speed
+ * @param speed speed
+ * @param send send updated speed to player
+ */
public void setMovementSpeed(float speed, boolean send) {
+ if (speed < 0) { // Apparently effects can break this?
+ server.getLogger().debug("Invalid setMovementSpeed: " + speed);
+ return;
+ }
super.setMovementSpeed(speed);
if (this.spawned && send) {
- this.sendMovementSpeed(speed);
+ this.setAttribute(Attribute.getAttribute(Attribute.MOVEMENT_SPEED).setValue(speed).setDefaultValue(speed));
}
}
- public void sendMovementSpeed(float speed){
+ /**
+ * Send movement speed attribute
+ * @param speed speed
+ */
+ public void sendMovementSpeed(float speed) {
Attribute attribute = Attribute.getAttribute(Attribute.MOVEMENT_SPEED).setValue(speed);
this.setAttribute(attribute);
}
+ /**
+ * Get the entity which killed the player
+ * @return entity which killed the player or null
+ */
public Entity getKiller() {
return killer;
}
@Override
public boolean attack(EntityDamageEvent source) {
- if (!this.isAlive()) {
+ if (!spawned || closed || !this.isAlive()) {
return false;
}
if (this.isSpectator() || (this.isCreative() && source.getCause() != DamageCause.SUICIDE)) {
- //source.setCancelled();
+ source.setCancelled();
return false;
- } else if (this.getAdventureSettings().get(Type.ALLOW_FLIGHT) && source.getCause() == DamageCause.FALL) {
- //source.setCancelled();
+ } else if (source.getCause() == DamageCause.FALL && this.getAllowFlight()) {
+ source.setCancelled();
return false;
- } else if (source.getCause() == DamageCause.FALL) {
- if (this.getLevel().getBlock(this.getPosition().floor().add(0.5, -1, 0.5)).getId() == Block.SLIME_BLOCK) {
- if (!this.isSneaking()) {
- //source.setCancelled();
- this.resetFallDistance();
- return false;
- }
- }
}
- if (super.attack(source)) { //!source.isCancelled()
+ if (super.attack(source)) {
if (this.getLastDamageCause() == source && this.spawned) {
if (source instanceof EntityDamageByEntityEvent) {
Entity damager = ((EntityDamageByEntityEvent) source).getDamager();
if (damager instanceof Player) {
- ((Player) damager).getFoodData().updateFoodExpLevel(0.1);
+ ((Player) damager).foodData.updateFoodExpLevel(0.1);
}
}
EntityEventPacket pk = new EntityEventPacket();
@@ -4544,15 +5925,17 @@ public boolean dropItem(Item item) {
}
if (item.isNull()) {
- this.server.getLogger().debug(this.getName() + " attempted to drop a null item (" + item + ")");
+ this.server.getLogger().debug(this.username + " 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.setUsingItem(false);
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false);
+ Vector3 motion = this.getDirectionVector().multiply(0.4);
+ EntityItem entityItem = this.level.dropAndGetItem(this.add(0, 1.3, 0), item, motion, 40);
+ if (entityItem != null) {
+ entityItem.droppedBy = this;
+ }
return true;
}
@@ -4568,15 +5951,18 @@ public EntityItem dropAndGetItem(Item item) {
}
if (item.isNull()) {
- this.server.getLogger().debug(this.getName() + " attempted to drop a null item (" + item + ")");
+ this.server.getLogger().debug(this.getName() + " attempted to drop a null item (" + item + ')');
return null;
}
- Vector3 motion = this.getDirectionVector().multiply(0.4);
-
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false);
+ this.setUsingItem(false);
- return this.level.dropAndGetItem(this.add(0, 1.3, 0), item, motion, 40);
+ Vector3 motion = this.getDirectionVector().multiply(0.4);
+ EntityItem entityItem = this.level.dropAndGetItem(this.add(0, 1.3, 0), item, motion, 40);
+ if (entityItem != null) {
+ entityItem.droppedBy = this;
+ }
+ return entityItem;
}
public void sendPosition(Vector3 pos) {
@@ -4595,47 +5981,90 @@ public void sendPosition(Vector3 pos, double yaw, double pitch, int mode) {
this.sendPosition(pos, yaw, pitch, mode, null);
}
+ /**
+ * Send player's position and rotation
+ * @param pos position
+ * @param yaw yaw
+ * @param pitch pitch
+ * @param mode movement mode
+ * @param targets receivers
+ */
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;
- pk.y = (float) (pos.y + this.getEyeHeight());
+ pk.y = (float) (pos.y + this.getBaseOffset());
pk.z = (float) pos.z;
pk.headYaw = (float) yaw;
pk.pitch = (float) pitch;
pk.yaw = (float) yaw;
pk.mode = mode;
+ pk.onGround = this.onGround;
+
if (this.riding != null) {
pk.ridingEid = this.riding.getId();
pk.mode = MovePlayerPacket.MODE_PITCH;
}
+ this.ySize = 0;
+
if (targets != null) {
Server.broadcastPacket(targets, pk);
} else {
+ this.clientMovements.clear();
+
this.dataPacket(pk);
}
}
+ /**
+ * Internal: Broadcast player movement to viewers
+ * @param x x
+ * @param y y
+ * @param z z
+ * @param yaw yaw
+ * @param pitch pitch
+ * @param headYaw headYaw
+ */
+ private void sendPositionToViewers(double x, double y, double z, double yaw, double pitch, double headYaw) {
+ MovePlayerPacket pk = new MovePlayerPacket();
+ pk.eid = this.getId();
+ pk.x = (float) x;
+ pk.y = (float) (y + this.getBaseOffset());
+ pk.z = (float) z;
+ pk.headYaw = (float) headYaw;
+ pk.pitch = (float) pitch;
+ pk.yaw = (float) yaw;
+ pk.mode = MovePlayerPacket.MODE_NORMAL;
+ pk.onGround = this.onGround;
+
+ if (this.riding != null) {
+ pk.ridingEid = this.riding.getId();
+ pk.mode = MovePlayerPacket.MODE_PITCH;
+ }
+
+ this.ySize = 0;
+
+ Server.broadcastPacket(this.getViewers().values(), pk);
+ }
+
@Override
protected void checkChunks() {
- if (this.chunk == null || (this.chunk.getX() != ((int) this.x >> 4) || this.chunk.getZ() != ((int) this.z >> 4))) {
+ if (this.chunk == null || (this.chunk.getX() != this.getChunkX() || this.chunk.getZ() != this.getChunkZ())) {
if (this.chunk != null) {
this.chunk.removeEntity(this);
}
- this.chunk = this.level.getChunk((int) this.x >> 4, (int) this.z >> 4, true);
+ this.chunk = this.level.getChunk(this.getChunkX(), this.getChunkZ(), true);
if (!this.justCreated) {
- Map newChunk = this.level.getChunkPlayers((int) this.x >> 4, (int) this.z >> 4);
- newChunk.remove(this.getLoaderId());
+ Map newChunk = this.level.getChunkPlayers(this.getChunkX(), this.getChunkZ());
+ newChunk.remove(this.loaderId);
- //List reload = new ArrayList<>();
for (Player player : new ArrayList<>(this.hasSpawned.values())) {
- if (!newChunk.containsKey(player.getLoaderId())) {
+ if (!newChunk.containsKey(player.loaderId)) {
this.despawnFrom(player);
} else {
- newChunk.remove(player.getLoaderId());
- //reload.add(player);
+ newChunk.remove(player.loaderId);
}
}
@@ -4653,9 +6082,13 @@ protected void checkChunks() {
}
protected boolean checkTeleportPosition() {
+ return checkTeleportPosition(false);
+ }
+
+ protected boolean checkTeleportPosition(boolean enderPearl) {
if (this.teleportPosition != null) {
- int chunkX = (int) this.teleportPosition.x >> 4;
- int chunkZ = (int) this.teleportPosition.z >> 4;
+ int chunkX = this.teleportPosition.getChunkX();
+ int chunkZ = this.teleportPosition.getChunkZ();
for (int X = -1; X <= 1; ++X) {
for (int Z = -1; Z <= 1; ++Z) {
@@ -4667,7 +6100,9 @@ protected boolean checkTeleportPosition() {
}
this.spawnToAll();
- this.forceMovement = this.teleportPosition;
+ if (!enderPearl) {
+ this.forceMovement = this.teleportPosition;
+ }
this.teleportPosition = null;
return true;
}
@@ -4676,13 +6111,8 @@ protected boolean checkTeleportPosition() {
}
protected void sendPlayStatus(int status) {
- sendPlayStatus(status, false);
- }
-
- protected void sendPlayStatus(int status, boolean immediate) {
PlayStatusPacket pk = new PlayStatusPacket();
pk.status = status;
-
this.dataPacket(pk);
}
@@ -4692,89 +6122,75 @@ public boolean teleport(Location location, TeleportCause cause) {
return false;
}
- Location from = this.getLocation();
Location to = location;
if (cause != null) {
+ Location from = this.getLocation();
PlayerTeleportEvent event = new PlayerTeleportEvent(this, from, to, cause);
this.server.getPluginManager().callEvent(event);
if (event.isCancelled()) return false;
to = event.getTo();
}
- //TODO Remove it! A hack to solve the client-side teleporting bug! (inside into the block)
+ // HACK: 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.formOpen = false;
- this.teleportPosition = new Vector3(this.x, this.y, this.z);
- this.forceMovement = this.teleportPosition;
- this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_TELEPORT);
+ this.teleportPosition = this;
+ if (cause != PlayerTeleportEvent.TeleportCause.ENDER_PEARL) {
+ this.forceMovement = this.teleportPosition;
+ }
- this.checkTeleportPosition();
+ if (this.dimensionChangeInProgress) {
+ this.dimensionChangeInProgress = false;
+ } else {
+ this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_TELEPORT);
+ this.checkTeleportPosition(cause == PlayerTeleportEvent.TeleportCause.ENDER_PEARL);
+ this.dummyBossBars.values().forEach(DummyBossBar::reshow);
+ }
this.resetFallDistance();
this.nextChunkOrderRun = 0;
this.resetClientMovement();
- //DummyBossBar
- this.getDummyBossBars().values().forEach(DummyBossBar::reshow);
- //Weather
- this.getLevel().sendWeather(this);
- //Update time
- this.getLevel().sendTime(this);
+ this.stopFishing(false);
return true;
}
return false;
}
- protected void forceSendEmptyChunks() {
- int chunkPositionX = this.getFloorX() >> 4;
- int chunkPositionZ = this.getFloorZ() >> 4;
- for (int x = -chunkRadius; x < chunkRadius; x++) {
- for (int z = -chunkRadius; z < chunkRadius; z++) {
- LevelChunkPacket chunk = new LevelChunkPacket();
- chunk.chunkX = chunkPositionX + x;
- chunk.chunkZ = chunkPositionZ + z;
- chunk.data = new byte[0];
- this.dataPacket(chunk);
- }
- }
- }
-
+ /**
+ * Warning: Using teleportImmediate() may have unexpected side effects. Please use teleport() instead.
+ * Teleports the player immediately without calling PlayerTeleportEvent.
+ * @param location target location
+ */
public void teleportImmediate(Location location) {
this.teleportImmediate(location, TeleportCause.PLUGIN);
}
+ /**
+ * Warning: Using teleportImmediate() may have unexpected side effects. Please use teleport() instead.
+ * Teleports the player immediately without calling PlayerTeleportEvent.
+ * @param location target location
+ * @param cause teleport cause
+ */
public void teleportImmediate(Location location, TeleportCause cause) {
- Location from = this.getLocation();
- if (super.teleport(location, cause)) {
+ // HACK: solve the client-side teleporting bug (inside into the block)
+ if (super.teleport(location.getY() == location.getFloorY() ? location.add(0, 0.00001, 0) : location, cause)) {
this.removeAllWindows();
+ this.formOpen = false;
- if (from.getLevel().getId() != location.getLevel().getId()) { //Different level, update compass position
- SetSpawnPositionPacket pk = new SetSpawnPositionPacket();
- pk.spawnType = SetSpawnPositionPacket.TYPE_WORLD_SPAWN;
- Position spawn = location.getLevel().getSpawnLocation();
- pk.x = spawn.getFloorX();
- pk.y = spawn.getFloorY();
- pk.z = spawn.getFloorZ();
- dataPacket(pk);
- }
-
- this.forceMovement = new Vector3(this.x, this.y, this.z);
- this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_RESET);
+ this.forceMovement = this;
+ this.sendPosition(this, this.yaw, this.pitch, MovePlayerPacket.MODE_TELEPORT);
this.resetFallDistance();
this.orderChunks();
this.nextChunkOrderRun = 0;
this.resetClientMovement();
- //DummyBossBar
- this.getDummyBossBars().values().forEach(DummyBossBar::reshow);
- //Weather
- this.getLevel().sendWeather(this);
- //Update time
- this.getLevel().sendTime(this);
+ this.stopFishing(false);
}
}
@@ -4798,12 +6214,13 @@ public int showFormWindow(FormWindow window) {
* @return form id to use in {@link PlayerFormRespondedEvent}
*/
public int showFormWindow(FormWindow window, int id) {
+ if (formOpen) return 0;
ModalFormRequestPacket packet = new ModalFormRequestPacket();
packet.formId = id;
packet.data = window.getJSONData();
this.formWindows.put(packet.formId, window);
-
this.dataPacket(packet);
+ this.formOpen = true;
return id;
}
@@ -4828,10 +6245,8 @@ public int addServerSettings(FormWindow window) {
* @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) {
- DummyBossBar bossBar = new DummyBossBar.Builder(this).text(text).length(length).build();
- return this.createBossBar(bossBar);
+ return this.createBossBar(new DummyBossBar.Builder(this).text(text).length(length).build());
}
/**
@@ -4876,7 +6291,6 @@ public Map getDummyBossBars() {
* @param length The new BossBar length
* @param bossBarId The BossBar ID
*/
- @Deprecated
public void updateBossBar(String text, int length, long bossBarId) {
if (this.dummyBossBars.containsKey(bossBarId)) {
DummyBossBar bossBar = this.dummyBossBars.get(bossBarId);
@@ -4897,6 +6311,11 @@ public void removeBossBar(long bossBarId) {
}
}
+ /**
+ * Get window id of an open Inventory
+ * @param inventory inventory
+ * @return id of the inventory window or -1 if player doesn't have the window open
+ */
public int getWindowId(Inventory inventory) {
if (this.windows.containsKey(inventory)) {
return this.windows.get(inventory);
@@ -4905,6 +6324,11 @@ public int getWindowId(Inventory inventory) {
return -1;
}
+ /**
+ * Get on open inventory by window id
+ * @param id window id
+ * @return inventory (if open) or null
+ */
public Inventory getWindowById(int id) {
return this.windowIndex.get(id);
}
@@ -4965,13 +6389,14 @@ public void removeWindow(Inventory inventory) {
protected void removeWindow(Inventory inventory, boolean isResponse) {
inventory.close(this);
- // TODO: This needs a proper fix
- // Requiring isResponse here causes issues with inventory events and an item duplication glitch
- if (/*isResponse &&*/ !this.permanentWindows.contains(this.getWindowId(inventory))) {
+ if (/*isResponse &&*/ !this.permanentWindows.contains(this.getWindowId(inventory))) { // Possible dupe
this.windows.remove(inventory);
}
}
+ /**
+ * Send contents of all open inventories to the player
+ */
public void sendAllInventories() {
for (Inventory inv : this.windows.keySet()) {
inv.sendContents(this);
@@ -4991,72 +6416,120 @@ protected void addDefaultWindows() {
this.craftingGrid = this.playerUIInventory.getCraftingGrid();
this.addWindow(this.craftingGrid, ContainerIds.NONE);
-
- //TODO: more windows
}
+ /**
+ * Get player's ui inventory
+ * @return ui inventory
+ */
public PlayerUIInventory getUIInventory() {
return playerUIInventory;
}
+ /**
+ * Get player's cursor inventory
+ * @return cursor inventory
+ */
public PlayerCursorInventory getCursorInventory() {
return this.playerUIInventory.getCursorInventory();
}
+ /**
+ * Get player's crafting grid
+ * @return crafting grid
+ */
public CraftingGrid getCraftingGrid() {
return this.craftingGrid;
}
+ /**
+ * Set player's crafting grid
+ * @param grid crafting grid
+ */
public void setCraftingGrid(CraftingGrid grid) {
this.craftingGrid = grid;
this.addWindow(grid, ContainerIds.NONE);
}
+ /**
+ * Resets crafting grid type and moves all UI inventory contents back to player inventory or drops them.
+ */
public void resetCraftingGridType() {
- if (this.craftingGrid != null) {
- Item[] drops = this.inventory.addItem(this.craftingGrid.getContents().values().toArray(new Item[0]));
+ if (this.playerUIInventory != null) {
+ Item[] drops;
+
+ if (this.craftingGrid != null) {
+ drops = this.inventory.addItem(this.craftingGrid.getContents().values().toArray(new Item[0]));
+ this.craftingGrid.clearAll();
- if (drops.length > 0) {
for (Item drop : drops) {
- this.dropItem(drop);
+ this.level.dropItem(this, drop);
}
}
- drops = this.inventory.addItem(this.getCursorInventory().getItem(0));
- if (drops.length > 0) {
- for (Item drop : drops) {
- this.dropItem(drop);
- }
+ drops = this.inventory.addItem(this.playerUIInventory.getCursorInventory().getItemFast(0)); // cloned in addItem
+ this.playerUIInventory.getCursorInventory().clear(0);
+
+ for (Item drop : drops) {
+ this.level.dropItem(this, drop);
}
+ // Don't trust the client to handle this
+ this.moveBlockUIContents(Player.ANVIL_WINDOW_ID); // LOOM_WINDOW_ID is the same as ANVIL_WINDOW_ID?
+ this.moveBlockUIContents(Player.ENCHANT_WINDOW_ID);
+ this.moveBlockUIContents(Player.BEACON_WINDOW_ID);
+ this.moveBlockUIContents(Player.SMITHING_WINDOW_ID);
this.playerUIInventory.clearAll();
- if (this.craftingGrid instanceof BigCraftingGrid) {
+ if (this.craftingGrid instanceof BigCraftingGrid && this.connected) {
this.craftingGrid = this.playerUIInventory.getCraftingGrid();
this.addWindow(this.craftingGrid, ContainerIds.NONE);
-//
-// ContainerClosePacket pk = new ContainerClosePacket(); //be sure, big crafting is really closed
-// pk.windowId = ContainerIds.NONE;
-// this.dataPacket(pk);
}
+ }
+
+ this.craftingType = CRAFTING_SMALL;
+ }
- this.craftingType = CRAFTING_SMALL;
+ /**
+ * Move all block UI contents back to player inventory or drop them
+ * @param window window id
+ */
+ private void moveBlockUIContents(int window) {
+ Inventory inventory = this.getWindowById(window);
+ if (inventory != null) {
+ Item[] drops = this.inventory.addItem(inventory.getContents().values().toArray(new Item[0]));
+ inventory.clearAll();
+ for (Item drop : drops) {
+ this.level.dropItem(this, drop);
+ }
}
}
+ /**
+ * Remove all windows
+ */
public void removeAllWindows() {
removeAllWindows(false);
}
+ /**
+ * Remove all windows
+ * @param permanent remove permanent windows
+ */
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());
}
}
+ /**
+ * Get id of the window client has requested to be closed
+ * @return window id or Integer.MIN_VALUE if no window is being closed
+ */
public int getClosingWindowId() {
return this.closingWindowId;
}
@@ -5086,24 +6559,22 @@ public void onChunkChanged(FullChunk chunk) {
this.usedChunks.remove(Level.chunkHash(chunk.getX(), chunk.getZ()));
}
+ /* Note: Update Level useChunkLoaderApi checks if more ChunkLoader API is ever used here */
+
@Override
public void onChunkLoaded(FullChunk chunk) {
-
}
@Override
public void onChunkPopulated(FullChunk chunk) {
-
}
@Override
public void onChunkUnloaded(FullChunk chunk) {
-
}
@Override
public void onBlockChanged(Vector3 block) {
-
}
@Override
@@ -5113,131 +6584,212 @@ public int getLoaderId() {
@Override
public boolean isLoaderActive() {
- return this.isConnected();
+ return this.connected;
}
-
- public static BatchPacket getChunkCacheFromData(int chunkX, int chunkZ, int subChunkCount, byte[] payload) {
+ /**
+ * Get chunk cache from data
+ * @param chunkX chunk x
+ * @param chunkZ chunk z
+ * @param subChunkCount sub chunk count
+ * @param payload data
+ * @return BatchPacket
+ */
+ public static BatchPacket getChunkCacheFromData(int chunkX, int chunkZ, int subChunkCount, byte[] payload, int dimension) {
LevelChunkPacket pk = new LevelChunkPacket();
pk.chunkX = chunkX;
pk.chunkZ = chunkZ;
+ pk.dimension = dimension;
pk.subChunkCount = subChunkCount;
pk.data = payload;
- pk.encode();
+ pk.tryEncode();
- BatchPacket batch = new BatchPacket();
- byte[][] batchPayload = new byte[2][];
byte[] buf = pk.getBuffer();
- batchPayload[0] = Binary.writeUnsignedVarInt(buf.length);
- batchPayload[1] = buf;
- byte[] data = Binary.appendBytes(batchPayload);
+ BinaryStream batched = new BinaryStream(new byte[5 + buf.length]).reset();
+ batched.putUnsignedVarInt(buf.length);
+ batched.put(buf);
try {
- batch.payload = Network.deflateRaw(data, Server.getInstance().networkCompressionLevel);
+ byte[] bytes = batched.getBuffer();
+ BatchPacket compress = new BatchPacket();
+ if (Server.getInstance().useSnappy) {
+ compress.payload = SnappyCompression.compress(bytes);
+ } else {
+ compress.payload = Zlib.deflateRaw(bytes, Server.getInstance().networkCompressionLevel);
+ }
+ return compress;
} catch (Exception e) {
throw new RuntimeException(e);
}
- return batch;
}
- private boolean foodEnabled = true;
-
+ /**
+ * Check whether food is enabled or not
+ * @return food enabled
+ */
public boolean isFoodEnabled() {
return !(this.isCreative() || this.isSpectator()) && this.foodEnabled;
}
+ /**
+ * Enable or disable food
+ * @param foodEnabled food enabled
+ */
public void setFoodEnabled(boolean foodEnabled) {
this.foodEnabled = foodEnabled;
}
+ /**
+ * Get player's food data
+ * @return food data
+ */
public PlayerFood getFoodData() {
return this.foodData;
}
- //todo a lot on dimension
+ /**
+ * Send dimension change
+ * @param dimension dimension id
+ */
+ public void setDimension(int dimension) {
+ if (!this.loggedIn) {
+ return; // Do not send ChangeDimensionPacket before StartGamePacket
+ }
- private void setDimension(int dimension) {
- ChangeDimensionPacket pk = new ChangeDimensionPacket();
- pk.dimension = dimension;
- pk.x = (float) this.x;
- pk.y = (float) this.y;
- pk.z = (float) this.z;
- this.dataPacket(pk);
+ this.dimensionChangeInProgress = true;
+ this.awaitingDimensionAck = true;
+
+ ChangeDimensionPacket changeDimensionPacket = new ChangeDimensionPacket();
+ changeDimensionPacket.dimension = dimension;
+ changeDimensionPacket.x = (float) this.x;
+ changeDimensionPacket.y = (float) this.y;
+ changeDimensionPacket.z = (float) this.z;
+ changeDimensionPacket.respawn = !this.isAlive();
+ this.dataPacket(changeDimensionPacket);
+
+ NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket();
+ chunkPublisherUpdatePacket.position = this.asBlockVector3();
+ chunkPublisherUpdatePacket.radius = this.chunkRadius << 4;
+ this.dataPacket(chunkPublisherUpdatePacket);
+
+ this.dimensionFix560 = true;
}
@Override
- public boolean switchLevel(Level level) {
- Level oldLevel = this.level;
- if (super.switchLevel(level)) {
- SetSpawnPositionPacket spawnPosition = new SetSpawnPositionPacket();
- spawnPosition.spawnType = SetSpawnPositionPacket.TYPE_WORLD_SPAWN;
- Position spawn = level.getSpawnLocation();
- spawnPosition.x = spawn.getFloorX();
- spawnPosition.y = spawn.getFloorY();
- spawnPosition.z = spawn.getFloorZ();
- this.dataPacket(spawnPosition);
-
- // Remove old chunks
- for (long index : new ArrayList<>(this.usedChunks.keySet())) {
- int chunkX = Level.getHashX(index);
- int chunkZ = Level.getHashZ(index);
- this.unloadChunk(chunkX, chunkZ, oldLevel);
- }
- this.usedChunks.clear();
+ protected void preSwitchLevel() {
+ // Make sure batch packets from the previous world gets through first
+ this.networkSession.flush();
- SetTimePacket setTime = new SetTimePacket();
- setTime.time = level.getTime();
- this.dataPacket(setTime);
+ // Remove old chunks
+ this.unloadChunks(true);
+ }
- GameRulesChangedPacket gameRulesChanged = new GameRulesChangedPacket();
- gameRulesChanged.gameRules = level.getGameRules();
- this.dataPacket(gameRulesChanged);
- return true;
- }
+ @Override
+ protected void afterSwitchLevel() {
+ // Send spawn to update compass position
+ SetSpawnPositionPacket spawnPosition = new SetSpawnPositionPacket();
+ spawnPosition.spawnType = SetSpawnPositionPacket.TYPE_WORLD_SPAWN;
+ Position spawn = level.getSpawnLocation();
+ spawnPosition.x = spawn.getFloorX();
+ spawnPosition.y = spawn.getFloorY();
+ spawnPosition.z = spawn.getFloorZ();
+ spawnPosition.dimension = level.getDimension();
+ this.dataPacket(spawnPosition);
+
+ // Update time and weather
+ level.sendTime(this);
+ level.sendWeather(this);
+
+ // Update game rules
+ GameRulesChangedPacket packet = new GameRulesChangedPacket();
+ packet.gameRulesMap = level.getGameRules().getGameRules();
+ this.dataPacket(packet);
- return false;
+ // Reset sleeping timer
+ this.timeSinceRest = 0;
}
+ /**
+ * Enable or disable movement check
+ * @param checkMovement movement check enabled
+ */
public void setCheckMovement(boolean checkMovement) {
this.checkMovement = checkMovement;
}
+ /**
+ * @return player movement checks enabled
+ */
public boolean isCheckingMovement() {
return this.checkMovement;
}
+ /**
+ * Set locale
+ * @param locale locale
+ */
public synchronized void setLocale(Locale locale) {
this.locale.set(locale);
}
+ /**
+ * Get locale
+ * @return locale
+ */
public synchronized Locale getLocale() {
return this.locale.get();
}
@Override
public void setSprinting(boolean value) {
+ this.setSprinting(value, true);
+ }
+
+ /**
+ * Update movement speed to start/stop sprinting
+ * @param value sprinting
+ * @param send send updated speed to client
+ */
+ public void setSprinting(boolean value, boolean send) {
if (isSprinting() != value) {
super.setSprinting(value);
-
- if(this.hasEffect(Effect.SPEED)) {
- float movementSpeed = this.getMovementSpeed();
- this.sendMovementSpeed(value ? movementSpeed * 1.3f : movementSpeed);
- }
+ this.setMovementSpeed(value ? getMovementSpeed() * 1.3f : getMovementSpeed() / 1.3f, send);
}
}
+ /**
+ * Transfer player to other server
+ * @param address target server address
+ */
public void transfer(InetSocketAddress address) {
- String hostName = address.getAddress().getHostAddress();
- int port = address.getPort();
+ transfer(address.getAddress().getHostAddress(), address.getPort());
+ }
+
+ /**
+ * Transfer player to other server
+ * @param hostName target server address
+ * @param port target server port
+ */
+ public void transfer(String hostName, int port) {
TransferPacket pk = new TransferPacket();
pk.address = hostName;
pk.port = port;
this.dataPacket(pk);
}
+ /**
+ * Get player's LoginChainData
+ * @return login chain data
+ */
public LoginChainData getLoginChainData() {
return this.loginChainData;
}
+ /**
+ * Try to pick up an entity
+ * @param entity target
+ * @param near near
+ * @return success
+ */
public boolean pickupEntity(Entity entity, boolean near) {
if (!this.spawned || !this.isAlive() || !this.isOnline() || this.isSpectator() || entity.isClosed()) {
return false;
@@ -5245,19 +6797,25 @@ public boolean pickupEntity(Entity entity, boolean near) {
if (near) {
if (entity instanceof EntityArrow && ((EntityArrow) entity).hadCollision) {
- ItemArrow item = new ItemArrow();
+ EntityArrow a = ((EntityArrow) entity);
+ ItemArrow item = (ItemArrow) Item.get(Item.ARROW, a.getData());
if (!this.isCreative() && !this.inventory.canAddItem(item)) {
return false;
}
- InventoryPickupArrowEvent ev = new InventoryPickupArrowEvent(this.inventory, (EntityArrow) entity);
+ InventoryPickupArrowEvent ev = new InventoryPickupArrowEvent(this.inventory, a);
- int pickupMode = ((EntityArrow) entity).getPickupMode();
- if (pickupMode == EntityArrow.PICKUP_NONE || (pickupMode == EntityArrow.PICKUP_CREATIVE && !this.isCreative())) {
+ int pickupMode = a.getPickupMode();
+ if (pickupMode == EntityArrow.PICKUP_NONE_REMOVE || pickupMode == EntityArrow.PICKUP_NONE || (pickupMode == EntityArrow.PICKUP_CREATIVE && !this.isCreative())) {
ev.setCancelled();
}
this.server.getPluginManager().callEvent(ev);
+
+ if (pickupMode == EntityArrow.PICKUP_NONE_REMOVE) {
+ entity.close();
+ }
+
if (ev.isCancelled()) {
return false;
}
@@ -5269,12 +6827,31 @@ public boolean pickupEntity(Entity entity, boolean near) {
this.dataPacket(pk);
if (!this.isCreative()) {
- this.inventory.addItem(item.clone());
+ this.inventory.addItem(item);
}
entity.close();
return true;
- } else if (entity instanceof EntityThrownTrident && ((EntityThrownTrident) entity).hadCollision) {
+ } else if (entity instanceof EntityThrownTrident) {
+ if (!((EntityThrownTrident) entity).hadCollision) {
+ if (entity.noClip) {
+ if (!this.equals(((EntityProjectile) entity).shootingEntity)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ if (!((EntityThrownTrident) entity).shotByPlayer()) {
+ return false;
+ }
+
Item item = ((EntityThrownTrident) entity).getItem();
+
+ if (item.hasEnchantment(Enchantment.ID_TRIDENT_LOYALTY) && !this.equals(((EntityProjectile) entity).shootingEntity)) {
+ return false;
+ }
+
if (!this.isCreative() && !this.inventory.canAddItem(item)) {
return false;
}
@@ -5282,11 +6859,16 @@ public boolean pickupEntity(Entity entity, boolean near) {
InventoryPickupTridentEvent ev = new InventoryPickupTridentEvent(this.inventory, (EntityThrownTrident) entity);
int pickupMode = ((EntityThrownTrident) entity).getPickupMode();
- if (pickupMode == EntityThrownTrident.PICKUP_NONE || (pickupMode == EntityThrownTrident.PICKUP_CREATIVE && !this.isCreative())) {
+ if (pickupMode == EntityArrow.PICKUP_NONE_REMOVE || pickupMode == EntityThrownTrident.PICKUP_NONE || (pickupMode == EntityThrownTrident.PICKUP_CREATIVE && !this.isCreative())) {
ev.setCancelled();
}
this.server.getPluginManager().callEvent(ev);
+
+ if (pickupMode == EntityArrow.PICKUP_NONE_REMOVE) {
+ entity.close();
+ }
+
if (ev.isCancelled()) {
return false;
}
@@ -5298,13 +6880,20 @@ public boolean pickupEntity(Entity entity, boolean near) {
this.dataPacket(pk);
if (!this.isCreative()) {
- this.inventory.addItem(item.clone());
+ int favSlot = ((EntityThrownTrident) entity).getFavoredSlot();
+ if (favSlot != -1 && !this.isCreative() && inventory.getItemFast(favSlot).getId() == Item.AIR) {
+ inventory.setItem(favSlot, item.clone());
+ } else {
+ inventory.addItem(item); // cloned in addItem
+ }
}
+
entity.close();
return true;
} else if (entity instanceof EntityItem) {
- if (((EntityItem) entity).getPickupDelay() <= 0) {
- Item item = ((EntityItem) entity).getItem();
+ EntityItem entityItem = (EntityItem) entity;
+ if (entityItem.getPickupDelay() <= 0) {
+ Item item = entityItem.getItem();
if (item != null) {
if (!this.isCreative() && !this.inventory.canAddItem(item)) {
@@ -5312,7 +6901,7 @@ public boolean pickupEntity(Entity entity, boolean near) {
}
InventoryPickupItemEvent ev;
- this.server.getPluginManager().callEvent(ev = new InventoryPickupItemEvent(this.inventory, (EntityItem) entity));
+ this.server.getPluginManager().callEvent(ev = new InventoryPickupItemEvent(this.inventory, entityItem));
if (ev.isCancelled()) {
return false;
}
@@ -5323,7 +6912,16 @@ public boolean pickupEntity(Entity entity, boolean near) {
this.awardAchievement("mineWood");
break;
case Item.DIAMOND:
- this.awardAchievement("diamond");
+ this.awardAchievement("diamonds");
+ if (entityItem.droppedBy != null && entityItem.droppedBy != this) {
+ entityItem.droppedBy.awardAchievement("diamondsToYou");
+ }
+ break;
+ case Item.LEATHER:
+ this.awardAchievement("killCow");
+ break;
+ case Item.BLAZE_ROD:
+ this.awardAchievement("blazeRod");
break;
}
@@ -5333,7 +6931,7 @@ public boolean pickupEntity(Entity entity, boolean near) {
Server.broadcastPacket(entity.getViewers().values(), pk);
this.dataPacket(pk);
- this.inventory.addItem(item.clone());
+ this.inventory.addItem(item); // cloned in addItem
entity.close();
return true;
}
@@ -5341,37 +6939,49 @@ public boolean pickupEntity(Entity entity, boolean near) {
}
}
- int tick = this.getServer().getTick();
- if (pickedXPOrb < tick && entity instanceof EntityXPOrb && this.boundingBox.isVectorInside(entity)) {
+ if (pickedXPOrb < server.getTick() && entity instanceof EntityXPOrb && this.boundingBox.isVectorInside(entity)) {
EntityXPOrb xpOrb = (EntityXPOrb) entity;
if (xpOrb.getPickupDelay() <= 0) {
int exp = xpOrb.getExp();
- entity.kill();
+ entity.close();
this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_EXPERIENCE_ORB);
- pickedXPOrb = tick;
+ pickedXPOrb = server.getTick();
- //Mending
ArrayList itemsWithMending = new ArrayList<>();
for (int i = 0; i < 4; i++) {
- if (inventory.getArmorItem(i).getEnchantment((short)Enchantment.ID_MENDING) != null) {
+ if (inventory.getArmorItem(i).hasEnchantment(Enchantment.ID_MENDING)) {
itemsWithMending.add(inventory.getSize() + i);
}
}
- if (inventory.getItemInHand().getEnchantment((short)Enchantment.ID_MENDING) != null) {
+
+ if (inventory.getItemInHandFast().hasEnchantment(Enchantment.ID_MENDING)) {
itemsWithMending.add(inventory.getHeldItemIndex());
}
- if (itemsWithMending.size() > 0) {
- Random rand = new Random();
- Integer itemToRepair = itemsWithMending.get(rand.nextInt(itemsWithMending.size()));
- Item toRepair = inventory.getItem(itemToRepair);
- if (toRepair instanceof ItemTool || toRepair instanceof ItemArmor) {
- if (toRepair.getDamage() > 0) {
- int dmg = toRepair.getDamage() - 2;
+
+ Item offhand = this.getOffhandInventory().getItem(0);
+ if (offhand.getId() == Item.SHIELD && offhand.hasEnchantment(Enchantment.ID_MENDING)) {
+ itemsWithMending.add(-1);
+ }
+
+ if (!itemsWithMending.isEmpty()) {
+ int itemToRepair = itemsWithMending.get(Utils.random.nextInt(itemsWithMending.size()));
+ boolean isOffhand = itemToRepair == -1;
+
+ Item repaired = isOffhand ? offhand : inventory.getItem(itemToRepair);
+ if (repaired instanceof ItemTool || repaired instanceof ItemArmor) {
+ if (repaired.getDamage() > 0) {
+ int dmg = repaired.getDamage() - (exp << 1); // repair 2 points per xp
if (dmg < 0) {
dmg = 0;
}
- toRepair.setDamage(dmg);
- inventory.setItem(itemToRepair, toRepair);
+
+ repaired.setDamage(dmg);
+
+ if (isOffhand) {
+ this.getOffhandInventory().setItem(0, repaired);
+ } else {
+ inventory.setItem(itemToRepair, repaired);
+ }
return true;
}
}
@@ -5403,6 +7013,11 @@ public boolean equals(Object obj) {
return Objects.equals(this.getUniqueId(), other.getUniqueId()) && this.getId() == other.getId();
}
+ /**
+ * Check if the player is currently breaking a block
+ *
+ * @return is breaking a block
+ */
public boolean isBreakingBlock() {
return this.breakingBlock != null;
}
@@ -5470,57 +7085,104 @@ public boolean doesTriggerPressurePlate() {
return this.gamemode != SPECTATOR;
}
- @Override
- public String toString() {
- return "Player(name='" + getName() +
- "', location=" + super.toString() +
- ')';
- }
-
+ /**
+ * Get ticks since sleeping in the current world last time
+ *
+ * @return ticks since sleeping
+ */
public int getTimeSinceRest() {
return timeSinceRest;
}
- public void setTimeSinceRest(int timeSinceRest) {
- this.timeSinceRest = timeSinceRest;
+ /**
+ * Set ticks since sleeping in the current world last time
+ *
+ * @param ticks ticks since sleeping
+ */
+ public void setTimeSinceRest(int ticks) {
+ this.timeSinceRest = ticks;
}
- public NetworkPlayerSession getNetworkSession() {
- return this.networkSession;
+ @Override
+ public String toString() {
+ return "Player(name='" + getName() + "', location=" + super.toString() + ')';
}
- protected void processPreLogin() {
- this.loginVerified = true;
- final Player playerInstance = this;
+ @Override
+ public void setAirTicks(int ticks) {
+ if (this.airTicks != ticks) {
+ if (this.spawned || ticks > this.airTicks) { // Don't consume air before spawned
+ this.airTicks = ticks;
+ this.setDataPropertyAndSendOnlyToSelf(new ShortEntityData(DATA_AIR, ticks));
+ }
+ }
+ }
- this.preLoginEventTask = new AsyncTask() {
- private PlayerAsyncPreLoginEvent event;
+ /**
+ * Send current held item to client
+ */
+ private void syncHeldItem() {
+ InventorySlotPacket pk = new InventorySlotPacket();
+ pk.slot = this.inventory.getHeldItemIndex();
+ pk.item = this.inventory.getItem(pk.slot);
+ pk.inventoryId = ContainerIds.INVENTORY;
+ this.dataPacket(pk);
+ }
- @Override
- public void onRun() {
- this.event = new PlayerAsyncPreLoginEvent(username, uuid, loginChainData, playerInstance.getSkin(), playerInstance.getAddress(), playerInstance.getPort());
- server.getPluginManager().callEvent(this.event);
- }
+ /**
+ * Run every tick to send updated data if needed
+ */
+ void resetPacketCounters() {
+ if (this.needSendAdventureSettings) {
+ this.needSendAdventureSettings = false;
+ this.adventureSettings.update(false);
+ }
+ if (this.needSendData) {
+ this.needSendData = false;
+ this.sendData(this); // Send data only once even if multiple actions fail
+ }
+ if (this.needSendFoodLevel) {
+ this.needSendFoodLevel = false;
+ this.foodData.sendFoodLevel();
+ }
+ if (this.needSendInventory && this.spawned) {
+ this.needSendInventory = false;
+ this.getCursorInventory().sendContents(this);
+ this.sendAllInventories();
+ }
+ if (this.needSendHeldItem && this.spawned) {
+ this.needSendHeldItem = false;
+ this.syncHeldItem();
+ }
+ }
- @Override
- public void onCompletion(Server server) {
- if (!playerInstance.connected) {
- return;
- }
+ /**
+ * Check whether player can eat (difficulty, gamemode, current food level)
+ *
+ * @param update send current food level to client
+ * @return can eat
+ */
+ public boolean canEat(boolean update) {
+ if (this.foodData.getLevel() < this.foodData.getMaxLevel() || this.isCreative() || this.server.getDifficulty() == 0) {
+ return true;
+ }
+ if (update) {
+ this.needSendFoodLevel = true;
+ }
+ return false;
+ }
- if (this.event.getLoginResult() == LoginResult.KICK) {
- playerInstance.close(this.event.getKickMessage(), this.event.getKickMessage());
- } else if (playerInstance.shouldLogin) {
- playerInstance.setSkin(this.event.getSkin());
- playerInstance.completeLoginSequence();
- for (Consumer action : this.event.getScheduledActions()) {
- action.accept(server);
- }
- }
- }
- };
+ /**
+ * Get Player's NetworkPlayerSession
+ *
+ * @return network session
+ */
+ public NetworkPlayerSession getNetworkSession() {
+ return this.networkSession;
+ }
- this.server.getScheduler().scheduleAsyncTask(this.preLoginEventTask);
- this.processLogin();
+ @Override
+ public final boolean canSaveToStorage() {
+ return false;
}
}
diff --git a/src/main/java/cn/nukkit/PlayerFood.java b/src/main/java/cn/nukkit/PlayerFood.java
index 90ff6a9a9ca..20a594980e1 100644
--- a/src/main/java/cn/nukkit/PlayerFood.java
+++ b/src/main/java/cn/nukkit/PlayerFood.java
@@ -9,14 +9,15 @@
import cn.nukkit.potion.Effect;
/**
+ * This class handles player's food.
+ *
* Created by funcraft on 2015/11/11.
*/
public class PlayerFood {
- private int foodLevel = 20;
- private final int maxFoodLevel;
- private float foodSaturationLevel = 20f;
- private int foodTickTimer = 0;
+ private int foodLevel;
+ private float foodSaturationLevel;
+ private short foodTickTimer = 0;
private double foodExpLevel = 0;
private final Player player;
@@ -24,7 +25,6 @@ public class PlayerFood {
public PlayerFood(Player player, int foodLevel, float foodSaturationLevel) {
this.player = player;
this.foodLevel = foodLevel;
- this.maxFoodLevel = 20;
this.foodSaturationLevel = foodSaturationLevel;
}
@@ -37,7 +37,7 @@ public int getLevel() {
}
public int getMaxLevel() {
- return this.maxFoodLevel;
+ return 20;
}
public void setLevel(int foodLevel) {
@@ -53,16 +53,16 @@ public void setLevel(int foodLevel, float saturationLevel) {
foodLevel = 0;
}
- if (foodLevel <= 6 && !(this.getLevel() <= 6)) {
- if (this.getPlayer().isSprinting()) {
- this.getPlayer().setSprinting(false);
+ if (foodLevel <= 6 && !(this.foodLevel <= 6)) {
+ if (this.player.isSprinting()) {
+ this.player.setSprinting(false);
}
}
- PlayerFoodLevelChangeEvent ev = new PlayerFoodLevelChangeEvent(this.getPlayer(), foodLevel, saturationLevel);
- this.getPlayer().getServer().getPluginManager().callEvent(ev);
+ PlayerFoodLevelChangeEvent ev = new PlayerFoodLevelChangeEvent(this.player, foodLevel, saturationLevel);
+ this.player.getServer().getPluginManager().callEvent(ev);
if (ev.isCancelled()) {
- this.sendFoodLevel(this.getLevel());
+ this.sendFoodLevel(this.foodLevel);
return;
}
int foodLevel0 = ev.getFoodLevel();
@@ -81,10 +81,10 @@ public float getFoodSaturationLevel() {
}
public void setFoodSaturationLevel(float fsl) {
- if (fsl > this.getLevel()) fsl = this.getLevel();
+ if (fsl > this.foodLevel) fsl = this.foodLevel;
if (fsl < 0) fsl = 0;
- PlayerFoodLevelChangeEvent ev = new PlayerFoodLevelChangeEvent(this.getPlayer(), this.getLevel(), fsl);
- this.getPlayer().getServer().getPluginManager().callEvent(ev);
+ PlayerFoodLevelChangeEvent ev = new PlayerFoodLevelChangeEvent(this.player, this.foodLevel, fsl);
+ this.player.getServer().getPluginManager().callEvent(ev);
if (ev.isCancelled()) {
return;
}
@@ -97,8 +97,8 @@ public void useHunger() {
}
public void useHunger(int amount) {
- float sfl = this.getFoodSaturationLevel();
- int foodLevel = this.getLevel();
+ float sfl = this.foodSaturationLevel;
+ int foodLevel = this.foodLevel;
if (sfl > 0) {
float newSfl = sfl - amount;
if (newSfl < 0) newSfl = 0;
@@ -113,11 +113,11 @@ public void addFoodLevel(Food food) {
}
public void addFoodLevel(int foodLevel, float fsl) {
- this.setLevel(this.getLevel() + foodLevel, this.getFoodSaturationLevel() + fsl);
+ this.setLevel(this.foodLevel + foodLevel, this.foodSaturationLevel + fsl);
}
public void sendFoodLevel() {
- this.sendFoodLevel(this.getLevel());
+ this.sendFoodLevel(this.foodLevel);
}
public void reset() {
@@ -129,51 +129,52 @@ public void reset() {
}
public void sendFoodLevel(int foodLevel) {
- if (this.getPlayer().spawned) {
- this.getPlayer().setAttribute(Attribute.getAttribute(Attribute.MAX_HUNGER).setValue(foodLevel));
+ if (this.player.spawned) {
+ this.player.setAttribute(Attribute.getAttribute(Attribute.MAX_HUNGER).setValue(foodLevel).setDefaultValue(getMaxLevel()));
}
}
public void update(int tickDiff) {
- if (!this.getPlayer().isFoodEnabled()) return;
- if (this.getPlayer().isAlive()) {
+ if (!this.player.isFoodEnabled()) return;
+ if (this.player.isAlive()) {
int diff = Server.getInstance().getDifficulty();
- if (this.getLevel() > 17) {
+ if (this.foodLevel > 17 || diff == 0) {
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);
+ if (this.player.getHealth() < this.player.getRealMaxHealth()) {
+ EntityRegainHealthEvent ev = new EntityRegainHealthEvent(this.player, 1, EntityRegainHealthEvent.CAUSE_EATING);
+ this.player.heal(ev);
+ this.updateFoodExpLevel(6);
}
this.foodTickTimer = 0;
}
- } else if (this.getLevel() == 0) {
+ } else if (this.foodLevel == 0) {
this.foodTickTimer += tickDiff;
if (this.foodTickTimer >= 80) {
- EntityDamageEvent ev = new EntityDamageEvent(this.getPlayer(), DamageCause.HUNGER, 1);
- float now = this.getPlayer().getHealth();
+ EntityDamageEvent ev = new EntityDamageEvent(this.player, DamageCause.HUNGER, 1);
+ float now = this.player.getHealth();
if (diff == 1) {
- if (now > 10) this.getPlayer().attack(ev);
+ if (now > 10) this.player.attack(ev);
} else if (diff == 2) {
- if (now > 1) this.getPlayer().attack(ev);
+ if (now > 1) this.player.attack(ev);
} else {
- this.getPlayer().attack(ev);
+ this.player.attack(ev);
}
this.foodTickTimer = 0;
}
}
- if (this.getPlayer().hasEffect(Effect.HUNGER)) {
- this.updateFoodExpLevel(0.1 * (this.getPlayer().getEffect(Effect.HUNGER).getAmplifier() + 1));
+ Effect hunger = this.player.getEffect(Effect.HUNGER);
+ if (hunger != null) {
+ this.updateFoodExpLevel(0.1 * (hunger.getAmplifier() + 1));
}
}
}
public void updateFoodExpLevel(double use) {
- if (!this.getPlayer().isFoodEnabled()) return;
+ if (!this.player.isFoodEnabled()) return;
if (Server.getInstance().getDifficulty() == 0) return;
- if (this.getPlayer().hasEffect(Effect.SATURATION)) return;
+ if (this.player.hasEffect(Effect.SATURATION)) return;
this.foodExpLevel += use;
if (this.foodExpLevel > 4) {
this.useHunger(1);
@@ -199,4 +200,9 @@ public void setFoodLevel(int foodLevel) {
public void setFoodLevel(int foodLevel, float saturationLevel) {
setLevel(foodLevel, saturationLevel);
}
+
+ @Override
+ public String toString() {
+ return "PlayerFood(player= " + player + ", foodLevel=" + foodLevel + ", foodSaturationLevel=" + foodSaturationLevel + ", foodTickTimer=" + foodTickTimer + ", foodExpLevel=" + foodExpLevel + ")";
+ }
}
diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java
index fc95456c37c..169c7d69534 100644
--- a/src/main/java/cn/nukkit/Server.java
+++ b/src/main/java/cn/nukkit/Server.java
@@ -4,6 +4,8 @@
import cn.nukkit.blockentity.*;
import cn.nukkit.command.*;
import cn.nukkit.console.NukkitConsole;
+import cn.nukkit.customblock.CustomBlockManager;
+import cn.nukkit.dispenser.DispenseBehaviorRegister;
import cn.nukkit.entity.Attribute;
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.EntityHuman;
@@ -36,12 +38,8 @@
import cn.nukkit.level.format.LevelProvider;
import cn.nukkit.level.format.LevelProviderManager;
import cn.nukkit.level.format.anvil.Anvil;
-import cn.nukkit.level.format.leveldb.LevelDB;
-import cn.nukkit.level.format.mcregion.McRegion;
-import cn.nukkit.level.generator.Flat;
-import cn.nukkit.level.generator.Generator;
-import cn.nukkit.level.generator.Nether;
-import cn.nukkit.level.generator.Normal;
+import cn.nukkit.level.format.leveldb.LevelDBProvider;
+import cn.nukkit.level.generator.*;
import cn.nukkit.math.NukkitMath;
import cn.nukkit.metadata.EntityMetadataStore;
import cn.nukkit.metadata.LevelMetadataStore;
@@ -52,11 +50,10 @@
import cn.nukkit.nbt.tag.DoubleTag;
import cn.nukkit.nbt.tag.FloatTag;
import cn.nukkit.nbt.tag.ListTag;
-import cn.nukkit.network.CompressBatchedTask;
+import cn.nukkit.network.BatchingHelper;
import cn.nukkit.network.Network;
import cn.nukkit.network.RakNetInterface;
import cn.nukkit.network.SourceInterface;
-import cn.nukkit.network.protocol.BatchPacket;
import cn.nukkit.network.protocol.DataPacket;
import cn.nukkit.network.protocol.PlayerListPacket;
import cn.nukkit.network.protocol.ProtocolInfo;
@@ -79,7 +76,6 @@
import cn.nukkit.scheduler.Task;
import cn.nukkit.utils.*;
import cn.nukkit.utils.bugreport.ExceptionHandler;
-import co.aikar.timings.Timings;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import io.netty.buffer.ByteBuf;
@@ -89,13 +85,8 @@
import org.iq80.leveldb.Options;
import org.iq80.leveldb.impl.Iq80DBFactory;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
+import java.io.*;
+import java.net.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
@@ -104,107 +95,75 @@
import java.util.regex.Pattern;
/**
+ * The main server class
+ *
* @author MagicDroidX
* @author Box
*/
@Log4j2
public class Server {
+ /**
+ * Permission to receive admin broadcasts such as command usage.
+ */
public static final String BROADCAST_CHANNEL_ADMINISTRATIVE = "nukkit.broadcast.admin";
+ /**
+ * Permission to receive common broadcasts such as join/quit/death/achievement messages.
+ */
public static final String BROADCAST_CHANNEL_USERS = "nukkit.broadcast.user";
- private static Server instance = null;
-
- private BanList banByName;
-
- private BanList banByIP;
-
- private Config operators;
-
- private Config whitelist;
+ private static Server instance;
- private AtomicBoolean isRunning = new AtomicBoolean(true);
+ private final BanList banByName;
+ private final BanList banByIP;
+ private final Config operators;
+ private final Config whitelist;
+ private final Config properties;
+ private final Config config;
- private boolean hasStopped = false;
-
- private PluginManager pluginManager;
+ private final String filePath;
+ private final String dataPath;
+ private final String pluginPath;
- private ServerScheduler scheduler;
+ private final PluginManager pluginManager;
+ private final ServerScheduler scheduler;
+ private final BaseLang baseLang;
+ private final NukkitConsole console;
+ private final ConsoleThread consoleThread;
+ private final SimpleCommandMap commandMap;
+ private final CraftingManager craftingManager;
+ private final ResourcePackManager resourcePackManager;
+ private final ConsoleCommandSender consoleSender;
+ private boolean hasStopped;
+ private final AtomicBoolean isRunning = new AtomicBoolean(true);
private int tickCounter;
-
private long nextTick;
-
private final float[] tickAverage = {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20};
-
private final float[] useAverage = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
private float maxTick = 20;
-
private float maxUse = 0;
-
- private final NukkitConsole console;
- private final ConsoleThread consoleThread;
-
- private SimpleCommandMap commandMap;
-
- private CraftingManager craftingManager;
-
- private ResourcePackManager resourcePackManager;
-
- private ConsoleCommandSender consoleSender;
-
- private int maxPlayers;
-
- private boolean autoSave = true;
-
- private RCON rcon;
-
- private EntityMetadataStore entityMetadata;
-
- private PlayerMetadataStore playerMetadata;
-
- private LevelMetadataStore levelMetadata;
-
- private Network network;
-
- private boolean networkCompressionAsync;
- public int networkCompressionLevel;
- private int networkZlibProvider;
- public int networkCompressionThreshold;
- public boolean encryptionEnabled;
-
- private boolean autoTickRate;
- private int autoTickRateLimit;
- private boolean alwaysTickPlayers;
private int baseTickRate;
- private Boolean getAllowFlight = null;
- private int difficulty = Integer.MAX_VALUE;
- private int defaultGamemode = Integer.MAX_VALUE;
-
- private int autoSaveTicker = 0;
- private int autoSaveTicks = 6000;
-
- private BaseLang baseLang;
-
- private boolean forceLanguage;
-
- private UUID serverID;
-
- private final String filePath;
- private final String dataPath;
- private final String pluginPath;
-
+ private int autoSaveTicker;
+ private int maxPlayers; // setMaxPlayers
+ private boolean autoSave = true; // setAutoSave
+ private int difficulty; // setDifficulty
+ int c_s_spawnThreshold;
+ private String ip;
+ private int port;
+ private final UUID serverID;
+ private RCON rcon;
+ private final Network network;
private QueryHandler queryHandler;
-
private QueryRegenerateEvent queryRegenerateEvent;
-
- private Config properties;
- private Config config;
+ private final EntityMetadataStore entityMetadata;
+ private final PlayerMetadataStore playerMetadata;
+ private final LevelMetadataStore levelMetadata;
private final Map players = new HashMap<>();
+ final Map playerList = new HashMap<>();
- private final Map playerList = new HashMap<>();
+ private static final Pattern uuidPattern = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}.dat$");
private final Map levels = new HashMap() {
public Level put(Integer key, Level value) {
@@ -212,13 +171,11 @@ public Level put(Integer key, Level value) {
levelArray = levels.values().toArray(new Level[0]);
return result;
}
-
public boolean remove(Object key, Object value) {
boolean result = super.remove(key, value);
levelArray = levels.values().toArray(new Level[0]);
return result;
}
-
public Level remove(Object key) {
Level result = super.remove(key);
levelArray = levels.values().toArray(new Level[0]);
@@ -227,22 +184,154 @@ public Level remove(Object key) {
};
private Level[] levelArray = new Level[0];
-
private final ServiceManager serviceManager = new NKServiceManager();
-
- private Level defaultLevel = null;
-
- private boolean allowNether;
-
+ private Level defaultLevel;
private final Thread currentThread;
-
private Watchdog watchdog;
-
- private DB nameLookup;
-
+ private final DB nameLookup;
private PlayerDataSerializer playerDataSerializer;
+ private final BatchingHelper batchingHelper;
- private final Set ignoredPackets = new HashSet<>();
+ /**
+ * The server's MOTD. Remember to call network.setName() when updated.
+ */
+ public String motd;
+ /**
+ * Default player data saving enabled.
+ */
+ public boolean shouldSavePlayerData;
+ /**
+ * Anti fly checks enabled.
+ */
+ public boolean allowFlight;
+ /**
+ * Hardcore mode enabled.
+ */
+ public boolean isHardcore;
+ /**
+ * Force resource packs.
+ */
+ public boolean forceResources;
+ /**
+ * Force player gamemode to default on every join.
+ */
+ public boolean forceGamemode;
+ /**
+ * The nether dimension and portals enabled.
+ */
+ public boolean netherEnabled;
+ /**
+ * Whitelist enabled.
+ */
+ public boolean whitelistEnabled;
+ /**
+ * Xbox authentication enabled.
+ */
+ public boolean xboxAuth;
+ /**
+ * Server side achievements enabled.
+ */
+ public boolean achievementsEnabled;
+ /**
+ * The end dimension and portals enabled.
+ */
+ public boolean endEnabled;
+ /**
+ * Pvp enabled. Can be changed per world using game rules.
+ */
+ public boolean pvpEnabled;
+ /**
+ * Announce server side announcements to all players.
+ */
+ public boolean announceAchievements;
+ /**
+ * How many chunks are sent to player per tick.
+ */
+ public int chunksPerTick;
+ /**
+ * How many chunks needs to be sent before the player can spawn.
+ */
+ public int spawnThreshold;
+ /**
+ * Zlib compression level for sent packets.
+ */
+ public int networkCompressionLevel;
+ /**
+ * Maximum view distance in chunks.
+ */
+ public int viewDistance;
+ /**
+ * Server's default gamemode.
+ */
+ public int gamemode;
+ /**
+ * Minimum amount of time between player skin changes.
+ */
+ public int skinChangeCooldown;
+ /**
+ * Spawn protection radius.
+ */
+ public int spawnRadius;
+ /**
+ * How often auto save should happen.
+ */
+ public int autoSaveTicks;
+ /**
+ * Limit automatic tick rate.
+ */
+ public int autoTickRateLimit;
+ /**
+ * Set LevelDB cache size.
+ */
+ public int levelDbCache;
+ /**
+ * Use native LevelDB implementation for better performance.
+ */
+ public boolean useNativeLevelDB;
+ /**
+ * Showing plugins in query enabled.
+ */
+ public boolean queryPlugins;
+ /**
+ * Chunk caching enabled.
+ */
+ public boolean cacheChunks;
+ /**
+ * Whether attacking an entity should stop player from sprinting.
+ */
+ public boolean attackStopSprint;
+ /**
+ * Enable automatic tick rate adjustments.
+ */
+ public boolean autoTickRate;
+ /**
+ * Force server side translations.
+ */
+ public boolean forceLanguage;
+ /**
+ * Always tick players.
+ */
+ public boolean alwaysTickPlayers;
+ /**
+ * Don't disable client's own packs when force-resources is enabled.
+ */
+ public boolean forceResourcesAllowOwnPacks;
+ /**
+ * Enable encryption.
+ */
+ public boolean encryptionEnabled;
+ /**
+ * Use Snappy for packet compression for 1.19.30+ clients.
+ */
+ public final boolean useSnappy;
+ /**
+ * Batch packets smaller than this will not be compressed.
+ */
+ public int networkCompressionThreshold;
+ /**
+ * Temporary disable world saving to allow safe backup of leveldb worlds.
+ */
+ public boolean holdWorldSave;
Server(final String filePath, String dataPath, String pluginPath, String predefinedLanguage) {
Preconditions.checkState(instance == null, "Already initialized!");
@@ -262,16 +351,14 @@ public Level remove(Object key) {
new File(pluginPath).mkdirs();
}
- this.dataPath = new File(dataPath).getAbsolutePath() + "/";
- this.pluginPath = new File(pluginPath).getAbsolutePath() + "/";
-
- this.console = new NukkitConsole(this);
- this.consoleThread = new ConsoleThread();
- this.consoleThread.start();
+ this.dataPath = new File(dataPath).getAbsolutePath() + '/';
+ this.pluginPath = new File(pluginPath).getAbsolutePath() + '/';
this.playerDataSerializer = new DefaultPlayerDataSerializer(this);
- //todo: VersionString 现在不必要
+ this.console = new NukkitConsole();
+ this.consoleThread = new ConsoleThread();
+ this.consoleThread.start();
if (!new File(this.dataPath + "nukkit.yml").exists()) {
this.getLogger().info(TextFormat.GREEN + "Welcome! Please choose a language first!");
@@ -318,14 +405,12 @@ public Level remove(Object key) {
} catch (IOException e) {
throw new RuntimeException(e);
}
-
}
- this.console.setExecutingCommands(true);
-
- log.info("Loading {} ...", TextFormat.GREEN + "nukkit.yml" + TextFormat.WHITE);
this.config = new Config(this.dataPath + "nukkit.yml", Config.YAML);
+ log.info("Loading server properties...");
+
Nukkit.DEBUG = NukkitMath.clamp(this.getConfig("debug.level", 1), 1, 3);
int logLevel = (Nukkit.DEBUG + 3) * 100;
@@ -337,53 +422,22 @@ public Level remove(Object key) {
}
}
- ignoredPackets.addAll(getConfig().getStringList("debug.ignored-packets"));
- ignoredPackets.add("BatchPacket");
-
- log.info("Loading {} ...", TextFormat.GREEN + "server.properties" + TextFormat.WHITE);
- this.properties = new Config(this.dataPath + "server.properties", Config.PROPERTIES, new ConfigSection() {
- {
- put("motd", "A Nukkit Powered Server");
- put("sub-motd", "https://nukkitx.com");
- put("server-port", 19132);
- put("server-ip", "0.0.0.0");
- put("view-distance", 10);
- put("white-list", false);
- put("achievements", true);
- put("announce-player-achievements", true);
- put("spawn-protection", 16);
- put("max-players", 20);
- put("allow-flight", false);
- put("spawn-animals", true);
- put("spawn-mobs", true);
- put("gamemode", 0);
- put("force-gamemode", false);
- put("hardcore", false);
- put("pvp", true);
- put("difficulty", 1);
- put("generator-settings", "");
- put("level-name", "world");
- put("level-seed", "");
- put("level-type", "DEFAULT");
- put("allow-nether", true);
- put("enable-query", true);
- put("enable-rcon", false);
- put("rcon.password", Base64.getEncoder().encodeToString(UUID.randomUUID().toString().replace("-", "").getBytes()).substring(3, 13));
- put("auto-save", true);
- put("force-resources", false);
- put("xbox-auth", true);
- }
- });
+ //ignoredPackets.addAll(getConfig().getStringList("debug.ignored-packets"));
+ //ignoredPackets.add("BatchPacket");
- // Allow Nether? (determines if we create a nether world if one doesn't exist on startup)
- this.allowNether = this.properties.getBoolean("allow-nether", true);
+ this.properties = new Config(this.dataPath + "server.properties", Config.PROPERTIES, new ServerProperties());
+
+ // Should not be modified after startup
+ this.useSnappy = this.getConfig("network.compression-use-snappy", false);
- this.forceLanguage = this.getConfig("settings.force-language", false);
this.baseLang = new BaseLang(this.getConfig("settings.language", BaseLang.FALLBACK_LANGUAGE));
+
+ this.loadSettings();
+
log.info(this.getLanguage().translateString("language.selected", new String[]{getLanguage().getName(), getLanguage().getLang()}));
log.info(getLanguage().translateString("nukkit.server.start", TextFormat.AQUA + this.getVersion() + TextFormat.RESET));
- Object poolSize = this.getConfig("settings.async-workers", (Object) "auto");
+ Object poolSize = this.getConfig("settings.async-workers", "auto");
if (!(poolSize instanceof Integer)) {
try {
poolSize = Integer.valueOf((String) poolSize);
@@ -394,30 +448,17 @@ public Level remove(Object key) {
ServerScheduler.WORKERS = (int) poolSize;
- this.networkZlibProvider = this.getConfig("network.zlib-provider", 2);
- Zlib.setProvider(this.networkZlibProvider);
-
- this.networkCompressionLevel = this.getConfig("network.compression-level", 7);
- this.networkCompressionAsync = this.getConfig("network.async-compression", true);
- this.networkCompressionThreshold = this.getConfig("network.batch-threshold", 256);
- this.encryptionEnabled = this.getConfig("network.encryption", false);
-
- if (!this.encryptionEnabled) {
- this.getLogger().warning("Encryption is not enabled. For better security, it's recommended to enable it (network.encryption=true in nukkit.yml) if you don't use a proxy software.");
- }
+ this.scheduler = new ServerScheduler();
- this.autoTickRate = this.getConfig("level-settings.auto-tick-rate", true);
- this.autoTickRateLimit = this.getConfig("level-settings.auto-tick-rate-limit", 20);
- this.alwaysTickPlayers = this.getConfig("level-settings.always-tick-players", false);
- this.baseTickRate = this.getConfig("level-settings.base-tick-rate", 1);
+ this.console.setExecutingCommands(true); // Scheduler needs to be ready
- this.scheduler = new ServerScheduler();
+ this.batchingHelper = new BatchingHelper();
if (this.getPropertyBoolean("enable-rcon", false)) {
try {
- this.rcon = new RCON(this, this.getPropertyString("rcon.password", ""), (!this.getIp().equals("")) ? this.getIp() : "0.0.0.0", this.getPropertyInt("rcon.port", this.getPort()));
+ this.rcon = new RCON(this, this.getPropertyString("rcon.password", ""), (!this.getIp().isEmpty()) ? this.getIp() : "0.0.0.0", this.getPropertyInt("rcon.port", this.getPort()));
} catch (IllegalArgumentException e) {
- log.error(getLanguage().translateString(e.getMessage(), e.getCause().getMessage()));
+ log.error(baseLang.translateString(e.getMessage(), e.getCause().getMessage()));
}
}
@@ -432,64 +473,37 @@ public Level remove(Object key) {
this.banByIP = new BanList(this.dataPath + "banned-ips.json");
this.banByIP.load();
- this.maxPlayers = this.getPropertyInt("max-players", 20);
- this.setAutoSave(this.getPropertyBoolean("auto-save", true));
-
- if (this.getPropertyBoolean("hardcore", false) && this.getDifficulty() < 3) {
- this.setPropertyInt("difficulty", 3);
- }
-
- boolean bugReport;
- if (this.getConfig().exists("settings.bug-report")) {
- bugReport = this.getConfig().getBoolean("settings.bug-report");
- this.getProperties().remove("bug-report");
- } else {
- bugReport = this.getPropertyBoolean("bug-report", true); //backwards compat
- }
- if (bugReport) {
- ExceptionHandler.registerExceptionHandler();
- }
-
- log.info(this.getLanguage().translateString("nukkit.server.networkStart", new String[]{this.getIp().equals("") ? "*" : this.getIp(), String.valueOf(this.getPort())}));
- this.serverID = UUID.randomUUID();
-
- this.network = new Network(this);
- this.network.setName(this.getMotd());
- this.network.setSubName(this.getSubMotd());
-
- log.info(this.getLanguage().translateString("nukkit.server.info", this.getName(), TextFormat.YELLOW + this.getNukkitVersion() + TextFormat.WHITE, TextFormat.AQUA + this.getCodename() + TextFormat.WHITE, this.getApiVersion()));
- log.info(this.getLanguage().translateString("nukkit.server.license", this.getName()));
-
this.consoleSender = new ConsoleCommandSender();
this.commandMap = new SimpleCommandMap(this);
- // Initialize metrics
- new NukkitMetrics(this);
-
- this.registerEntities();
- this.registerBlockEntities();
+ registerEntities();
+ registerBlockEntities();
Block.init();
Enchantment.init();
+ GlobalBlockPalette.init();
RuntimeItems.init();
Item.init();
- EnumBiome.values(); //load class, this also registers biomes
+ EnumBiome.values();
Effect.init();
Potion.init();
Attribute.init();
- GlobalBlockPalette.getOrCreateRuntimeId(0, 0); //Force it to load
+ DispenseBehaviorRegister.init();
+ CustomBlockManager.init(this);
- // Convert legacy data before plugins get the chance to mess with it.
+ // Convert legacy data before plugins get the chance to mess with it
try {
nameLookup = Iq80DBFactory.factory.open(new File(dataPath, "players"), new Options()
- .createIfMissing(true)
- .compressionType(CompressionType.ZLIB_RAW));
+ .createIfMissing(true)
+ .compressionType(CompressionType.ZLIB_RAW));
} catch (IOException e) {
throw new RuntimeException(e);
}
convertLegacyPlayerData();
+ this.serverID = UUID.randomUUID();
+
this.craftingManager = new CraftingManager();
this.resourcePackManager = new ResourcePackManager(new File(Nukkit.DATA_PATH, "resource_packs"));
@@ -500,21 +514,49 @@ public Level remove(Object key) {
this.queryRegenerateEvent = new QueryRegenerateEvent(this, 5);
+ log.info(this.baseLang.translateString("nukkit.server.networkStart", new String[]{this.getIp().isEmpty() ? "*" : this.getIp(), String.valueOf(this.getPort())}));
+ this.network = new Network(this);
+ this.network.setName(this.getMotd());
+ this.network.setSubName(this.getSubMotd());
this.network.registerInterface(new RakNetInterface(this));
- this.pluginManager.loadPlugins(this.pluginPath);
+ if (!this.encryptionEnabled) {
+ this.getLogger().warning("Encryption is not enabled! For better security, it's recommended to enable it (network.encryption: true in nukkit.yml) if you don't use a proxy software.");
+ }
+
+ if (!this.xboxAuth) {
+ this.getLogger().warning("Xbox authentication is not enabled! It's recommended to enable it (xbox-auth=on in server.properties) if you don't use a proxy software or an authentication plugin.");
+ }
+
+ log.info(this.getLanguage().translateString("nukkit.server.info", this.getName(), TextFormat.YELLOW + this.getNukkitVersion() + TextFormat.WHITE, TextFormat.AQUA + this.getCodename() + TextFormat.WHITE, this.getApiVersion()));
+ log.info(this.getLanguage().translateString("nukkit.server.license", this.getName()));
+
+ ExceptionHandler.registerExceptionHandler();
+ this.pluginManager.loadPlugins(this.pluginPath);
this.enablePlugins(PluginLoadOrder.STARTUP);
+ try {
+ if (CustomBlockManager.get().closeRegistry()) {
+ RuntimeItems.getMapping().generatePalette();
+ }
+
+ Item.initCreativeItems();
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to init custom blocks", e);
+ }
+
+ craftingManager.rebuildPacket();
+
LevelProviderManager.addProvider(this, Anvil.class);
- LevelProviderManager.addProvider(this, McRegion.class);
- LevelProviderManager.addProvider(this, LevelDB.class);
+ LevelProviderManager.addProvider(this, LevelDBProvider.class);
Generator.addGenerator(Flat.class, "flat", Generator.TYPE_FLAT);
Generator.addGenerator(Normal.class, "normal", Generator.TYPE_INFINITE);
Generator.addGenerator(Normal.class, "default", Generator.TYPE_INFINITE);
Generator.addGenerator(Nether.class, "nether", Generator.TYPE_NETHER);
- //todo: add old generator and hell generator
+ Generator.addGenerator(End.class, "the_end", Generator.TYPE_THE_END);
+ Generator.addGenerator(cn.nukkit.level.generator.Void.class, "void", Generator.TYPE_VOID);
for (String name : this.getConfig("worlds", new HashMap()).keySet()) {
if (!this.loadLevel(name)) {
@@ -564,20 +606,22 @@ public Level remove(Object key) {
this.setDefaultLevel(this.getLevelByName(defaultName));
}
- this.properties.save(true);
-
- if (this.getDefaultLevel() == null) {
- this.getLogger().emergency(this.getLanguage().translateString("nukkit.level.defaultError"));
+ if (this.defaultLevel == null) {
+ this.getLogger().emergency(this.baseLang.translateString("nukkit.level.defaultError"));
this.forceShutdown();
-
return;
}
- EnumLevel.initLevels();
+ this.properties.save(true);
- if (this.getConfig("ticks-per.autosave", 6000) > 0) {
- this.autoSaveTicks = this.getConfig("ticks-per.autosave", 6000);
- }
+ //for (Map.Entry entry : this.getLevels().entrySet()) {
+ Level level = this.defaultLevel;//entry.getValue();
+ this.getLogger().debug("Preparing spawn region for level " + level.getName());
+ Position spawn = level.getSpawnLocation();
+ level.populateChunk(spawn.getChunkX(), spawn.getChunkZ(), true);
+ //}
+
+ EnumLevel.initLevels();
this.enablePlugins(PluginLoadOrder.POSTWORLD);
@@ -586,6 +630,9 @@ public Level remove(Object key) {
this.watchdog.start();
}
+ // Initialize metrics
+ new NukkitMetrics(this);
+
this.start();
}
@@ -658,81 +705,35 @@ public int broadcast(TextContainer message, String permissions) {
}
public static void broadcastPacket(Collection players, DataPacket packet) {
- packet.tryEncode();
-
for (Player player : players) {
player.dataPacket(packet);
}
}
public static void broadcastPacket(Player[] players, DataPacket packet) {
- packet.tryEncode();
-
for (Player player : players) {
player.dataPacket(packet);
}
}
- @Deprecated
public void batchPackets(Player[] players, DataPacket[] packets) {
- this.batchPackets(players, packets, false);
- }
-
- @Deprecated
- public void batchPackets(Player[] players, DataPacket[] packets, boolean forceSync) {
if (players == null || packets == null || players.length == 0 || packets.length == 0) {
return;
}
- BatchPacketsEvent ev = new BatchPacketsEvent(players, packets, forceSync);
- getPluginManager().callEvent(ev);
+ BatchPacketsEvent ev = new BatchPacketsEvent(players, packets, true);
+ pluginManager.callEvent(ev);
if (ev.isCancelled()) {
return;
}
- Timings.playerNetworkSendTimer.startTiming();
- byte[][] payload = new byte[packets.length * 2][];
- for (int i = 0; i < packets.length; i++) {
- DataPacket p = packets[i];
- int idx = i * 2;
- p.tryEncode();
- byte[] buf = p.getBuffer();
- payload[idx] = Binary.writeUnsignedVarInt(buf.length);
- payload[idx + 1] = buf;
- packets[i] = null;
- }
-
- List targets = new ArrayList<>();
- for (Player p : players) {
- if (p.isConnected()) {
- targets.add(p.getSocketAddress());
- }
- }
-
- if (!forceSync && this.networkCompressionAsync) {
- this.getScheduler().scheduleAsyncTask(new CompressBatchedTask(payload, targets, this.networkCompressionLevel));
- } else {
- try {
- byte[] data = Binary.appendBytes(payload);
- this.broadcastPacketsCallback(Network.deflateRaw(data, this.networkCompressionLevel), targets);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- Timings.playerNetworkSendTimer.stopTiming();
- }
-
- public void broadcastPacketsCallback(byte[] data, List targets) {
- BatchPacket pk = new BatchPacket();
- pk.payload = data;
-
- for (InetSocketAddress i : targets) {
- if (this.players.containsKey(i)) {
- this.players.get(i).dataPacket(pk);
- }
- }
+ this.batchingHelper.batchPackets(players, packets);
}
+ /**
+ * Enable all plugins with matching load order
+ * @param type load order
+ */
public void enablePlugins(PluginLoadOrder type) {
for (Plugin plugin : new ArrayList<>(this.pluginManager.getPlugins().values())) {
if (!plugin.isEnabled() && type == plugin.getDescription().getOrder()) {
@@ -746,21 +747,33 @@ public void enablePlugins(PluginLoadOrder type) {
}
}
+ /**
+ * Enable a plugin
+ * @param plugin plugin
+ */
public void enablePlugin(Plugin plugin) {
this.pluginManager.enablePlugin(plugin);
}
+ /**
+ * Disable all loaded plugins
+ */
public void disablePlugins() {
this.pluginManager.disablePlugins();
}
+ /**
+ * Run a command as CommandSender. Use server.getConsoleSender() to run as CONSOLE.
+ * @param sender command sender
+ * @param commandLine command without slash
+ * @return command was found and attempted to be executed
+ */
public boolean dispatchCommand(CommandSender sender, String commandLine) throws ServerException {
// First we need to check if this command is on the main thread or not, if not, warn the user
if (!this.isPrimaryThread()) {
- getLogger().warning("Command Dispatched Async: " + commandLine);
- getLogger().warning("Please notify author of plugin causing this execution to fix this bug!", new Throwable());
- // TODO: We should sync the command to the main thread too!
+ getLogger().warning("Command dispatched asynchronously: " + commandLine);
}
+
if (sender == null) {
throw new ServerException("CommandSender is not valid");
}
@@ -774,57 +787,68 @@ public boolean dispatchCommand(CommandSender sender, String commandLine) throws
return false;
}
- //todo: use ticker to check console
+ /**
+ * Get server console CommandSender
+ * @return ConsoleCommandSender
+ */
public ConsoleCommandSender getConsoleSender() {
return consoleSender;
}
+ /**
+ * Reload the server. Notice: may cause issues with some plugins.
+ */
public void reload() {
- log.info("Reloading...");
-
log.info("Saving levels...");
-
for (Level level : this.levelArray) {
level.save();
}
- this.pluginManager.disablePlugins();
this.pluginManager.clearPlugins();
this.commandMap.clearCommands();
- log.info("Reloading properties...");
+ log.info("Reloading server properties...");
this.properties.reload();
- this.maxPlayers = this.getPropertyInt("max-players", 20);
-
- if (this.getPropertyBoolean("hardcore", false) && this.getDifficulty() < 3) {
- this.setPropertyInt("difficulty", difficulty = 3);
- }
+ this.loadSettings();
this.banByIP.load();
this.banByName.load();
this.reloadWhitelist();
this.operators.reload();
- for (BanEntry entry : this.getIPBans().getEntires().values()) {
+ for (BanEntry entry : this.banByIP.getEntires().values()) {
try {
- this.getNetwork().blockAddress(InetAddress.getByName(entry.getName()), -1);
- } catch (UnknownHostException e) {
- // ignore
- }
+ this.network.blockAddress(InetAddress.getByName(entry.getName()));
+ } catch (UnknownHostException ignore) {}
}
+ log.info("Reloading plugins...");
this.pluginManager.registerInterface(JavaPluginLoader.class);
this.pluginManager.loadPlugins(this.pluginPath);
this.enablePlugins(PluginLoadOrder.STARTUP);
this.enablePlugins(PluginLoadOrder.POSTWORLD);
- Timings.reset();
}
+ /**
+ * Mark the server to be shut down.
+ */
public void shutdown() {
isRunning.compareAndSet(true, false);
}
+ /**
+ * Shut down the server immediately.
+ */
public void forceShutdown() {
+ this.forceShutdown(this.getConfig("settings.shutdown-message", "Server closed"));
+ }
+
+ /**
+ * Shut down the server immediately.
+ *
+ * @param reason message that shows to players on disconnect
+ */
+ public void forceShutdown(String reason) {
if (this.hasStopped) {
return;
}
@@ -835,103 +859,117 @@ public void forceShutdown() {
this.hasStopped = true;
ServerStopEvent serverStopEvent = new ServerStopEvent();
- getPluginManager().callEvent(serverStopEvent);
+ pluginManager.callEvent(serverStopEvent);
+
+ if (this.holdWorldSave) {
+ this.getLogger().warning("World save hold was not released! Any backup currently being taken may be invalid");
+ }
if (this.rcon != null) {
+ this.getLogger().debug("Closing RCON...");
this.rcon.close();
}
+ this.getLogger().debug("Disconnecting all players...");
for (Player player : new ArrayList<>(this.players.values())) {
- player.close(player.getLeaveMessage(), this.getConfig("settings.shutdown-message", "Server closed"));
+ player.close(player.getLeaveMessage(), reason);
}
- this.getLogger().debug("Disabling all plugins");
- this.pluginManager.disablePlugins();
+ this.getLogger().debug("Disabling all plugins...");
+ this.disablePlugins();
- this.getLogger().debug("Removing event handlers");
+ this.getLogger().debug("Removing event handlers...");
HandlerList.unregisterAll();
- this.getLogger().debug("Stopping all tasks");
+ this.getLogger().debug("Stopping all tasks...");
this.scheduler.cancelAllTasks();
this.scheduler.mainThreadHeartbeat(Integer.MAX_VALUE);
- this.getLogger().debug("Unloading all levels");
+ this.getLogger().debug("Unloading all levels...");
for (Level level : this.levelArray) {
this.unloadLevel(level, true);
+ this.nextTick = System.currentTimeMillis(); // Fix Watchdog killing the server while saving worlds
}
- this.getLogger().debug("Closing console");
+ this.getLogger().debug("Closing console...");
this.consoleThread.interrupt();
- this.getLogger().debug("Stopping network interfaces");
+ this.getLogger().debug("Closing BatchingHelper...");
+ this.batchingHelper.shutdown();
+
+ this.getLogger().debug("Stopping network interfaces...");
for (SourceInterface interfaz : this.network.getInterfaces()) {
interfaz.shutdown();
this.network.unregisterInterface(interfaz);
}
if (nameLookup != null) {
+ this.getLogger().debug("Closing name lookup DB...");
nameLookup.close();
}
- this.getLogger().debug("Disabling timings");
- Timings.stopServer();
+ this.getLogger().debug("Stopping Watchdog...");
if (this.watchdog != null) {
this.watchdog.kill();
}
- //todo other things
} catch (Exception e) {
log.fatal("Exception happened while shutting down, exiting the process", e);
System.exit(1);
}
}
+ /**
+ * Internal: Start the server
+ */
public void start() {
- if (this.getPropertyBoolean("enable-query", true)) {
+ if (this.getPropertyBoolean("enable-query", false)) {
this.queryHandler = new QueryHandler();
}
- for (BanEntry entry : this.getIPBans().getEntires().values()) {
+ for (BanEntry entry : this.banByIP.getEntires().values()) {
try {
- this.network.blockAddress(InetAddress.getByName(entry.getName()), -1);
- } catch (UnknownHostException e) {
- // ignore
- }
+ this.network.blockAddress(InetAddress.getByName(entry.getName()));
+ } catch (UnknownHostException ignore) {}
}
- //todo send usage setting
this.tickCounter = 0;
- log.info(this.getLanguage().translateString("nukkit.server.defaultGameMode", getGamemodeString(this.getGamemode())));
+ //log.info(this.getLanguage().translateString("nukkit.server.defaultGameMode", getGamemodeString(this.getGamemode())));
- log.info(this.getLanguage().translateString("nukkit.server.startFinished", String.valueOf((double) (System.currentTimeMillis() - Nukkit.START_TIME) / 1000)));
+ log.info(this.baseLang.translateString("nukkit.server.startFinished", String.valueOf((double) (System.currentTimeMillis() - Nukkit.START_TIME) / 1000)));
this.tickProcessor();
this.forceShutdown();
}
+ private static final byte[] PREFIX = {(byte) 0xfe, (byte) 0xfd};
+
+ /**
+ * Internal: Handle query
+ * @param address sender address
+ * @param payload payload
+ */
public void handlePacket(InetSocketAddress address, ByteBuf payload) {
try {
- if (!payload.isReadable(3)) {
+ if (this.queryHandler == null || !payload.isReadable(3)) {
return;
}
byte[] prefix = new byte[2];
payload.readBytes(prefix);
-
- if (!Arrays.equals(prefix, new byte[]{(byte) 0xfe, (byte) 0xfd})) {
- return;
- }
- if (this.queryHandler != null) {
+ if (Arrays.equals(prefix, PREFIX)) {
this.queryHandler.handle(address, payload);
}
} catch (Exception e) {
log.error("Error whilst handling packet", e);
-
- this.network.blockAddress(address.getAddress(), -1);
+ this.network.blockAddress(address.getAddress(), 300);
}
}
private int lastLevelGC;
+ /**
+ * Internal: Tick the server
+ */
public void tickProcessor() {
this.nextTick = System.currentTimeMillis();
try {
@@ -945,22 +983,25 @@ public void tickProcessor() {
if (next - 0.1 > current) {
long allocated = next - current - 1;
- { // Instead of wasting time, do something potentially useful
- int offset = 0;
- for (int i = 0; i < levelArray.length; i++) {
- offset = (i + lastLevelGC) % levelArray.length;
- Level level = levelArray[offset];
+ // Instead of wasting time, do something potentially useful
+ int offset = 0;
+ for (int i = 0; i < levelArray.length; i++) {
+ offset = (i + lastLevelGC) % levelArray.length;
+ Level level = levelArray[offset];
+ if (!level.isBeingConverted) {
level.doGarbageCollection(allocated - 1);
- allocated = next - System.currentTimeMillis();
- if (allocated <= 0) {
- break;
- }
}
- lastLevelGC = offset + 1;
+ allocated = next - System.currentTimeMillis();
+ if (allocated <= 0) break;
}
+ lastLevelGC = offset + 1;
if (allocated > 0) {
- Thread.sleep(allocated, 900000);
+ try {
+ Thread.sleep(allocated, 900000);
+ } catch (Exception e) {
+ this.getLogger().logException(e);
+ }
}
}
} catch (RuntimeException e) {
@@ -974,11 +1015,8 @@ public void tickProcessor() {
}
public void onPlayerCompleteLoginSequence(Player player) {
- this.sendFullPlayerListData(player);
- }
-
- public void onPlayerLogin(Player player) {
-
+ this.playerList.put(player.getUniqueId(), player);
+ this.updatePlayerListData(player.getUniqueId(), player.getId(), player.getDisplayName(), player.getSkin(), player.getLoginChainData().getXUID());
}
public void addPlayer(InetSocketAddress socketAddress, Player player) {
@@ -991,9 +1029,7 @@ public void addOnlinePlayer(Player player) {
}
public void removeOnlinePlayer(Player player) {
- if (this.playerList.containsKey(player.getUniqueId())) {
- this.playerList.remove(player.getUniqueId());
-
+ if (this.playerList.remove(player.getUniqueId()) != null) {
PlayerListPacket pk = new PlayerListPacket();
pk.type = PlayerListPacket.TYPE_REMOVE;
pk.entries = new PlayerListPacket.Entry[]{new PlayerListPacket.Entry(player.getUniqueId())};
@@ -1018,7 +1054,7 @@ public void updatePlayerListData(UUID uuid, long entityId, String name, Skin ski
PlayerListPacket pk = new PlayerListPacket();
pk.type = PlayerListPacket.TYPE_ADD;
pk.entries = new PlayerListPacket.Entry[]{new PlayerListPacket.Entry(uuid, entityId, name, skin, xboxUserId)};
- Server.broadcastPacket(players, pk);
+ this.batchPackets(players, new DataPacket[]{pk}); // This is sent "directly" so it always gets through before possible TYPE_REMOVE packet for NPCs etc.
}
public void updatePlayerListData(UUID uuid, long entityId, String name, Skin skin, String xboxUserId, Collection players) {
@@ -1033,7 +1069,13 @@ public void removePlayerListData(UUID uuid, Player[] players) {
PlayerListPacket pk = new PlayerListPacket();
pk.type = PlayerListPacket.TYPE_REMOVE;
pk.entries = new PlayerListPacket.Entry[]{new PlayerListPacket.Entry(uuid)};
- Server.broadcastPacket(players, pk);
+ for (Player player : players) {
+ player.dataPacket(pk);
+ }
+ }
+
+ public void removePlayerListData(UUID uuid, Collection players) {
+ this.removePlayerListData(uuid, players.toArray(new Player[0]));
}
public void removePlayerListData(UUID uuid, Player player) {
@@ -1043,22 +1085,17 @@ public void removePlayerListData(UUID uuid, Player player) {
player.dataPacket(pk);
}
- public void removePlayerListData(UUID uuid, Collection players) {
- this.removePlayerListData(uuid, players.toArray(new Player[0]));
- }
-
public void sendFullPlayerListData(Player player) {
PlayerListPacket pk = new PlayerListPacket();
pk.type = PlayerListPacket.TYPE_ADD;
pk.entries = this.playerList.values().stream()
.map(p -> new PlayerListPacket.Entry(
- p.getUniqueId(),
- p.getId(),
- p.getDisplayName(),
- p.getSkin(),
- p.getLoginChainData().getXUID()))
+ p.getUniqueId(),
+ p.getId(),
+ p.getDisplayName(),
+ p.getSkin(),
+ p.getLoginChainData().getXUID()))
.toArray(PlayerListPacket.Entry[]::new);
-
player.dataPacket(pk);
}
@@ -1066,23 +1103,20 @@ public void sendRecipeList(Player player) {
player.dataPacket(CraftingManager.packet);
}
- private void checkTickUpdates(int currentTick, long tickTime) {
- for (Player p : new ArrayList<>(this.players.values())) {
- /*if (!p.loggedIn && (tickTime - p.creationTime) >= 10000 && p.kick(PlayerKickEvent.Reason.LOGIN_TIMEOUT, "Login timeout")) {
- continue;
- }
-
- client freezes when applying resource packs
- todo: fix*/
-
- if (this.alwaysTickPlayers) {
+ private void checkTickUpdates(int currentTick) {
+ if (this.alwaysTickPlayers) {
+ for (Player p : new ArrayList<>(this.players.values())) {
p.onUpdate(currentTick);
}
}
- //Do level ticks
+ for (Player p : this.getOnlinePlayers().values()) {
+ p.resetPacketCounters();
+ }
+
+ // Do level ticks
for (Level level : this.levelArray) {
- if (level.getTickRate() > this.baseTickRate && --level.tickRateCounter > 0) {
+ if (level.isBeingConverted || (level.getTickRate() > this.baseTickRate && --level.tickRateCounter > 0)) {
continue;
}
@@ -1103,74 +1137,72 @@ private void checkTickUpdates(int currentTick, long tickTime) {
} else if (tickMs >= 50) {
if (level.getTickRate() == this.baseTickRate) {
level.setTickRate(Math.max(this.baseTickRate + 1, Math.min(this.autoTickRateLimit, tickMs / 50)));
- this.getLogger().debug("Level \"" + level.getName() + "\" took " + NukkitMath.round(tickMs, 2) + "ms, setting tick rate to " + level.getTickRate() + " ticks");
+ this.getLogger().debug("Level \"" + level.getName() + "\" took " + tickMs + "ms, setting tick rate to " + level.getTickRate() + " ticks");
} else if ((tickMs / level.getTickRate()) >= 50 && level.getTickRate() < this.autoTickRateLimit) {
level.setTickRate(level.getTickRate() + 1);
- this.getLogger().debug("Level \"" + level.getName() + "\" took " + NukkitMath.round(tickMs, 2) + "ms, setting tick rate to " + level.getTickRate() + " ticks");
+ this.getLogger().debug("Level \"" + level.getName() + "\" took " + tickMs + "ms, setting tick rate to " + level.getTickRate() + " ticks");
}
level.tickRateCounter = level.getTickRate();
}
}
} catch (Exception e) {
- log.error(this.getLanguage().translateString("nukkit.level.tickError",
- new String[]{level.getFolderName(), Utils.getExceptionMessage(e)}));
+ log.error(this.baseLang.translateString("nukkit.level.tickError", new String[]{level.getFolderName(), Utils.getExceptionMessage(e)}));
}
}
}
public void doAutoSave() {
- if (this.getAutoSave()) {
- Timings.levelSaveTimer.startTiming();
- for (Player player : new ArrayList<>(this.players.values())) {
+ if (this.autoSave) {
+ log.debug("Running auto save...");
+
+ for (Player player : this.players.values()) {
if (player.isOnline()) {
player.save(true);
- } else if (!player.isConnected()) {
- this.removePlayer(player);
}
}
for (Level level : this.levelArray) {
- level.save();
+ if (level.getAutoSave()) {
+ if (level.getProvider() != null) {
+ try {
+ level.save();
+ } catch (Exception ex) {
+ getLogger().error("Failed to auto save " + level.getName(), ex);
+ }
+ }
+ }
}
- Timings.levelSaveTimer.stopTiming();
}
}
- private boolean tick() {
+ private void tick() {
long tickTime = System.currentTimeMillis();
- // TODO
long time = tickTime - this.nextTick;
if (time < -25) {
try {
Thread.sleep(Math.max(5, -time - 25));
} catch (InterruptedException e) {
- Server.getInstance().getLogger().logException(e);
+ this.getLogger().logException(e);
}
}
long tickTimeNano = System.nanoTime();
if ((tickTime - this.nextTick) < -25) {
- return false;
+ return;
}
- Timings.fullServerTickTimer.startTiming();
-
++this.tickCounter;
- Timings.connectionTimer.startTiming();
this.network.processInterfaces();
if (this.rcon != null) {
this.rcon.check();
}
- Timings.connectionTimer.stopTiming();
- Timings.schedulerTimer.startTiming();
this.scheduler.mainThreadHeartbeat(this.tickCounter);
- Timings.schedulerTimer.stopTiming();
- this.checkTickUpdates(this.tickCounter, tickTime);
+ this.checkTickUpdates(this.tickCounter);
for (Player player : new ArrayList<>(this.players.values())) {
player.checkNetwork();
@@ -1178,13 +1210,14 @@ private boolean tick() {
if ((this.tickCounter & 0b1111) == 0) {
this.titleTick();
- this.network.resetStatistics();
+
+ //this.network.resetStatistics(); // Unnecessary since addStatistics is not used in the new raknet
this.maxTick = 20;
this.maxUse = 0;
if ((this.tickCounter & 0b111111111) == 0) {
try {
- this.getPluginManager().callEvent(this.queryRegenerateEvent = new QueryRegenerateEvent(this, 5));
+ this.pluginManager.callEvent(this.queryRegenerateEvent = new QueryRegenerateEvent(this, 5));
if (this.queryHandler != null) {
this.queryHandler.regenerateInfo();
}
@@ -1193,25 +1226,23 @@ private boolean tick() {
}
}
- this.getNetwork().updateName();
+ this.network.updateName();
}
- if (this.autoSave && ++this.autoSaveTicker >= this.autoSaveTicks) {
+ if (++this.autoSaveTicker >= this.autoSaveTicks) {
this.autoSaveTicker = 0;
this.doAutoSave();
}
if (this.tickCounter % 100 == 0) {
for (Level level : this.levelArray) {
- level.doChunkGarbageCollection();
+ if (!level.isBeingConverted) {
+ level.doChunkGarbageCollection();
+ }
}
}
- Timings.fullServerTickTimer.stopTiming();
- //long now = System.currentTimeMillis();
long nowNano = System.nanoTime();
- //float tick = Math.min(20, 1000 / Math.max(1, now - tickTime));
- //float use = Math.min(1, (now - tickTime) / 50);
float tick = (float) Math.min(20, 1000000000 / Math.max(1000000, ((double) nowNano - tickTimeNano)));
float use = (float) Math.min(1, ((double) (nowNano - tickTimeNano)) / 50000000);
@@ -1235,36 +1266,26 @@ private boolean tick() {
} else {
this.nextTick += 50;
}
-
- return true;
}
public long getNextTick() {
return nextTick;
}
- // TODO: Fix title tick
- public void titleTick() {
- if (!Nukkit.ANSI || !Nukkit.TITLE) {
+ private void titleTick() {
+ if (!Nukkit.TITLE) {
return;
}
-
Runtime runtime = Runtime.getRuntime();
double used = NukkitMath.round((double) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024, 2);
double max = NukkitMath.round(((double) runtime.maxMemory()) / 1024 / 1024, 2);
- String usage = Math.round(used / max * 100) + "%";
- String title = (char) 0x1b + "]0;" + this.getName() + " "
- + this.getNukkitVersion()
- + " | Online " + this.players.size() + "/" + this.getMaxPlayers()
- + " | Memory " + usage;
- if (!Nukkit.shortTitle) {
- title += " | U " + NukkitMath.round((this.network.getUpload() / 1024 * 1000), 2)
- + " D " + NukkitMath.round((this.network.getDownload() / 1024 * 1000), 2) + " kB/s";
- }
- title += " | TPS " + this.getTicksPerSecond()
- + " | Load " + this.getTickUsage() + "%" + (char) 0x07;
-
- System.out.print(title);
+ System.out.print((char) 0x1b + "]0;Nukkit " + Nukkit.VERSION +
+ " | Online " + this.playerList.size() + '/' + this.maxPlayers +
+ " | Memory " + Math.round(used / max * 100) + '%' +
+ /*" | U " + NukkitMath.round((this.network.getUpload() / 1024 * 1000), 2) +
+ " D " + NukkitMath.round((this.network.getDownload() / 1024 * 1000), 2) + " kB/s" +*/
+ " | TPS " + this.getTicksPerSecond() +
+ " | Load " + this.getTickUsage() + '%' + (char) 0x07);
}
public QueryRegenerateEvent getQueryInformation() {
@@ -1284,7 +1305,7 @@ public String getNukkitVersion() {
}
public String getCodename() {
- return Nukkit.CODENAME;
+ return "";
}
public String getVersion() {
@@ -1316,15 +1337,15 @@ public void setMaxPlayers(int maxPlayers) {
}
public int getPort() {
- return this.getPropertyInt("server-port", 19132);
+ return port;
}
public int getViewDistance() {
- return this.getPropertyInt("view-distance", 10);
+ return viewDistance;
}
public String getIp() {
- return this.getPropertyString("server-ip", "0.0.0.0");
+ return ip;
}
public UUID getServerUniqueId() {
@@ -1343,23 +1364,15 @@ public void setAutoSave(boolean autoSave) {
}
public String getLevelType() {
- return this.getPropertyString("level-type", "DEFAULT");
- }
-
- public boolean getGenerateStructures() {
- return this.getPropertyBoolean("generate-structures", true);
+ return this.getPropertyString("level-type", "default");
}
public int getGamemode() {
- try {
- return this.getPropertyInt("gamemode", 0) & 0b11;
- } catch (NumberFormatException exception) {
- return getGamemodeFromString(this.getPropertyString("gamemode")) & 0b11;
- }
+ return gamemode;
}
public boolean getForceGamemode() {
- return this.getPropertyBoolean("force-gamemode", false);
+ return this.forceGamemode;
}
public static String getGamemodeString(int mode) {
@@ -1386,23 +1399,22 @@ public static int getGamemodeFromString(String str) {
case "survival":
case "s":
return Player.SURVIVAL;
-
case "1":
case "creative":
case "c":
return Player.CREATIVE;
-
case "2":
case "adventure":
case "a":
return Player.ADVENTURE;
-
case "3":
case "spectator":
case "spc":
case "view":
case "v":
return Player.SPECTATOR;
+ case "default":
+ return Server.getInstance().getDefaultGamemode();
}
return -1;
}
@@ -1413,17 +1425,14 @@ public static int getDifficultyFromString(String str) {
case "peaceful":
case "p":
return 0;
-
case "1":
case "easy":
case "e":
return 1;
-
case "2":
case "normal":
case "n":
return 2;
-
case "3":
case "hard":
case "h":
@@ -1433,9 +1442,6 @@ public static int getDifficultyFromString(String str) {
}
public int getDifficulty() {
- if (this.difficulty == Integer.MAX_VALUE) {
- this.difficulty = getDifficultyFromString(this.getPropertyString("difficulty", "1"));
- }
return this.difficulty;
}
@@ -1448,45 +1454,45 @@ public void setDifficulty(int difficulty) {
}
public boolean hasWhitelist() {
- return this.getPropertyBoolean("white-list", false);
+ return this.whitelistEnabled;
}
public int getSpawnRadius() {
- return this.getPropertyInt("spawn-protection", 16);
+ return spawnRadius;
}
public boolean getAllowFlight() {
- if (getAllowFlight == null) {
- getAllowFlight = this.getPropertyBoolean("allow-flight", false);
- }
- return getAllowFlight;
+ return allowFlight;
}
public boolean isHardcore() {
- return this.getPropertyBoolean("hardcore", false);
+ return this.isHardcore;
}
public int getDefaultGamemode() {
- if (this.defaultGamemode == Integer.MAX_VALUE) {
- this.defaultGamemode = this.getGamemode();
- }
- return this.defaultGamemode;
+ return this.getGamemode();
}
+ /**
+ * Get MOTD
+ * @return motd
+ */
public String getMotd() {
- return this.getPropertyString("motd", "A Nukkit Powered Server");
+ return motd;
}
+ /**
+ * Get Sub-MOTD (level name)
+ * @return sub-motd
+ */
public String getSubMotd() {
- String subMotd = this.getPropertyString("sub-motd", "https://nukkitx.com");
- if (subMotd.isEmpty()) {
- subMotd = "https://nukkitx.com"; // The client doesn't allow empty sub-motd in 1.16.210
- }
- return subMotd;
+ String sub = this.getPropertyString("sub-motd", "Powered by Nukkit");
+ if (sub.isEmpty()) sub = "Powered by Nukkit";
+ return sub;
}
public boolean getForceResources() {
- return this.getPropertyBoolean("force-resources", false);
+ return this.forceResources;
}
public MainLogger getLogger() {
@@ -1521,14 +1527,29 @@ public ServerScheduler getScheduler() {
return scheduler;
}
+ /**
+ * Get current tick
+ *
+ * @return current tick
+ */
public int getTick() {
return tickCounter;
}
+ /**
+ * Get ticks per second
+ *
+ * @return TPS
+ */
public float getTicksPerSecond() {
return ((float) Math.round(this.maxTick * 100)) / 100;
}
+ /**
+ * Get average ticks per second
+ *
+ * @return average TPS
+ */
public float getTicksPerSecondAverage() {
float sum = 0;
int count = this.tickAverage.length;
@@ -1538,36 +1559,81 @@ public float getTicksPerSecondAverage() {
return (float) NukkitMath.round(sum / count, 2);
}
+ /**
+ * Get main thread load
+ *
+ * @return tick usage %
+ */
public float getTickUsage() {
return (float) NukkitMath.round(this.maxUse * 100, 2);
}
+ /**
+ * Get average main thread load
+ *
+ * @return average main thread load
+ */
public float getTickUsageAverage() {
float sum = 0;
- int count = this.useAverage.length;
for (float aUseAverage : this.useAverage) {
sum += aUseAverage;
}
- return ((float) Math.round(sum / count * 100)) / 100;
+ return ((float) Math.round(sum / this.useAverage.length * 100)) / 100;
}
+ /**
+ * Get command map
+ *
+ * @return command map
+ */
public SimpleCommandMap getCommandMap() {
return commandMap;
}
+ /**
+ * Get all online players
+ *
+ * @return online players
+ */
public Map getOnlinePlayers() {
return ImmutableMap.copyOf(playerList);
}
+ /**
+ * Get online player count
+ *
+ * @return online player count
+ */
+ public int getOnlinePlayersCount() {
+ return this.playerList.size();
+ }
+
+ /**
+ * Register a recipe to CraftingManager.
+ * Please use getCraftingManager().registerRecipe(protocol, recipe) instead
+ * @param recipe recipe
+ */
public void addRecipe(Recipe recipe) {
this.craftingManager.registerRecipe(recipe);
}
+ /**
+ * Get an online player by uuid
+ *
+ * @param uuid uuid
+ * @return Optional Player
+ */
public Optional getPlayer(UUID uuid) {
Preconditions.checkNotNull(uuid, "uuid");
return Optional.ofNullable(playerList.get(uuid));
}
+ /**
+ * Get known player uuid by player name
+ *
+ * @param name player name
+ * @return Optional UUID
+ */
public Optional lookupName(String name) {
byte[] nameBytes = name.toLowerCase().getBytes(StandardCharsets.UTF_8);
byte[] uuidBytes = nameLookup.get(nameBytes);
@@ -1576,7 +1642,7 @@ public Optional lookupName(String name) {
}
if (uuidBytes.length != 16) {
- log.warn("Invalid uuid in name lookup database detected! Removing");
+ log.warn("Invalid uuid in name lookup database detected! Removing...");
nameLookup.delete(nameBytes);
return Optional.empty();
}
@@ -1597,12 +1663,12 @@ void updateName(UUID uuid, String name) {
@Deprecated
public IPlayer getOfflinePlayer(final String name) {
- IPlayer result = this.getPlayerExact(name.toLowerCase());
+ IPlayer result = this.getPlayerExact(name);
if (result != null) {
return result;
}
- return lookupName(name).map(uuid -> new OfflinePlayer(this, uuid))
+ return lookupName(name).map(uuid -> new OfflinePlayer(this, uuid, name))
.orElse(new OfflinePlayer(this, name));
}
@@ -1645,7 +1711,7 @@ private CompoundTag getOfflinePlayerDataInternal(String name, boolean runEvent,
Optional dataStream = Optional.empty();
try {
- dataStream = event.getSerializer().read(name, event.getUuid().orElse(null));
+ dataStream = event.getSerializer().read(name, event.getUuid().orElse(null)); // TODO: should the name be lower case?
if (dataStream.isPresent()) {
return NBTIO.readCompressed(dataStream.get());
}
@@ -1667,9 +1733,10 @@ private CompoundTag getOfflinePlayerDataInternal(String name, boolean runEvent,
log.info(this.getLanguage().translateString("nukkit.data.playerNotFound", name));
}
Position spawn = this.getDefaultLevel().getSafeSpawn();
+ long time = System.currentTimeMillis();
nbt = new CompoundTag()
- .putLong("firstPlayed", System.currentTimeMillis() / 1000)
- .putLong("lastPlayed", System.currentTimeMillis() / 1000)
+ .putLong("firstPlayed", time / 1000)
+ .putLong("lastPlayed", time / 1000)
.putList(new ListTag("Pos")
.add(new DoubleTag("0", spawn.x))
.add(new DoubleTag("1", spawn.y))
@@ -1687,7 +1754,7 @@ private CompoundTag getOfflinePlayerDataInternal(String name, boolean runEvent,
.add(new FloatTag("1", 0)))
.putFloat("FallDistance", 0)
.putShort("Fire", 0)
- .putShort("Air", 300)
+ .putShort("Air", 400)
.putBoolean("OnGround", true)
.putBoolean("Invulnerable", false);
@@ -1714,33 +1781,45 @@ public void saveOfflinePlayerData(String name, CompoundTag tag, boolean async) {
}
private void saveOfflinePlayerData(String name, CompoundTag tag, boolean async, boolean runEvent) {
- String nameLower = name.toLowerCase();
if (this.shouldSavePlayerData()) {
+ String nameLower = name.toLowerCase();
PlayerDataSerializeEvent event = new PlayerDataSerializeEvent(nameLower, playerDataSerializer);
if (runEvent) {
pluginManager.callEvent(event);
}
- this.getScheduler().scheduleTask(new Task() {
- boolean hasRun = false;
+ if (async) {
+ this.getScheduler().scheduleTask(new Task() {
+ private volatile boolean hasRun = false;
- @Override
- public void onRun(int currentTick) {
- this.onCancel();
- }
+ @Override
+ public void onRun(int currentTick) {
+ this.onCancel();
+ }
- //doing it like this ensures that the playerdata will be saved in a server shutdown
- @Override
- public void onCancel() {
- if (!this.hasRun) {
- this.hasRun = true;
- saveOfflinePlayerDataInternal(event.getSerializer(), tag, nameLower, event.getUuid().orElse(null));
+ // Doing it like this ensures that the player data will be saved in a server shutdown
+ @Override
+ public void onCancel() {
+ if (!this.hasRun) {
+ this.hasRun = true;
+ saveOfflinePlayerDataInternal(event.getSerializer(), tag, nameLower, event.getUuid().orElse(null));
+ }
}
- }
- }, async);
+ }, true);
+ } else {
+ saveOfflinePlayerDataInternal(event.getSerializer(), tag, nameLower, event.getUuid().orElse(null));
+ }
}
}
+ /**
+ * Internal: Save offline player data
+ *
+ * @param serializer serializer
+ * @param tag compound tag
+ * @param name player name
+ * @param uuid player uuid
+ */
private void saveOfflinePlayerDataInternal(PlayerDataSerializer serializer, CompoundTag tag, String name, UUID uuid) {
try (OutputStream dataStream = serializer.write(name, uuid)) {
NBTIO.writeGZIPCompressed(tag, dataStream, ByteOrder.BIG_ENDIAN);
@@ -1749,9 +1828,11 @@ private void saveOfflinePlayerDataInternal(PlayerDataSerializer serializer, Comp
}
}
+ /**
+ * Internal: Convert legacy player saves to the uuid based saving
+ */
private void convertLegacyPlayerData() {
File dataDirectory = new File(getDataPath(), "players/");
- Pattern uuidPattern = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}.dat$");
File[] files = dataDirectory.listFiles(file -> {
String name = file.getName();
@@ -1798,6 +1879,12 @@ private void convertLegacyPlayerData() {
}
}
+ /**
+ * Get an online player by name
+ *
+ * @param name player name
+ * @return Player or null
+ */
public Player getPlayer(String name) {
Player found = null;
name = name.toLowerCase();
@@ -1818,10 +1905,15 @@ public Player getPlayer(String name) {
return found;
}
+ /**
+ * Get an online player by exact player name
+ *
+ * @param name exact player name
+ * @return Player or null
+ */
public Player getPlayerExact(String name) {
- name = name.toLowerCase();
for (Player player : this.getOnlinePlayers().values()) {
- if (player.getName().toLowerCase().equals(name)) {
+ if (player.getName().equalsIgnoreCase(name)) {
return player;
}
}
@@ -1829,6 +1921,12 @@ public Player getPlayerExact(String name) {
return null;
}
+ /**
+ * Get players that match with the name
+ *
+ * @param partialName name
+ * @return matching players
+ */
public Player[] matchPlayer(String partialName) {
partialName = partialName.toLowerCase();
List matchedPlayer = new ArrayList<>();
@@ -1843,46 +1941,79 @@ public Player[] matchPlayer(String partialName) {
return matchedPlayer.toArray(new Player[0]);
}
+ /**
+ * Internal: Remove a player from the server
+ *
+ * @param player player
+ */
public void removePlayer(Player player) {
- Player toRemove = this.players.remove(player.getSocketAddress());
- if (toRemove != null) {
+ if (this.players.remove(player.getSocketAddress()) != null) {
return;
}
for (InetSocketAddress socketAddress : new ArrayList<>(this.players.keySet())) {
- Player p = this.players.get(socketAddress);
- if (player == p) {
+ if (player == this.players.get(socketAddress)) {
this.players.remove(socketAddress);
break;
}
}
}
+ /**
+ * Get all levels
+ *
+ * @return levels
+ */
public Map getLevels() {
return levels;
}
+ /**
+ * Get default level
+ *
+ * @return default level
+ */
public Level getDefaultLevel() {
return defaultLevel;
}
+ /**
+ * Change the default level
+ *
+ * @param defaultLevel new default level
+ */
public void setDefaultLevel(Level defaultLevel) {
if (defaultLevel == null || (this.isLevelLoaded(defaultLevel.getFolderName()) && defaultLevel != this.defaultLevel)) {
this.defaultLevel = defaultLevel;
}
}
+ /**
+ * Check whether a level is loaded
+ *
+ * @param name level name
+ * @return is loaded
+ */
public boolean isLevelLoaded(String name) {
return this.getLevelByName(name) != null;
}
+ /**
+ * Get a level by ID
+ *
+ * @param levelId level ID
+ * @return Level or null
+ */
public Level getLevel(int levelId) {
- if (this.levels.containsKey(levelId)) {
- return this.levels.get(levelId);
- }
- return null;
+ return this.levels.get(levelId);
}
+ /**
+ * Get a level by name
+ *
+ * @param name level name
+ * @return Level or null
+ */
public Level getLevelByName(String name) {
for (Level level : this.levelArray) {
if (level.getFolderName().equalsIgnoreCase(name)) {
@@ -1893,28 +2024,53 @@ public Level getLevelByName(String name) {
return null;
}
+ /**
+ * Unload a level.
+ * Notice that the default level cannot be unloaded without forceUnload=true
+ *
+ * @param level Level
+ * @return unloaded
+ */
public boolean unloadLevel(Level level) {
return this.unloadLevel(level, false);
}
+ /**
+ * Unload a level
+ *
+ * Notice: the default level cannot be unloaded without forceUnload=true
+ *
+ * @param level Level
+ * @param forceUnload force unload (ignore cancelled events and default level)
+ * @return unloaded
+ */
public boolean unloadLevel(Level level, boolean forceUnload) {
- if (level == this.getDefaultLevel() && !forceUnload) {
+ if (level == this.defaultLevel && !forceUnload) {
throw new IllegalStateException("The default level cannot be unloaded while running, please switch levels.");
}
return level.unload(forceUnload);
-
}
+ /**
+ * Load a level by name
+ *
+ * @param name level name
+ * @return loaded
+ */
public boolean loadLevel(String name) {
if (Objects.equals(name.trim(), "")) {
throw new LevelException("Invalid empty level name");
}
+
+ if (!this.isPrimaryThread()) {
+ getLogger().warning("Level loaded asynchronously: " + name);
+ }
+
if (this.isLevelLoaded(name)) {
return true;
} else if (!this.isLevelGenerated(name)) {
- log.warn(this.getLanguage().translateString("nukkit.level.notFound", name));
-
+ log.warn(this.baseLang.translateString("nukkit.level.notFound", name));
return false;
}
@@ -1923,14 +2079,13 @@ public boolean loadLevel(String name) {
if (name.contains("/") || name.contains("\\")) {
path = name;
} else {
- path = this.getDataPath() + "worlds/" + name + "/";
+ path = this.dataPath + "worlds/" + name + '/';
}
Class extends LevelProvider> provider = LevelProviderManager.getProvider(path);
if (provider == null) {
- log.error(this.getLanguage().translateString("nukkit.level.loadError", new String[]{name, "Unknown provider"}));
-
+ log.error(this.baseLang.translateString("nukkit.level.loadError", new String[]{name, "Unknown provider"}));
return false;
}
@@ -1938,37 +2093,76 @@ public boolean loadLevel(String name) {
try {
level = new Level(this, name, path, provider);
} catch (Exception e) {
- log.error(this.getLanguage().translateString("nukkit.level.loadError", new String[]{name, e.getMessage()}));
+ log.error(this.baseLang.translateString("nukkit.level.loadError", new String[]{name, e.getMessage()}));
return false;
}
- this.levels.put(level.getId(), level);
-
level.initLevel();
- this.getPluginManager().callEvent(new LevelLoadEvent(level));
+ this.levels.put(level.getId(), level);
level.setTickRate(this.baseTickRate);
+ this.pluginManager.callEvent(new LevelLoadEvent(level));
return true;
}
+ /**
+ * Generate a new level
+ *
+ * @param name level name
+ * @return generated
+ */
public boolean generateLevel(String name) {
- return this.generateLevel(name, new java.util.Random().nextLong());
+ return this.generateLevel(name, Utils.random.nextLong());
}
+ /**
+ * Generate a new level
+ *
+ * @param name level name
+ * @param seed level seed
+ * @return generated
+ */
public boolean generateLevel(String name, long seed) {
return this.generateLevel(name, seed, null);
}
+ /**
+ * Generate a new level
+ *
+ * @param name level name
+ * @param seed level seed
+ * @param generator level generator
+ * @return generated
+ */
public boolean generateLevel(String name, long seed, Class extends Generator> generator) {
return this.generateLevel(name, seed, generator, new HashMap<>());
}
+ /**
+ * Generate a new level
+ *
+ * @param name level name
+ * @param seed level seed
+ * @param generator level generator
+ * @param options level generator options
+ * @return generated
+ */
public boolean generateLevel(String name, long seed, Class extends Generator> generator, Map options) {
return generateLevel(name, seed, generator, options, null);
}
+ /**
+ * Generate a new level
+ *
+ * @param name level name
+ * @param seed level seed
+ * @param generator level generator
+ * @param options level generator options
+ * @param provider level provider
+ * @return generated
+ */
public boolean generateLevel(String name, long seed, Class extends Generator> generator, Map options, Class extends LevelProvider> provider) {
if (Objects.equals(name.trim(), "") || this.isLevelGenerated(name)) {
return false;
@@ -1983,7 +2177,7 @@ public boolean generateLevel(String name, long seed, Class extends Generator>
}
if (provider == null) {
- provider = LevelProviderManager.getProviderByName(this.getConfig().get("level-settings.default-format", "anvil"));
+ provider = LevelProviderManager.getProviderByName("leveldb");
}
String path;
@@ -1991,7 +2185,7 @@ public boolean generateLevel(String name, long seed, Class extends Generator>
if (name.contains("/") || name.contains("\\")) {
path = name;
} else {
- path = this.getDataPath() + "worlds/" + name + "/";
+ path = this.dataPath + "worlds/" + name + '/';
}
Level level;
@@ -1999,51 +2193,28 @@ public boolean generateLevel(String name, long seed, Class extends Generator>
provider.getMethod("generate", String.class, String.class, long.class, Class.class, Map.class).invoke(null, path, name, seed, generator, options);
level = new Level(this, name, path, provider);
- this.levels.put(level.getId(), level);
level.initLevel();
+
+ this.levels.put(level.getId(), level);
+
level.setTickRate(this.baseTickRate);
} catch (Exception e) {
- log.error(this.getLanguage().translateString("nukkit.level.generationError", new String[]{name, Utils.getExceptionMessage(e)}));
+ log.error(this.baseLang.translateString("nukkit.level.generationError", new String[]{name, Utils.getExceptionMessage(e)}));
return false;
}
- this.getPluginManager().callEvent(new LevelInitEvent(level));
-
- this.getPluginManager().callEvent(new LevelLoadEvent(level));
-
- /*this.getLogger().notice(this.getLanguage().translateString("nukkit.level.backgroundGeneration", name));
-
- int centerX = (int) level.getSpawnLocation().getX() >> 4;
- int centerZ = (int) level.getSpawnLocation().getZ() >> 4;
-
- TreeMap order = new TreeMap<>();
-
- for (int X = -3; X <= 3; ++X) {
- for (int Z = -3; Z <= 3; ++Z) {
- int distance = X * X + Z * Z;
- int chunkX = X + centerX;
- int chunkZ = Z + centerZ;
- order.put(Level.chunkHash(chunkX, chunkZ), distance);
- }
- }
-
- List> sortList = new ArrayList<>(order.entrySet());
-
- Collections.sort(sortList, new Comparator>() {
- @Override
- public int compare(Map.Entry o1, Map.Entry o2) {
- return o2.getValue() - o1.getValue();
- }
- });
-
- for (String index : order.keySet()) {
- Chunk.Entry entry = Level.getChunkXZ(index);
- level.populateChunk(entry.chunkX, entry.chunkZ, true);
- }*/
+ this.pluginManager.callEvent(new LevelInitEvent(level));
+ this.pluginManager.callEvent(new LevelLoadEvent(level));
return true;
}
+ /**
+ * Check whether a level by name is generated
+ *
+ * @param name level name
+ * @return level found
+ */
public boolean isLevelGenerated(String name) {
if (Objects.equals(name.trim(), "")) {
return false;
@@ -2055,7 +2226,7 @@ public boolean isLevelGenerated(String name) {
if (name.contains("/") || name.contains("\\")) {
path = name;
} else {
- path = this.getDataPath() + "worlds/" + name + "/";
+ path = this.dataPath + "worlds/" + name + '/';
}
return LevelProviderManager.getProvider(path) != null;
@@ -2064,19 +2235,38 @@ public boolean isLevelGenerated(String name) {
return true;
}
+ /**
+ * Get BaseLang (server's default language)
+ *
+ * @return BaseLang
+ */
public BaseLang getLanguage() {
return baseLang;
}
+ /**
+ * Is forcing language enabled
+ *
+ * @return force-language enabled
+ */
public boolean isLanguageForced() {
return forceLanguage;
}
+ /**
+ * Get Network
+ *
+ * @return Network
+ */
public Network getNetwork() {
return network;
}
- //Revising later...
+ /**
+ * Get nukkit.yml
+ *
+ * @return config
+ */
public Config getConfig() {
return this.config;
}
@@ -2091,48 +2281,117 @@ public T getConfig(String variable, T defaultValue) {
return value == null ? defaultValue : (T) value;
}
+ /**
+ * Get server.properties
+ *
+ * @return server.properties as a Config
+ */
public Config getProperties() {
return this.properties;
}
+ /**
+ * Get a value from server.properties
+ *
+ * @param variable key
+ * @return value
+ */
public Object getProperty(String variable) {
return this.getProperty(variable, null);
}
+ /**
+ * Get a value from server.properties
+ *
+ * @param variable key
+ * @param defaultValue default value
+ * @return value
+ */
public Object getProperty(String variable, Object defaultValue) {
return this.properties.exists(variable) ? this.properties.get(variable) : defaultValue;
}
+ /**
+ * Set a string value in server.properties
+ *
+ * @param variable key
+ * @param value value
+ */
public void setPropertyString(String variable, String value) {
this.properties.set(variable, value);
this.properties.save();
}
- public String getPropertyString(String variable) {
- return this.getPropertyString(variable, null);
+ /**
+ * Get a string value from server.properties
+ *
+ * @param key key
+ * @return value
+ */
+ public String getPropertyString(String key) {
+ return this.getPropertyString(key, null);
}
+ /**
+ * Get a string value from server.properties
+ *
+ * @param key key
+ * @param defaultValue default value
+ * @return value
+ */
public String getPropertyString(String key, String defaultValue) {
return this.properties.exists(key) ? this.properties.get(key).toString() : defaultValue;
}
+ /**
+ * Get an int value from server.properties
+ *
+ * @param variable key
+ * @return value
+ */
public int getPropertyInt(String variable) {
return this.getPropertyInt(variable, null);
}
+ /**
+ * Get an int value from server.properties
+ *
+ * @param variable key
+ * @param defaultValue default value
+ * @return value
+ */
public int getPropertyInt(String variable, Integer defaultValue) {
return this.properties.exists(variable) ? (!this.properties.get(variable).equals("") ? Integer.parseInt(String.valueOf(this.properties.get(variable))) : defaultValue) : defaultValue;
}
+ /**
+ * Set an int value in server.properties
+ *
+ * @param variable key
+ * @param value value
+ */
public void setPropertyInt(String variable, int value) {
this.properties.set(variable, value);
this.properties.save();
}
+ /**
+ * Get a boolean value from server.properties
+ *
+ * @param variable key
+ * @return value
+ */
public boolean getPropertyBoolean(String variable) {
return this.getPropertyBoolean(variable, null);
}
+ /**
+ * Get a boolean value from server.properties
+ *
+ * @param variable key
+ * @param defaultValue default value
+ * @return value
+ */
public boolean getPropertyBoolean(String variable, Object defaultValue) {
Object value = this.properties.exists(variable) ? this.properties.get(variable) : defaultValue;
if (value instanceof Boolean) {
@@ -2148,11 +2407,23 @@ public boolean getPropertyBoolean(String variable, Object defaultValue) {
return false;
}
+ /**
+ * Set a boolean value in server.properties
+ *
+ * @param variable key
+ * @param value value
+ */
public void setPropertyBoolean(String variable, boolean value) {
this.properties.set(variable, value ? "1" : "0");
this.properties.save();
}
+ /**
+ * Get plugin commands
+ *
+ * @param name command name
+ * @return PluginIdentifiableCommand or null
+ */
public PluginIdentifiableCommand getPluginCommand(String name) {
Command command = this.commandMap.getCommand(name);
if (command instanceof PluginIdentifiableCommand) {
@@ -2162,14 +2433,29 @@ public PluginIdentifiableCommand getPluginCommand(String name) {
}
}
+ /**
+ * Get list of banned players
+ *
+ * @return ban list
+ */
public BanList getNameBans() {
return this.banByName;
}
+ /**
+ * Get list of IP bans
+ *
+ * @return IP bans
+ */
public BanList getIPBans() {
return this.banByIP;
}
+ /**
+ * Give player the operator status
+ *
+ * @param name player name
+ */
public void addOp(String name) {
this.operators.set(name.toLowerCase(), true);
Player player = this.getPlayerExact(name);
@@ -2179,6 +2465,11 @@ public void addOp(String name) {
this.operators.save(true);
}
+ /**
+ * Remove player's operator status
+ *
+ * @param name player name
+ */
public void removeOp(String name) {
this.operators.remove(name.toLowerCase());
Player player = this.getPlayerExact(name);
@@ -2188,43 +2479,78 @@ public void removeOp(String name) {
this.operators.save();
}
+ /**
+ * Add a player to whitelist
+ *
+ * @param name player name
+ */
public void addWhitelist(String name) {
this.whitelist.set(name.toLowerCase(), true);
this.whitelist.save(true);
}
+ /**
+ * Remove a player from whitelist
+ *
+ * @param name player name
+ */
public void removeWhitelist(String name) {
this.whitelist.remove(name.toLowerCase());
this.whitelist.save(true);
}
+ /**
+ * Check whether a player is whitelisted
+ *
+ * @param name player name
+ * @return is whitelisted or whitelist is not enabled
+ */
public boolean isWhitelisted(String name) {
return !this.hasWhitelist() || this.operators.exists(name, true) || this.whitelist.exists(name, true);
}
+ /**
+ * Check whether a player is an operator
+ *
+ * @param name player name
+ * @return is operator
+ */
public boolean isOp(String name) {
- return name != null && this.operators.exists(name, true);
+ return this.operators.exists(name, true);
}
+ /**
+ * Get whitelist config
+ *
+ * @return whitelist
+ */
public Config getWhitelist() {
return whitelist;
}
+ /**
+ * Get operator list config
+ *
+ * @return operators
+ */
public Config getOps() {
return operators;
}
+ /**
+ * Reload whitelist
+ */
public void reloadWhitelist() {
this.whitelist.reload();
}
- public ServiceManager getServiceManager() {
- return serviceManager;
- }
-
+ /**
+ * Load command aliases from nukkit.yml
+ */
public Map> getCommandAliases() {
Object section = this.getConfig("aliases");
Map> result = new LinkedHashMap<>();
+
if (section instanceof Map) {
for (Map.Entry entry : (Set) ((Map) section).entrySet()) {
List commands = new ArrayList<>();
@@ -2241,51 +2567,84 @@ public Map> getCommandAliases() {
}
return result;
+ }
+ /**
+ * Get service manager
+ *
+ * @return service manager
+ */
+ public ServiceManager getServiceManager() {
+ return serviceManager;
}
+ /**
+ * Should player data saving be enabled
+ *
+ * @return player data saving enabled
+ */
public boolean shouldSavePlayerData() {
- return this.getConfig("player.save-player-data", true);
+ return shouldSavePlayerData;
}
+ /**
+ * How often player is allowed to change skin in game (in seconds)
+ *
+ * @return skin change cooldown
+ */
public int getPlayerSkinChangeCooldown() {
- return this.getConfig("player.skin-change-cooldown", 30);
+ return skinChangeCooldown;
}
/**
- * 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.
+ * 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
+ * @return true if the current thread matches the expected primary thread, false otherwise
*/
- public final boolean isPrimaryThread() {
- return (Thread.currentThread() == currentThread);
+ public boolean isPrimaryThread() {
+ return Thread.currentThread() == currentThread;
}
+ /**
+ * Get server's primary thread
+ *
+ * @return primary thread
+ */
public Thread getPrimaryThread() {
return currentThread;
}
- private void registerEntities() {
- Entity.registerEntity("Lightning", EntityLightning.class);
- Entity.registerEntity("Arrow", EntityArrow.class);
- Entity.registerEntity("EnderPearl", EntityEnderPearl.class);
- Entity.registerEntity("FallingSand", EntityFallingBlock.class);
- Entity.registerEntity("Firework", EntityFirework.class);
+ /**
+ * Internal method to register all default entities
+ */
+ private static void registerEntities() {
+ //Items
Entity.registerEntity("Item", EntityItem.class);
Entity.registerEntity("Painting", EntityPainting.class);
+ Entity.registerEntity("XpOrb", EntityXPOrb.class);
+ Entity.registerEntity("ArmorStand", EntityArmorStand.class);
+ Entity.registerEntity("EndCrystal", EntityEndCrystal.class);
+ Entity.registerEntity("FallingSand", EntityFallingBlock.class);
Entity.registerEntity("PrimedTnt", EntityPrimedTNT.class);
+ Entity.registerEntity("Firework", EntityFirework.class);
+ //Projectiles
+ Entity.registerEntity("Arrow", EntityArrow.class);
Entity.registerEntity("Snowball", EntitySnowball.class);
+ Entity.registerEntity("EnderPearl", EntityEnderPearl.class);
+ Entity.registerEntity("ThrownExpBottle", EntityExpBottle.class);
+ Entity.registerEntity("ThrownPotion", EntityPotion.class);
+ Entity.registerEntity("Egg", EntityEgg.class);
+ Entity.registerEntity("ThrownLingeringPotion", EntityPotionLingering.class);
+ Entity.registerEntity("ThrownTrident", EntityThrownTrident.class);
+ Entity.registerEntity("FishingHook", EntityFishingHook.class);
+ Entity.registerEntity("EnderEye", EntityEnderEye.class);
+ Entity.registerEntity("AreaEffectCloud", EntityAreaEffectCloud.class);
//Monsters
Entity.registerEntity("Blaze", EntityBlaze.class);
- Entity.registerEntity("CaveSpider", EntityCaveSpider.class);
Entity.registerEntity("Creeper", EntityCreeper.class);
+ Entity.registerEntity("CaveSpider", EntityCaveSpider.class);
Entity.registerEntity("Drowned", EntityDrowned.class);
Entity.registerEntity("ElderGuardian", EntityElderGuardian.class);
Entity.registerEntity("EnderDragon", EntityEnderDragon.class);
@@ -2293,92 +2652,90 @@ private void registerEntities() {
Entity.registerEntity("Endermite", EntityEndermite.class);
Entity.registerEntity("Evoker", EntityEvoker.class);
Entity.registerEntity("Ghast", EntityGhast.class);
- Entity.registerEntity("GlowSquid", EntityGlowSquid.class);
Entity.registerEntity("Guardian", EntityGuardian.class);
- Entity.registerEntity("Hoglin", EntityHoglin.class);
Entity.registerEntity("Husk", EntityHusk.class);
Entity.registerEntity("MagmaCube", EntityMagmaCube.class);
Entity.registerEntity("Phantom", EntityPhantom.class);
- Entity.registerEntity("Piglin", EntityPiglin.class);
- Entity.registerEntity("PiglinBrute", EntityPiglinBrute.class);
- Entity.registerEntity("Pillager", EntityPillager.class);
Entity.registerEntity("Ravager", EntityRavager.class);
Entity.registerEntity("Shulker", EntityShulker.class);
Entity.registerEntity("Silverfish", EntitySilverfish.class);
Entity.registerEntity("Skeleton", EntitySkeleton.class);
+ Entity.registerEntity("SkeletonHorse", EntitySkeletonHorse.class);
Entity.registerEntity("Slime", EntitySlime.class);
- Entity.registerEntity("SnowGolem", EntitySnowGolem.class);
Entity.registerEntity("Spider", EntitySpider.class);
Entity.registerEntity("Stray", EntityStray.class);
- Entity.registerEntity("Vex", EntityVex.class);
Entity.registerEntity("Vindicator", EntityVindicator.class);
- Entity.registerEntity("Warden", EntityWarden.class);
- Entity.registerEntity("Witch", EntityWitch.class);
- Entity.registerEntity("Wither", EntityWither.class);
+ Entity.registerEntity("Vex", EntityVex.class);
Entity.registerEntity("WitherSkeleton", EntityWitherSkeleton.class);
+ Entity.registerEntity("Wither", EntityWither.class);
+ Entity.registerEntity("Witch", EntityWitch.class);
+ Entity.registerEntity("ZombiePigman", EntityZombiePigman.class);
+ Entity.registerEntity("ZombieVillager", EntityZombieVillagerV1.class);
Entity.registerEntity("Zombie", EntityZombie.class);
+ Entity.registerEntity("Pillager", EntityPillager.class);
+ Entity.registerEntity("ZombieVillagerV2", EntityZombieVillager.class);
+ Entity.registerEntity("Hoglin", EntityHoglin.class);
+ Entity.registerEntity("Piglin", EntityPiglin.class);
Entity.registerEntity("Zoglin", EntityZoglin.class);
- Entity.registerEntity("ZombiePigman", EntityZombiePigman.class);
- Entity.registerEntity("ZombieVillager", EntityZombieVillager.class);
- Entity.registerEntity("ZombieVillagerV1", EntityZombieVillagerV1.class);
+ Entity.registerEntity("PiglinBrute", EntityPiglinBrute.class);
+ Entity.registerEntity("Warden", EntityWarden.class);
//Passive
- Entity.registerEntity("Allay", EntityAllay.class);
- Entity.registerEntity("Axolotl", EntityAxolotl.class);
Entity.registerEntity("Bat", EntityBat.class);
- Entity.registerEntity("Bee", EntityBee.class);
Entity.registerEntity("Cat", EntityCat.class);
Entity.registerEntity("Chicken", EntityChicken.class);
Entity.registerEntity("Cod", EntityCod.class);
Entity.registerEntity("Cow", EntityCow.class);
Entity.registerEntity("Dolphin", EntityDolphin.class);
Entity.registerEntity("Donkey", EntityDonkey.class);
- Entity.registerEntity("Fox", EntityFox.class);
- Entity.registerEntity("Frog", EntityFrog.class);
- Entity.registerEntity("Goat", EntityGoat.class);
Entity.registerEntity("Horse", EntityHorse.class);
+ Entity.registerEntity("IronGolem", EntityIronGolem.class);
Entity.registerEntity("Llama", EntityLlama.class);
Entity.registerEntity("Mooshroom", EntityMooshroom.class);
Entity.registerEntity("Mule", EntityMule.class);
- Entity.registerEntity("Ocelot", EntityOcelot.class);
Entity.registerEntity("Panda", EntityPanda.class);
Entity.registerEntity("Parrot", EntityParrot.class);
- Entity.registerEntity("Pig", EntityPig.class);
Entity.registerEntity("PolarBear", EntityPolarBear.class);
+ Entity.registerEntity("Pig", EntityPig.class);
Entity.registerEntity("Pufferfish", EntityPufferfish.class);
Entity.registerEntity("Rabbit", EntityRabbit.class);
Entity.registerEntity("Salmon", EntitySalmon.class);
Entity.registerEntity("Sheep", EntitySheep.class);
- Entity.registerEntity("SkeletonHorse", EntitySkeletonHorse.class);
Entity.registerEntity("Squid", EntitySquid.class);
- Entity.registerEntity("Strider", EntityStrider.class);
- Entity.registerEntity("Tadpole", EntityTadpole.class);
+ Entity.registerEntity("SnowGolem", EntitySnowGolem.class);
Entity.registerEntity("TropicalFish", EntityTropicalFish.class);
Entity.registerEntity("Turtle", EntityTurtle.class);
- Entity.registerEntity("Villager", EntityVillager.class);
- Entity.registerEntity("VillagerV1", EntityVillagerV1.class);
- Entity.registerEntity("WanderingTrader", EntityWanderingTrader.class);
Entity.registerEntity("Wolf", EntityWolf.class);
+ Entity.registerEntity("Ocelot", EntityOcelot.class);
+ Entity.registerEntity("Villager", EntityVillagerV1.class);
Entity.registerEntity("ZombieHorse", EntityZombieHorse.class);
- //Projectile
- Entity.registerEntity("Egg", EntityEgg.class);
- Entity.registerEntity("ThrownExpBottle", EntityExpBottle.class);
- Entity.registerEntity("ThrownPotion", EntityPotion.class);
- Entity.registerEntity("ThrownTrident", EntityThrownTrident.class);
- Entity.registerEntity("XpOrb", EntityXPOrb.class);
-
- Entity.registerEntity("Human", EntityHuman.class, true);
- //Vehicle
- Entity.registerEntity("Boat", EntityBoat.class);
+ Entity.registerEntity("WanderingTrader", EntityWanderingTrader.class);
+ Entity.registerEntity("VillagerV2", EntityVillager.class);
+ Entity.registerEntity("Fox", EntityFox.class);
+ Entity.registerEntity("Bee", EntityBee.class);
+ Entity.registerEntity("Strider", EntityStrider.class);
+ Entity.registerEntity("Goat", EntityGoat.class);
+ Entity.registerEntity("Axolotl", EntityAxolotl.class);
+ Entity.registerEntity("GlowSquid", EntityGlowSquid.class);
+ Entity.registerEntity("Allay", EntityAllay.class);
+ Entity.registerEntity("Frog", EntityFrog.class);
+ Entity.registerEntity("Tadpole", EntityTadpole.class);
+ Entity.registerEntity("Camel", EntityCamel.class);
+ //Vehicles
+ Entity.registerEntity("MinecartRideable", EntityMinecartEmpty.class);
Entity.registerEntity("MinecartChest", EntityMinecartChest.class);
Entity.registerEntity("MinecartHopper", EntityMinecartHopper.class);
- Entity.registerEntity("MinecartRideable", EntityMinecartEmpty.class);
Entity.registerEntity("MinecartTnt", EntityMinecartTNT.class);
-
- Entity.registerEntity("EndCrystal", EntityEndCrystal.class);
- Entity.registerEntity("FishingHook", EntityFishingHook.class);
+ Entity.registerEntity("Boat", EntityBoat.class);
+ Entity.registerEntity("ChestBoat", EntityChestBoat.class);
+ //Others
+ Entity.registerEntity("Human", EntityHuman.class, true);
+ Entity.registerEntity("Lightning", EntityLightning.class);
}
- private void registerBlockEntities() {
+ /**
+ * Internal method to register all default block entities
+ */
+ private static void registerBlockEntities() {
BlockEntity.registerBlockEntity(BlockEntity.FURNACE, BlockEntityFurnace.class);
BlockEntity.registerBlockEntity(BlockEntity.CHEST, BlockEntityChest.class);
BlockEntity.registerBlockEntity(BlockEntity.SIGN, BlockEntitySign.class);
@@ -2397,29 +2754,162 @@ private void registerBlockEntities() {
BlockEntity.registerBlockEntity(BlockEntity.JUKEBOX, BlockEntityJukebox.class);
BlockEntity.registerBlockEntity(BlockEntity.SHULKER_BOX, BlockEntityShulkerBox.class);
BlockEntity.registerBlockEntity(BlockEntity.BANNER, BlockEntityBanner.class);
+ BlockEntity.registerBlockEntity(BlockEntity.DROPPER, BlockEntityDropper.class);
+ BlockEntity.registerBlockEntity(BlockEntity.DISPENSER, BlockEntityDispenser.class);
+ BlockEntity.registerBlockEntity(BlockEntity.MOB_SPAWNER, BlockEntitySpawner.class);
BlockEntity.registerBlockEntity(BlockEntity.MUSIC, BlockEntityMusic.class);
+ BlockEntity.registerBlockEntity(BlockEntity.CAMPFIRE, BlockEntityCampfire.class);
+ BlockEntity.registerBlockEntity(BlockEntity.BARREL, BlockEntityBarrel.class);
+ BlockEntity.registerBlockEntity(BlockEntity.LECTERN, BlockEntityLectern.class);
+ BlockEntity.registerBlockEntity(BlockEntity.BLAST_FURNACE, BlockEntityBlastFurnace.class);
+ BlockEntity.registerBlockEntity(BlockEntity.SMOKER, BlockEntitySmoker.class);
+ BlockEntity.registerBlockEntity(BlockEntity.BELL, BlockEntityBell.class);
+ BlockEntity.registerBlockEntity(BlockEntity.PERSISTENT_CONTAINER, PersistentDataContainerBlockEntity.class);
}
+ /**
+ * Is nether enabled on this server
+ *
+ * @return nether enabled
+ */
public boolean isNetherAllowed() {
- return this.allowNether;
+ return this.netherEnabled;
}
+ /**
+ * Get player data serializer that is used to save player data
+ *
+ * @return player data serializer
+ */
public PlayerDataSerializer getPlayerDataSerializer() {
return playerDataSerializer;
}
+ /**
+ * Set player data serializer that is used to save player data
+ *
+ * @param playerDataSerializer player data serializer
+ */
public void setPlayerDataSerializer(PlayerDataSerializer playerDataSerializer) {
this.playerDataSerializer = Preconditions.checkNotNull(playerDataSerializer, "playerDataSerializer");
}
- public boolean isIgnoredPacket(Class extends DataPacket> clazz) {
- return this.ignoredPackets.contains(clazz.getSimpleName());
- }
-
+ /**
+ * Get the Server instance
+ *
+ * @return Server
+ */
public static Server getInstance() {
return instance;
}
+ /**
+ * Load server config
+ */
+ private void loadSettings() {
+ /* nukkit.yml */
+
+ this.forceLanguage = this.getConfig("settings.force-language", false);
+ this.queryPlugins = this.getConfig("settings.query-plugins", false);
+
+ this.networkCompressionThreshold = this.getConfig("network.batch-threshold", 256);
+ this.networkCompressionLevel = Math.max(Math.min(this.getConfig("network.compression-level", 4), 9), 0);
+ this.encryptionEnabled = this.getConfig("network.encryption", false);
+
+ this.autoTickRate = this.getConfig("level-settings.auto-tick-rate", true);
+ this.autoTickRateLimit = this.getConfig("level-settings.auto-tick-rate-limit", 20);
+ this.baseTickRate = this.getConfig("level-settings.base-tick-rate", 1);
+ this.alwaysTickPlayers = this.getConfig("level-settings.always-tick-players", false);
+
+ this.useNativeLevelDB = this.getConfig("leveldb.use-native", false);
+ this.levelDbCache = this.getConfig("leveldb.cache-size-mb", 80);
+
+ this.autoSaveTicks = this.getConfig("ticks-per.autosave", 6000);
+
+ this.shouldSavePlayerData = this.getConfig("player.save-player-data", true);
+ this.skinChangeCooldown = this.getConfig("player.skin-change-cooldown", 15);
+ this.attackStopSprint = this.getConfig("player.attack-stop-sprint", true);
+
+ this.chunksPerTick = this.getConfig("chunk-sending.per-tick", 4);
+ this.spawnThreshold = this.getConfig("spawn-threshold", 56);
+ this.cacheChunks = this.getConfig("cache-chunks", false);
+
+ this.c_s_spawnThreshold = (int) Math.ceil(Math.sqrt(this.spawnThreshold));
+
+ /* server.properties */
+
+ this.maxPlayers = this.getPropertyInt("max-players", 20);
+ this.netherEnabled = this.getPropertyBoolean("allow-nether", true);
+ //this.endEnabled = this.getPropertyBoolean("allow-the-end", true);
+ this.xboxAuth = this.getPropertyBoolean("xbox-auth", true);
+ this.achievementsEnabled = this.getPropertyBoolean("achievements", true);
+ this.pvpEnabled = this.getPropertyBoolean("pvp", true);
+ this.announceAchievements = this.getPropertyBoolean("announce-player-achievements", true);
+ this.queryPlugins = this.getPropertyBoolean("query-plugins", false);
+ this.allowFlight = this.getPropertyBoolean("allow-flight", false);
+ this.isHardcore = this.getPropertyBoolean("hardcore", false);
+ this.forceResources = this.getPropertyBoolean("force-resources", false);
+ this.forceResourcesAllowOwnPacks = this.getPropertyBoolean("force-resources-allow-client-packs", false);
+ this.whitelistEnabled = this.getPropertyBoolean("white-list", false);
+ this.forceGamemode = this.getPropertyBoolean("force-gamemode", false);
+ this.motd = this.getPropertyString("motd", "A Minecraft Server");
+ this.viewDistance = this.getPropertyInt("view-distance", 10);
+ this.port = this.getPropertyInt("server-port", 19132);
+ this.ip = this.getPropertyString("server-ip", "0.0.0.0");
+ this.spawnRadius = this.getPropertyInt("spawn-protection", 16);
+
+ this.setAutoSave(this.getPropertyBoolean("auto-save", true));
+
+ try {
+ this.gamemode = this.getPropertyInt("gamemode", 0) & 0b11;
+ } catch (NumberFormatException exception) {
+ this.gamemode = getGamemodeFromString(this.getPropertyString("gamemode")) & 0b11;
+ }
+
+ if (this.isHardcore && this.difficulty < 3) {
+ this.setDifficulty(3);
+ } else {
+ this.setDifficulty(getDifficultyFromString(this.getPropertyString("difficulty", "2")));
+ }
+ }
+
+ /**
+ * This class contains all default server.properties values.
+ */
+ private static class ServerProperties extends ConfigSection {
+ {
+ put("motd", "A Minecraft Server");
+ put("sub-motd", "Powered by Nukkit");
+ put("server-port", 19132);
+ put("server-ip", "0.0.0.0");
+ put("view-distance", 10);
+ put("achievements", true);
+ put("announce-player-achievements", true);
+ put("spawn-protection", 16);
+ put("gamemode", 0);
+ put("force-gamemode", false);
+ put("difficulty", 2);
+ put("hardcore", false);
+ put("pvp", true);
+ put("white-list", false);
+ put("generator-settings", "");
+ put("level-name", "world");
+ put("level-seed", "");
+ put("level-type", "default");
+ put("enable-rcon", false);
+ put("rcon.password", Base64.getEncoder().encodeToString(UUID.randomUUID().toString().replace("-", "").getBytes()).substring(3, 13));
+ put("force-resources", false);
+ put("force-resources-allow-client-packs", false);
+ put("xbox-auth", true);
+ put("auto-save", true);
+ put("force-language", false);
+ put("enable-query", false);
+ put("allow-flight", false);
+ put("allow-nether", true);
+ //put("allow-the-end", true);
+ }
+ }
+
private class ConsoleThread extends Thread implements InterruptibleThread {
@Override
diff --git a/src/main/java/cn/nukkit/api/API.java b/src/main/java/cn/nukkit/api/API.java
deleted file mode 100644
index 9276ee5bbe4..00000000000
--- a/src/main/java/cn/nukkit/api/API.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package cn.nukkit.api;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import static cn.nukkit.api.API.Definition.UNIVERSAL;
-import static cn.nukkit.api.API.Usage.BLEEDING;
-
-/**
- * Describes an API element.
- *
- * @author Lin Mulan, Nukkit Project
- * @see Usage
- * @see Definition
- */
-@Retention(RetentionPolicy.SOURCE)
-@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
-@API(usage = BLEEDING, definition = UNIVERSAL)
-@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.
- *
- *
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
- }
-
- /**
- * 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.
- */
- 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 c5cc814492e..a687d2ded7a 100644
--- a/src/main/java/cn/nukkit/block/Block.java
+++ b/src/main/java/cn/nukkit/block/Block.java
@@ -2,6 +2,8 @@
import cn.nukkit.Player;
import cn.nukkit.Server;
+import cn.nukkit.block.properties.BlockNotImplemented;
+import cn.nukkit.customblock.CustomBlockManager;
import cn.nukkit.entity.Entity;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
@@ -10,6 +12,7 @@
import cn.nukkit.level.Level;
import cn.nukkit.level.MovingObjectPosition;
import cn.nukkit.level.Position;
+import cn.nukkit.level.persistence.PersistentDataContainer;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.Vector3;
@@ -18,6 +21,8 @@
import cn.nukkit.plugin.Plugin;
import cn.nukkit.potion.Effect;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.material.BlockType;
+import cn.nukkit.utils.material.MaterialType;
import java.lang.reflect.Constructor;
import java.util.List;
@@ -25,305 +30,97 @@
import java.util.Optional;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class Block extends Position implements Metadatable, Cloneable, AxisAlignedBB, BlockID {
- public static Class[] list = null;
- public static Block[] fullList = null;
- public static int[] light = null;
- public static int[] lightFilter = null;
- public static boolean[] solid = null;
- public static double[] hardness = null;
- public static boolean[] transparent = null;
+
+ public static final int MAX_BLOCK_ID = 1024;
+ public static final int DATA_BITS = 6;
+ public static final int DATA_SIZE = 1 << DATA_BITS;
+ public static final int DATA_MASK = DATA_SIZE - 1;
+
+ public static final BlockLayer LAYER_NORMAL = BlockLayer.NORMAL;
+ public static final BlockLayer LAYER_WATERLOGGED = BlockLayer.WATERLOGGED;
+
+ @SuppressWarnings("rawtypes")
+ public static Class[] list;
+ public static Block[] fullList;
+ public static int[] light;
+ public static int[] lightFilter;
+ public static boolean[] solid;
+ public static double[] hardness;
+ public static boolean[] transparent;
+ public static boolean[] hasMeta;
+
+ private BlockLayer layer = LAYER_NORMAL;
+
+ private BlockType materialType;
+
/**
- * if a block has can have variants
+ * A commonly used block face pattern
*/
- public static boolean[] hasMeta = null;
+ protected static final int[] faces2534 = {2, 5, 3, 4};
protected Block() {}
- @SuppressWarnings("unchecked")
public static void init() {
if (list == null) {
- list = new Class[256];
- fullList = new Block[4096];
- light = new int[256];
- lightFilter = new int[256];
- solid = new boolean[256];
- hardness = new double[256];
- transparent = new boolean[256];
- hasMeta = new boolean[256];
-
- list[AIR] = BlockAir.class; //0
- list[STONE] = BlockStone.class; //1
- list[GRASS] = BlockGrass.class; //2
- list[DIRT] = BlockDirt.class; //3
- list[COBBLESTONE] = BlockCobblestone.class; //4
- list[PLANKS] = BlockPlanks.class; //5
- list[SAPLING] = BlockSapling.class; //6
- list[BEDROCK] = BlockBedrock.class; //7
- list[WATER] = BlockWater.class; //8
- list[STILL_WATER] = BlockWaterStill.class; //9
- list[LAVA] = BlockLava.class; //10
- list[STILL_LAVA] = BlockLavaStill.class; //11
- list[SAND] = BlockSand.class; //12
- list[GRAVEL] = BlockGravel.class; //13
- list[GOLD_ORE] = BlockOreGold.class; //14
- list[IRON_ORE] = BlockOreIron.class; //15
- list[COAL_ORE] = BlockOreCoal.class; //16
- list[WOOD] = BlockWood.class; //17
- list[LEAVES] = BlockLeaves.class; //18
- list[SPONGE] = BlockSponge.class; //19
- list[GLASS] = BlockGlass.class; //20
- list[LAPIS_ORE] = BlockOreLapis.class; //21
- list[LAPIS_BLOCK] = BlockLapis.class; //22
- list[DISPENSER] = BlockDispenser.class; //23
- list[SANDSTONE] = BlockSandstone.class; //24
- list[NOTEBLOCK] = BlockNoteblock.class; //25
- list[BED_BLOCK] = BlockBed.class; //26
- list[POWERED_RAIL] = BlockRailPowered.class; //27
- list[DETECTOR_RAIL] = BlockRailDetector.class; //28
- list[STICKY_PISTON] = BlockPistonSticky.class; //29
- list[COBWEB] = BlockCobweb.class; //30
- list[TALL_GRASS] = BlockTallGrass.class; //31
- list[DEAD_BUSH] = BlockDeadBush.class; //32
- list[PISTON] = BlockPiston.class; //33
- list[PISTON_HEAD] = BlockPistonHead.class; //34
- list[WOOL] = BlockWool.class; //35
- list[DANDELION] = BlockDandelion.class; //37
- list[FLOWER] = BlockFlower.class; //38
- list[BROWN_MUSHROOM] = BlockMushroomBrown.class; //39
- list[RED_MUSHROOM] = BlockMushroomRed.class; //40
- list[GOLD_BLOCK] = BlockGold.class; //41
- list[IRON_BLOCK] = BlockIron.class; //42
- list[DOUBLE_STONE_SLAB] = BlockDoubleSlabStone.class; //43
- list[STONE_SLAB] = BlockSlabStone.class; //44
- list[BRICKS_BLOCK] = BlockBricks.class; //45
- list[TNT] = BlockTNT.class; //46
- list[BOOKSHELF] = BlockBookshelf.class; //47
- list[MOSS_STONE] = BlockMossStone.class; //48
- list[OBSIDIAN] = BlockObsidian.class; //49
- list[TORCH] = BlockTorch.class; //50
- list[FIRE] = BlockFire.class; //51
- list[MONSTER_SPAWNER] = BlockMobSpawner.class; //52
- list[WOOD_STAIRS] = BlockStairsWood.class; //53
- list[CHEST] = BlockChest.class; //54
- list[REDSTONE_WIRE] = BlockRedstoneWire.class; //55
- list[DIAMOND_ORE] = BlockOreDiamond.class; //56
- list[DIAMOND_BLOCK] = BlockDiamond.class; //57
- list[WORKBENCH] = BlockCraftingTable.class; //58
- list[WHEAT_BLOCK] = BlockWheat.class; //59
- list[FARMLAND] = BlockFarmland.class; //60
- list[FURNACE] = BlockFurnace.class; //61
- list[BURNING_FURNACE] = BlockFurnaceBurning.class; //62
- list[SIGN_POST] = BlockSignPost.class; //63
- list[WOOD_DOOR_BLOCK] = BlockDoorWood.class; //64
- list[LADDER] = BlockLadder.class; //65
- list[RAIL] = BlockRail.class; //66
- list[COBBLESTONE_STAIRS] = BlockStairsCobblestone.class; //67
- list[WALL_SIGN] = BlockWallSign.class; //68
- list[LEVER] = BlockLever.class; //69
- list[STONE_PRESSURE_PLATE] = BlockPressurePlateStone.class; //70
- list[IRON_DOOR_BLOCK] = BlockDoorIron.class; //71
- list[WOODEN_PRESSURE_PLATE] = BlockPressurePlateWood.class; //72
- list[REDSTONE_ORE] = BlockOreRedstone.class; //73
- list[GLOWING_REDSTONE_ORE] = BlockOreRedstoneGlowing.class; //74
- list[UNLIT_REDSTONE_TORCH] = BlockRedstoneTorchUnlit.class;
- list[REDSTONE_TORCH] = BlockRedstoneTorch.class; //76
- list[STONE_BUTTON] = BlockButtonStone.class; //77
- list[SNOW_LAYER] = BlockSnowLayer.class; //78
- list[ICE] = BlockIce.class; //79
- list[SNOW_BLOCK] = BlockSnow.class; //80
- 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
- list[SOUL_SAND] = BlockSoulSand.class; //88
- list[GLOWSTONE_BLOCK] = BlockGlowstone.class; //89
- list[NETHER_PORTAL] = BlockNetherPortal.class; //90
- list[LIT_PUMPKIN] = BlockPumpkinLit.class; //91
- list[CAKE_BLOCK] = BlockCake.class; //92
- list[UNPOWERED_REPEATER] = BlockRedstoneRepeaterUnpowered.class; //93
- list[POWERED_REPEATER] = BlockRedstoneRepeaterPowered.class; //94
- list[INVISIBLE_BEDROCK] = BlockBedrockInvisible.class; //95
- list[TRAPDOOR] = BlockTrapdoor.class; //96
- list[MONSTER_EGG] = BlockMonsterEgg.class; //97
- list[STONE_BRICKS] = BlockBricksStone.class; //98
- list[BROWN_MUSHROOM_BLOCK] = BlockHugeMushroomBrown.class; //99
- list[RED_MUSHROOM_BLOCK] = BlockHugeMushroomRed.class; //100
- list[IRON_BARS] = BlockIronBars.class; //101
- list[GLASS_PANE] = BlockGlassPane.class; //102
- list[MELON_BLOCK] = BlockMelon.class; //103
- list[PUMPKIN_STEM] = BlockStemPumpkin.class; //104
- list[MELON_STEM] = BlockStemMelon.class; //105
- list[VINE] = BlockVine.class; //106
- list[FENCE_GATE] = BlockFenceGate.class; //107
- list[BRICK_STAIRS] = BlockStairsBrick.class; //108
- list[STONE_BRICK_STAIRS] = BlockStairsStoneBrick.class; //109
- list[MYCELIUM] = BlockMycelium.class; //110
- list[WATER_LILY] = BlockWaterLily.class; //111
- list[NETHER_BRICKS] = BlockBricksNether.class; //112
- list[NETHER_BRICK_FENCE] = BlockFenceNetherBrick.class; //113
- list[NETHER_BRICKS_STAIRS] = BlockStairsNetherBrick.class; //114
- list[NETHER_WART_BLOCK] = BlockNetherWart.class; //115
- list[ENCHANTING_TABLE] = BlockEnchantingTable.class; //116
- list[BREWING_STAND_BLOCK] = BlockBrewingStand.class; //117
- list[CAULDRON_BLOCK] = BlockCauldron.class; //118
- list[END_PORTAL] = BlockEndPortal.class; //119
- list[END_PORTAL_FRAME] = BlockEndPortalFrame.class; //120
- list[END_STONE] = BlockEndStone.class; //121
- list[DRAGON_EGG] = BlockDragonEgg.class; //122
- list[REDSTONE_LAMP] = BlockRedstoneLamp.class; //123
- list[LIT_REDSTONE_LAMP] = BlockRedstoneLampLit.class; //124
- //TODO: list[DROPPER] = BlockDropper.class; //125
- list[ACTIVATOR_RAIL] = BlockRailActivator.class; //126
- list[COCOA] = BlockCocoa.class; //127
- list[SANDSTONE_STAIRS] = BlockStairsSandstone.class; //128
- list[EMERALD_ORE] = BlockOreEmerald.class; //129
- list[ENDER_CHEST] = BlockEnderChest.class; //130
- list[TRIPWIRE_HOOK] = BlockTripWireHook.class;
- list[TRIPWIRE] = BlockTripWire.class; //132
- list[EMERALD_BLOCK] = BlockEmerald.class; //133
- list[SPRUCE_WOOD_STAIRS] = BlockStairsSpruce.class; //134
- list[BIRCH_WOOD_STAIRS] = BlockStairsBirch.class; //135
- list[JUNGLE_WOOD_STAIRS] = BlockStairsJungle.class; //136
-
- list[BEACON] = BlockBeacon.class; //138
- list[STONE_WALL] = BlockWall.class; //139
- list[FLOWER_POT_BLOCK] = BlockFlowerPot.class; //140
- list[CARROT_BLOCK] = BlockCarrot.class; //141
- list[POTATO_BLOCK] = BlockPotato.class; //142
- list[WOODEN_BUTTON] = BlockButtonWooden.class; //143
- list[SKULL_BLOCK] = BlockSkull.class; //144
- list[ANVIL] = BlockAnvil.class; //145
- list[TRAPPED_CHEST] = BlockTrappedChest.class; //146
- list[LIGHT_WEIGHTED_PRESSURE_PLATE] = BlockWeightedPressurePlateLight.class; //147
- list[HEAVY_WEIGHTED_PRESSURE_PLATE] = BlockWeightedPressurePlateHeavy.class; //148
- list[UNPOWERED_COMPARATOR] = BlockRedstoneComparatorUnpowered.class; //149
- list[POWERED_COMPARATOR] = BlockRedstoneComparatorPowered.class; //149
- list[DAYLIGHT_DETECTOR] = BlockDaylightDetector.class; //151
- list[REDSTONE_BLOCK] = BlockRedstone.class; //152
- list[QUARTZ_ORE] = BlockOreQuartz.class; //153
- list[HOPPER_BLOCK] = BlockHopper.class; //154
- list[QUARTZ_BLOCK] = BlockQuartz.class; //155
- list[QUARTZ_STAIRS] = BlockStairsQuartz.class; //156
- list[DOUBLE_WOOD_SLAB] = BlockDoubleSlabWood.class; //157
- list[WOOD_SLAB] = BlockSlabWood.class; //158
- list[STAINED_TERRACOTTA] = BlockTerracottaStained.class; //159
- list[STAINED_GLASS_PANE] = BlockGlassPaneStained.class; //160
-
- list[LEAVES2] = BlockLeaves2.class; //161
- list[WOOD2] = BlockWood2.class; //162
- list[ACACIA_WOOD_STAIRS] = BlockStairsAcacia.class; //163
- list[DARK_OAK_WOOD_STAIRS] = BlockStairsDarkOak.class; //164
- list[SLIME_BLOCK] = BlockSlime.class; //165
-
- list[IRON_TRAPDOOR] = BlockTrapdoorIron.class; //167
- list[PRISMARINE] = BlockPrismarine.class; //168
- list[SEA_LANTERN] = BlockSeaLantern.class; //169
- list[HAY_BALE] = BlockHayBale.class; //170
- list[CARPET] = BlockCarpet.class; //171
- list[TERRACOTTA] = BlockTerracotta.class; //172
- list[COAL_BLOCK] = BlockCoal.class; //173
- list[PACKED_ICE] = BlockIcePacked.class; //174
- list[DOUBLE_PLANT] = BlockDoublePlant.class; //175
- list[STANDING_BANNER] = BlockBanner.class; //176
- list[WALL_BANNER] = BlockWallBanner.class; //177
- list[DAYLIGHT_DETECTOR_INVERTED] = BlockDaylightDetectorInverted.class; //178
- list[RED_SANDSTONE] = BlockRedSandstone.class; //179
- list[RED_SANDSTONE_STAIRS] = BlockStairsRedSandstone.class; //180
- list[DOUBLE_RED_SANDSTONE_SLAB] = BlockDoubleSlabRedSandstone.class; //181
- list[RED_SANDSTONE_SLAB] = BlockSlabRedSandstone.class; //182
- list[FENCE_GATE_SPRUCE] = BlockFenceGateSpruce.class; //183
- list[FENCE_GATE_BIRCH] = BlockFenceGateBirch.class; //184
- list[FENCE_GATE_JUNGLE] = BlockFenceGateJungle.class; //185
- list[FENCE_GATE_DARK_OAK] = BlockFenceGateDarkOak.class; //186
- list[FENCE_GATE_ACACIA] = BlockFenceGateAcacia.class; //187
-
- list[SPRUCE_DOOR_BLOCK] = BlockDoorSpruce.class; //193
- list[BIRCH_DOOR_BLOCK] = BlockDoorBirch.class; //194
- list[JUNGLE_DOOR_BLOCK] = BlockDoorJungle.class; //195
- list[ACACIA_DOOR_BLOCK] = BlockDoorAcacia.class; //196
- list[DARK_OAK_DOOR_BLOCK] = BlockDoorDarkOak.class; //197
- list[GRASS_PATH] = BlockGrassPath.class; //198
- list[ITEM_FRAME_BLOCK] = BlockItemFrame.class; //199
- list[CHORUS_FLOWER] = BlockChorusFlower.class; //200
- list[PURPUR_BLOCK] = BlockPurpur.class; //201
-
- list[PURPUR_STAIRS] = BlockStairsPurpur.class; //203
-
- list[UNDYED_SHULKER_BOX] = BlockUndyedShulkerBox.class; //205
- list[END_BRICKS] = BlockBricksEndStone.class; //206
-
- list[END_ROD] = BlockEndRod.class; //208
- list[END_GATEWAY] = BlockEndGateway.class; //209
-
- list[MAGMA] = BlockMagma.class; //213
- list[BLOCK_NETHER_WART_BLOCK] = BlockNetherWartBlock.class; //214
- list[RED_NETHER_BRICK] = BlockBricksRedNether.class; //215
- list[BONE_BLOCK] = BlockBone.class; //216
-
- list[SHULKER_BOX] = BlockShulkerBox.class; //218
- list[PURPLE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedPurple.class; //219
- list[WHITE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedWhite.class; //220
- list[ORANGE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedOrange.class; //221
- list[MAGENTA_GLAZED_TERRACOTTA] = BlockTerracottaGlazedMagenta.class; //222
- list[LIGHT_BLUE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedLightBlue.class; //223
- list[YELLOW_GLAZED_TERRACOTTA] = BlockTerracottaGlazedYellow.class; //224
- list[LIME_GLAZED_TERRACOTTA] = BlockTerracottaGlazedLime.class; //225
- list[PINK_GLAZED_TERRACOTTA] = BlockTerracottaGlazedPink.class; //226
- list[GRAY_GLAZED_TERRACOTTA] = BlockTerracottaGlazedGray.class; //227
- list[SILVER_GLAZED_TERRACOTTA] = BlockTerracottaGlazedSilver.class; //228
- list[CYAN_GLAZED_TERRACOTTA] = BlockTerracottaGlazedCyan.class; //229
-
- list[BLUE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedBlue.class; //231
- list[BROWN_GLAZED_TERRACOTTA] = BlockTerracottaGlazedBrown.class; //232
- list[GREEN_GLAZED_TERRACOTTA] = BlockTerracottaGlazedGreen.class; //233
- list[RED_GLAZED_TERRACOTTA] = BlockTerracottaGlazedRed.class; //234
- list[BLACK_GLAZED_TERRACOTTA] = BlockTerracottaGlazedBlack.class; //235
- list[CONCRETE] = BlockConcrete.class; //236
- list[CONCRETE_POWDER] = BlockConcretePowder.class; //237
-
- list[CHORUS_PLANT] = BlockChorusPlant.class; //240
- list[STAINED_GLASS] = BlockGlassStained.class; //241
- list[PODZOL] = BlockPodzol.class; //243
- list[BEETROOT_BLOCK] = BlockBeetroot.class; //244
- list[STONECUTTER] = BlockStonecutter.class; //245
- list[GLOWING_OBSIDIAN] = BlockObsidianGlowing.class; //246
- //list[NETHER_REACTOR] = BlockNetherReactor.class; //247 Should not be removed
-
- //TODO: list[PISTON_EXTENSION] = BlockPistonExtension.class; //250
-
- list[OBSERVER] = BlockObserver.class; //251
-
- for (int id = 0; id < 256; id++) {
- Class c = list[id];
+ list = new Class[MAX_BLOCK_ID];
+ fullList = new Block[MAX_BLOCK_ID * DATA_SIZE];
+ light = new int[MAX_BLOCK_ID];
+ lightFilter = new int[MAX_BLOCK_ID];
+ solid = new boolean[MAX_BLOCK_ID];
+ hardness = new double[MAX_BLOCK_ID];
+ transparent = new boolean[MAX_BLOCK_ID];
+ hasMeta = new boolean[MAX_BLOCK_ID];
+
+ Blocks.init();
+
+ for (int id = 0; id < MAX_BLOCK_ID; id++) {
+ Class> c = list[id];
if (c != null) {
Block block;
try {
- block = (Block) c.newInstance();
- try {
- Constructor constructor = c.getDeclaredConstructor(int.class);
+ if (c.isAssignableFrom(BlockNotImplemented.class)) {
+ Constructor> constructor = c.getDeclaredConstructor(int.class, int.class);
constructor.setAccessible(true);
- for (int data = 0; data < 16; ++data) {
- fullList[(id << 4) | data] = (Block) constructor.newInstance(data);
+ block = (Block) constructor.newInstance(id, 0);
+ for (int data = 0; data < (1 << DATA_BITS); ++data) {
+ int fullId = (id << DATA_BITS) | data;
+ fullList[fullId] = (Block) constructor.newInstance(id, data);
}
hasMeta[id] = true;
- } catch (NoSuchMethodException ignore) {
- for (int data = 0; data < 16; ++data) {
- fullList[(id << 4) | data] = block;
+ } else {
+ block = (Block) c.newInstance();
+ try {
+ @SuppressWarnings("rawtypes")
+ Constructor constructor = c.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ for (int data = 0; data < (1 << DATA_BITS); ++data) {
+ int fullId = (id << DATA_BITS) | data;
+ Block blockState;
+ try {
+ blockState = (Block) constructor.newInstance(data);
+ if (blockState.getDamage() != data) {
+ blockState = new BlockUnknown(id, data);
+ }
+ } catch (Exception e) {
+ Server.getInstance().getLogger().error("Error while registering " + c.getName(), e);
+ blockState = new BlockUnknown(id, data);
+ }
+ fullList[fullId] = blockState;
+ }
+ hasMeta[id] = true;
+ } catch (NoSuchMethodException ignore) {
+ for (int data = 0; data < DATA_SIZE; ++data) {
+ int fullId = (id << DATA_BITS) | data;
+ fullList[fullId] = block;
+ }
}
}
} catch (Exception e) {
- Server.getInstance().getLogger().error("Error while registering " + c.getName(), e);
- for (int data = 0; data < 16; ++data) {
- fullList[(id << 4) | data] = new BlockUnknown(id, data);
- }
- return;
+ throw new RuntimeException("Error while registering " + c.getName(), e);
}
solid[id] = block.isSolid();
@@ -338,7 +135,11 @@ public static void init() {
} else {
lightFilter[id] = 1;
}
- } else {
+ } else if (block instanceof BlockSlime) {
+ lightFilter[id] = 1;
+ } else if (id == CAULDRON_BLOCK) {
+ lightFilter[id] = 3;
+ }else {
lightFilter[id] = 15;
}
} else {
@@ -346,56 +147,182 @@ public static void init() {
}
} else {
lightFilter[id] = 1;
- for (int data = 0; data < 16; ++data) {
- fullList[(id << 4) | data] = new BlockUnknown(id, data);
+ for (int data = 0; data < DATA_SIZE; ++data) {
+ fullList[(id << DATA_BITS) | data] = new BlockUnknown(id, data);
}
}
}
}
}
+ public static Block get(MaterialType type) {
+ return get(type, 0);
+ }
+
+ public static Block get(MaterialType type, Integer meta) {
+ if (!(type instanceof BlockType)) {
+ throw new IllegalArgumentException("Expected BlockType, got " + type.getClass().getSimpleName());
+ }
+ return get(type.getLegacyId(), meta);
+ }
+
public static Block get(int id) {
- return fullList[id << 4].clone();
+ if (id < 0) {
+ id = 255 - id;
+ }
+
+ if (id >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return CustomBlockManager.get().getBlock(id, 0);
+ }
+ return fullList[id << DATA_BITS].clone();
}
public static Block get(int id, Integer meta) {
- if (meta != null) {
- return fullList[(id << 4) + meta].clone();
- } else {
- return fullList[id << 4].clone();
+ if (id < 0) {
+ id = 255 - id;
+ }
+
+ int fullId = meta == null ? (id << DATA_BITS ) : ((id << DATA_BITS) | meta);
+ if (id >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return CustomBlockManager.get().getBlock(fullId);
}
+
+ return fullList[fullId].clone();
}
public static Block get(int id, Integer meta, Position pos) {
- Block block = fullList[(id << 4) | (meta == null ? 0 : meta)].clone();
+ return get(id, meta, pos, LAYER_NORMAL);
+ }
+
+ public static Block get(int id, Integer meta, Position pos, BlockLayer layer) {
+ if (id < 0) {
+ id = 255 - id;
+ }
+
+ Block block;
+ if (id >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ int fullId = (meta != null && meta > DATA_SIZE) ? (id << DATA_BITS) : ((id << DATA_BITS) | (meta == null ? 0 : meta));
+ block = CustomBlockManager.get().getBlock(fullId);
+ } else if (meta != null && meta > DATA_SIZE) {
+ block = fullList[id << DATA_BITS].clone();
+ block.setDamage(meta);
+ } else {
+ block = fullList[(id << DATA_BITS) | (meta == null ? 0 : meta)].clone();
+ }
+
if (pos != null) {
block.x = pos.x;
block.y = pos.y;
block.z = pos.z;
block.level = pos.level;
+ block.layer = layer;
}
return block;
}
public static Block get(int id, int data) {
- return fullList[(id << 4) + data].clone();
+ if (id < 0) {
+ id = 255 - id;
+ }
+
+ int fullId = (id << DATA_BITS ) | data;
+ if (id >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return CustomBlockManager.get().getBlock(fullId);
+ }
+ return fullList[fullId].clone();
}
public static Block get(int fullId, Level level, int x, int y, int z) {
- Block block = fullList[fullId].clone();
+ return get(fullId, level, x, y, z, LAYER_NORMAL);
+ }
+
+ public static Block get(int fullId, Level level, int x, int y, int z, BlockLayer layer) {
+ Block block;
+ if ((fullId >> DATA_BITS) >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ block = CustomBlockManager.get().getBlock(fullId);
+ } else {
+ block = fullList[fullId].clone();
+ }
+
block.x = x;
block.y = y;
block.z = z;
block.level = level;
+ block.layer = layer;
return block;
}
+ public static Block getUnsafe(int fullId) {
+ if ((fullId >> DATA_BITS ) >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return CustomBlockManager.get().getBlock(fullId);
+ }
+ return fullList[fullId];
+ }
+
+ public static int getBlockLight(int blockId) {
+ if (blockId >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return light[0]; // TODO: just temporary
+ }
+ return light[blockId];
+ }
+
+ public static int getBlockLightFilter(int blockId) {
+ if (blockId >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return lightFilter[0]; // TODO: just temporary
+ }
+ return lightFilter[blockId];
+ }
+
+ public static boolean isBlockSolidById(int blockId) {
+ if (blockId >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return solid[1]; // TODO: just temporary
+ }
+ return solid[blockId];
+ }
+
+ public static boolean isBlockTransparentById(int blockId) {
+ if (blockId >= CustomBlockManager.LOWEST_CUSTOM_BLOCK_ID) {
+ return transparent[1]; // TODO: just temporary
+ }
+ return transparent[blockId];
+ }
+
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- return this.getLevel().setBlock(this, this, true, true);
+ return this.canPlaceOn(block.down(), target) && this.getLevel().setBlock(this, this, true, true);
}
- //http://minecraft.gamepedia.com/Breaking
- public boolean canHarvestWithHand() { //used for calculating breaking time
+ public boolean canPlaceOn(Block floor, Position pos) {
+ return this.canBePlaced();
+ }
+
+ public boolean canHarvest(Item item) {
+ return this.canHarvestWithHand() || this.getToolTier() == 0 || this.getToolType() == ItemTool.TYPE_NONE || correctTool0(this.getToolType(), item, this.getId()) && item.getTier() >= this.getToolType();
+ }
+
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.NO_WATERLOGGING;
+ }
+
+ public enum WaterloggingType {
+ /**
+ * Block is not waterloggable
+ */
+ NO_WATERLOGGING,
+ /**
+ * If possible, water will be set to second layer when the block is placed into water
+ */
+ WHEN_PLACED_IN_WATER,
+ /**
+ * Water will flow into the block and water will be set to second layer
+ */
+ FLOW_INTO_BLOCK
+ }
+
+ public final boolean canWaterloggingFlowInto() {
+ return this.canBeFlowedInto() || this.getWaterloggingType() == WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ public boolean canHarvestWithHand() {
return true;
}
@@ -407,6 +334,10 @@ public int tickRate() {
return 10;
}
+ public boolean onBreak(Item item, Player player) {
+ return this.onBreak(item);
+ }
+
public boolean onBreak(Item item) {
return this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, true);
}
@@ -443,6 +374,10 @@ public int getToolType() {
return ItemTool.TYPE_NONE;
}
+ public int getToolTier() {
+ return 0;
+ }
+
public double getFrictionFactor() {
return 0.6;
}
@@ -487,6 +422,10 @@ public boolean canBePushed() {
return true;
}
+ public boolean breakWhenPushed() {
+ return false;
+ }
+
public boolean hasComparatorInputOverride() {
return false;
}
@@ -507,14 +446,30 @@ public BlockColor getColor() {
public abstract int getId();
+ public BlockType getBlockType() {
+ if (this.materialType == null) {
+ this.materialType = BlockTypes.getFromLegacy(this.getId());
+ }
+ return this.materialType;
+ }
+
/**
* The full id is a combination of the id and data.
* @return full id
*/
public int getFullId() {
- return (getId() << 4);
+ return getId() << DATA_BITS;
}
+ public int getItemId() {
+ int id = this.getId();
+ if (id > 255) {
+ return 255 - id;
+ }
+ return id;
+ }
+
+
public void addVelocityToEntity(Entity entity, Vector3 vector) {
}
@@ -524,7 +479,6 @@ public int getDamage() {
}
public void setDamage(int meta) {
- // Do nothing
}
public final void setDamage(Integer meta) {
@@ -539,13 +493,14 @@ final public void position(Position v) {
}
public Item[] getDrops(Item item) {
- if (this.getId() < 0 || this.getId() > list.length) { //Unknown blocks
+ if (this.getId() < 0 || this.getId() > list.length) {
return new Item[0];
- } else {
- return new Item[]{
- this.toItem()
- };
}
+
+ if (this.canHarvestWithHand() || this.canHarvest(item)) {
+ return new Item[]{this.toItem()};
+ }
+ return new Item[0];
}
private static double toolBreakTimeBonus0(int toolType, int toolTier, int blockId) {
@@ -596,7 +551,20 @@ private static int toolType0(Item item) {
return ItemTool.TYPE_NONE;
}
- private static boolean correctTool0(int blockToolType, Item item) {
+ private static boolean correctTool0(int blockToolType, Item item, int blockId) {
+ if (item.isShears() && (blockId == COBWEB || blockId == LEAVES || blockId == LEAVES2)) {
+ return true;
+ }
+
+ if ((blockId == LEAVES && item.isHoe()) ||
+ (blockId == LEAVES2 && item.isHoe())) {
+ return (blockToolType == ItemTool.TYPE_SHEARS && item.isHoe());
+ }
+
+ return correctTool(blockToolType, item);
+ }
+
+ private static boolean correctTool(int blockToolType, Item item) {
return (blockToolType == ItemTool.TYPE_SWORD && item.isSword()) ||
(blockToolType == ItemTool.TYPE_SHOVEL && item.isShovel()) ||
(blockToolType == ItemTool.TYPE_PICKAXE && item.isPickaxe()) ||
@@ -630,7 +598,7 @@ public double getBreakTime(Item item, Player player) {
}
int blockId = getId();
- boolean correctTool = correctTool0(getToolType(), item)
+ boolean correctTool = correctTool0(getToolType(), item, blockId)
|| item.isShears() && (blockId == COBWEB || blockId == LEAVES || blockId == LEAVES2);
boolean canHarvestWithHand = canHarvestWithHand();
int itemToolType = toolType0(item);
@@ -647,79 +615,28 @@ public double getBreakTime(Item item, Player player) {
efficiencyLoreLevel, hasteEffectLevel, insideOfWaterWithoutAquaAffinity, outOfWaterButNotOnGround);
}
- /**
- * @deprecated This function is lack of Player class and is not accurate enough, use #getBreakTime(Item, Player)
- * @param item item used
- * @return break time
- */
- @Deprecated
- public double getBreakTime(Item item) {
- double base = this.getHardness() * 1.5;
- if (this.canBeBrokenWith(item)) {
- if (this.getToolType() == ItemTool.TYPE_SHEARS && item.isShears()) {
- base /= 15;
- } else if (
- (this.getToolType() == ItemTool.TYPE_PICKAXE && item.isPickaxe()) ||
- (this.getToolType() == ItemTool.TYPE_AXE && item.isAxe()) ||
- (this.getToolType() == ItemTool.TYPE_SHOVEL && item.isShovel()) ||
- (this.getToolType() == ItemTool.TYPE_HOE && item.isHoe())
- ) {
- int tier = item.getTier();
- switch (tier) {
- case ItemTool.TIER_WOODEN:
- base /= 2;
- break;
- case ItemTool.TIER_STONE:
- base /= 4;
- break;
- case ItemTool.TIER_IRON:
- base /= 6;
- break;
- case ItemTool.TIER_DIAMOND:
- base /= 8;
- break;
- case ItemTool.TIER_NETHERITE:
- base /= 9;
- break;
- case ItemTool.TIER_GOLD:
- base /= 12;
- break;
- }
- }
- } else {
- base *= 3.33;
- }
-
- if (item.isSword()) {
- base *= 0.5;
- }
-
- return base;
- }
-
public boolean canBeBrokenWith(Item item) {
return this.getHardness() != -1;
}
public Block getSide(BlockFace face) {
- if (this.isValid()) {
- return this.getLevel().getBlock((int) x + face.getXOffset(), (int) y + face.getYOffset(), (int) z + face.getZOffset());
- }
- return this.getSide(face, 1);
+ return this.getSide(this.layer, face, 1);
}
public Block getSide(BlockFace face, int step) {
+ return this.getSide(this.layer, face, step);
+ }
+
+ public Block getSide(BlockLayer layer, BlockFace face) {
+ return this.getSide(layer, face, 1);
+ }
+
+ public Block getSide(BlockLayer layer, BlockFace face, int step) {
if (this.isValid()) {
- if (step == 1) {
- return this.getLevel().getBlock((int) x + face.getXOffset(), (int) y + face.getYOffset(), (int) z + face.getZOffset());
- } else {
- return this.getLevel().getBlock((int) x + face.getXOffset() * step, (int) y + face.getYOffset() * step, (int) z + face.getZOffset() * step);
- }
+ return this.getLevel().getBlock(super.getSide(face, step), layer, true);
}
- Block block = Block.get(Item.AIR, 0);
- block.x = (int) x + face.getXOffset() * step;
- block.y = (int) y + face.getYOffset() * step;
- block.z = (int) z + face.getZOffset() * step;
+ Block block = Block.get(Item.AIR, 0, Position.fromObject(new Vector3(this.x, this.y, this.z).getSide(face, step)));
+ block.layer = layer;
return block;
}
@@ -731,6 +648,10 @@ public Block up(int step) {
return getSide(BlockFace.UP, step);
}
+ public Block up(int step, BlockLayer layer) {
+ return this.getSide(layer, BlockFace.UP, step);
+ }
+
public Block down() {
return down(1);
}
@@ -739,6 +660,10 @@ public Block down(int step) {
return getSide(BlockFace.DOWN, step);
}
+ public Block down(int step, BlockLayer layer) {
+ return this.getSide(layer, BlockFace.DOWN, step);
+ }
+
public Block north() {
return north(1);
}
@@ -747,6 +672,10 @@ public Block north(int step) {
return getSide(BlockFace.NORTH, step);
}
+ public Block north(int step, BlockLayer layer) {
+ return this.getSide(layer, BlockFace.NORTH, step);
+ }
+
public Block south() {
return south(1);
}
@@ -755,6 +684,10 @@ public Block south(int step) {
return getSide(BlockFace.SOUTH, step);
}
+ public Block south(int step, BlockLayer layer) {
+ return this.getSide(layer, BlockFace.SOUTH, step);
+ }
+
public Block east() {
return east(1);
}
@@ -763,6 +696,10 @@ public Block east(int step) {
return getSide(BlockFace.EAST, step);
}
+ public Block east(int step, BlockLayer layer) {
+ return this.getSide(layer, BlockFace.EAST, step);
+ }
+
public Block west() {
return west(1);
}
@@ -771,9 +708,20 @@ public Block west(int step) {
return getSide(BlockFace.WEST, step);
}
+ public Block west(int step, BlockLayer layer) {
+ return this.getSide(layer, BlockFace.WEST, step);
+ }
+
+ protected boolean isBlockAboveAir() {
+ if (this.level == null) {
+ return true;
+ }
+ return this.level.getBlockIdAt((int) this.x, (int) this.y + 1, (int) this.z) == AIR;
+ }
+
@Override
public String toString() {
- return "Block[" + this.getName() + "] (" + this.getId() + ":" + this.getDamage() + ")";
+ return "Block[" + this.getName() + '|' + this.layer + "] (" + this.getId() + ':' + this.getDamage() + ')';
}
public boolean collidesWithBB(AxisAlignedBB bb) {
@@ -786,7 +734,6 @@ public boolean collidesWithBB(AxisAlignedBB bb, boolean collisionBB) {
}
public void onEntityCollide(Entity entity) {
-
}
public AxisAlignedBB getBoundingBox() {
@@ -801,6 +748,10 @@ protected AxisAlignedBB recalculateBoundingBox() {
return this;
}
+ protected AxisAlignedBB recalculateCollisionBoundingBox() {
+ return this.getBoundingBox();
+ }
+
@Override
public double getMinX() {
return this.x;
@@ -831,10 +782,6 @@ public double getMaxZ() {
return this.z + 1;
}
- protected AxisAlignedBB recalculateCollisionBoundingBox() {
- return getBoundingBox();
- }
-
public MovingObjectPosition calculateIntercept(Vector3 pos1, Vector3 pos2) {
AxisAlignedBB bb = this.getBoundingBox();
if (bb == null) {
@@ -967,7 +914,7 @@ public boolean isPowerSource() {
}
public String getLocationHash() {
- return this.getFloorX() + ":" + this.getFloorY() + ":" + this.getFloorZ();
+ return this.getFloorX() + ":" + this.getFloorY() + ':' + this.getFloorZ();
}
public int getDropExp() {
@@ -993,4 +940,78 @@ public Item toItem() {
public boolean canSilkTouch() {
return false;
}
+
+ public void setLayer(BlockLayer layer) {
+ this.layer = layer;
+ }
+
+ public BlockLayer getLayer() {
+ return this.layer;
+ }
+
+ protected static boolean canStayOnFullSolid(Block down) {
+ if (down.isTransparent()) {
+ switch (down.getId()) {
+ case BEACON:
+ case ICE:
+ case GLASS:
+ case STAINED_GLASS:
+ case HARD_GLASS:
+ case HARD_STAINED_GLASS:
+ case SCAFFOLDING:
+ case BARRIER:
+ case GLOWSTONE:
+ case SEA_LANTERN:
+ case HOPPER_BLOCK:
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ protected static boolean canStayOnFullNonSolid(Block down) {
+ if (canStayOnFullSolid(down)) {
+ return true;
+ }
+ switch (down.getId()) {
+ case COMPOSTER:
+ case CAULDRON_BLOCK:
+ case LAVA_CAULDRON:
+ return true;
+ }
+ return false;
+ }
+
+ public boolean alwaysDropsOnExplosion() {
+ return false;
+ }
+
+ /**
+ * Check whether client will see a block as water (is water or uses fake waterlogging)
+ * @param id block id
+ * @return block has water
+ */
+ public static boolean hasWater(int id) {
+ return id == WATER || id == STILL_WATER;
+ }
+
+ public Block setUpdatePos(Vector3 pos) {
+ return this; // Only need to save this for observers
+ }
+
+ public PersistentDataContainer getPersistentDataContainer() {
+ if (!this.isValid()) {
+ throw new IllegalStateException("Block does not have valid level");
+ }
+ return this.level.getPersistentDataContainer(this);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean hasPersistentDataContainer() {
+ if (!this.isValid()) {
+ throw new IllegalStateException("Block does not have valid level");
+ }
+ return this.level.hasPersistentDataContainer(this);
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockAcaciaSignStanding.java b/src/main/java/cn/nukkit/block/BlockAcaciaSignStanding.java
new file mode 100644
index 00000000000..da53929096d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAcaciaSignStanding.java
@@ -0,0 +1,45 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockAcaciaSignStanding extends BlockSignPost {
+
+ public BlockAcaciaSignStanding() {
+ this(0);
+ }
+
+ public BlockAcaciaSignStanding(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Acacia Sign Post";
+ }
+
+ @Override
+ public int getId() {
+ return ACACIA_STANDING_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.ACACIA_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return ACACIA_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return ACACIA_WALL_SIGN;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAcaciaWallSign.java b/src/main/java/cn/nukkit/block/BlockAcaciaWallSign.java
new file mode 100644
index 00000000000..05c1946614c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAcaciaWallSign.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockAcaciaWallSign extends BlockWallSign {
+
+ public BlockAcaciaWallSign() {
+ this(0);
+ }
+
+ public BlockAcaciaWallSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Acacia Wall Sign";
+ }
+
+ @Override
+ public int getId() {
+ return ACACIA_WALL_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.ACACIA_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return ACACIA_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return ACACIA_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAir.java b/src/main/java/cn/nukkit/block/BlockAir.java
index 1fe3b4167ec..f19ca3032d5 100644
--- a/src/main/java/cn/nukkit/block/BlockAir.java
+++ b/src/main/java/cn/nukkit/block/BlockAir.java
@@ -5,13 +5,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockAir extends BlockTransparent {
- public BlockAir() {}
-
@Override
public int getId() {
return AIR;
diff --git a/src/main/java/cn/nukkit/block/BlockAllow.java b/src/main/java/cn/nukkit/block/BlockAllow.java
new file mode 100644
index 00000000000..c3e71784320
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAllow.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockAllow extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return ALLOW;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public String getName() {
+ return "Allow";
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAmethyst.java b/src/main/java/cn/nukkit/block/BlockAmethyst.java
new file mode 100644
index 00000000000..add8cd19147
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAmethyst.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockAmethyst extends BlockSolid {
+
+ public BlockAmethyst() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Amethyst Block";
+ }
+
+ @Override
+ public int getId() {
+ return AMETHYST_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ // TODO:
+ /*@Override
+ public boolean isLavaResistant() {
+ return true;
+ }*/
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAmethystBud.java b/src/main/java/cn/nukkit/block/BlockAmethystBud.java
new file mode 100644
index 00000000000..2ded212179c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAmethystBud.java
@@ -0,0 +1,111 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.SimpleAxisAlignedBB;
+import cn.nukkit.utils.Faceable;
+
+public abstract class BlockAmethystBud extends BlockTransparentMeta implements Faceable {
+
+ public BlockAmethystBud() {
+ this(0);
+ }
+
+ public BlockAmethystBud(int meta) {
+ super(meta);
+ }
+
+ protected abstract String getSizeName();
+
+ @Override
+ public String getName() {
+ return this.getSizeName() + " Amethyst Bud";
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(face.getIndex());
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromIndex(this.getDamage());
+ }
+
+ public void setBlockFace(BlockFace face) {
+ this.setDamage(face.getIndex());
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ switch (this.getBlockFace()) {
+ case UP:
+ return boundingBox(this.getCrystalOffset(), 0, this.getCrystalOffset(), 16 - this.getCrystalOffset(),
+ this.getCrystalHeight(), 16 - this.getCrystalOffset());
+ case DOWN:
+ return boundingBox(this.getCrystalOffset(), 16 - this.getCrystalHeight(), this.getCrystalOffset(),
+ 16 - this.getCrystalOffset(), 16, 16 - this.getCrystalOffset());
+ case NORTH:
+ return boundingBox(this.getCrystalOffset(), this.getCrystalOffset(), 16 - this.getCrystalHeight(),
+ 16 - this.getCrystalOffset(), 16 - this.getCrystalOffset(), 16);
+ case SOUTH:
+ return boundingBox(this.getCrystalOffset(), this.getCrystalOffset(), 0,
+ 16 - this.getCrystalOffset(), 16 - this.getCrystalOffset(), this.getCrystalHeight());
+ case EAST:
+ return boundingBox(0, this.getCrystalOffset(), this.getCrystalOffset(),
+ this.getCrystalHeight(), 16 - this.getCrystalOffset(), 16 - this.getCrystalOffset());
+ case WEST:
+ return boundingBox(16 - this.getCrystalHeight(), this.getCrystalOffset(), this.getCrystalHeight(),
+ 16, 16 - this.getCrystalOffset(), 16 - this.getCrystalOffset());
+ }
+ return super.recalculateBoundingBox();
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1.5;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ protected abstract int getCrystalHeight();
+ protected abstract int getCrystalOffset();
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ protected static AxisAlignedBB boundingBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
+ return new SimpleAxisAlignedBB(minX / 16.0D, minY / 16.0D, minZ / 16.0D, maxX / 16.0D, maxY / 16.0D, maxZ / 16.0D);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAmethystBudLarge.java b/src/main/java/cn/nukkit/block/BlockAmethystBudLarge.java
new file mode 100644
index 00000000000..ef9f7fea2e6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAmethystBudLarge.java
@@ -0,0 +1,37 @@
+package cn.nukkit.block;
+
+public class BlockAmethystBudLarge extends BlockAmethystBud {
+
+ public BlockAmethystBudLarge() {
+ this(0);
+ }
+
+ public BlockAmethystBudLarge(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return LARGE_AMETHYST_BUD;
+ }
+
+ @Override
+ protected String getSizeName() {
+ return "Large";
+ }
+
+ @Override
+ protected int getCrystalHeight() {
+ return 5;
+ }
+
+ @Override
+ protected int getCrystalOffset() {
+ return 3;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 4;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAmethystBudMedium.java b/src/main/java/cn/nukkit/block/BlockAmethystBudMedium.java
new file mode 100644
index 00000000000..5afdae1b0c5
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAmethystBudMedium.java
@@ -0,0 +1,37 @@
+package cn.nukkit.block;
+
+public class BlockAmethystBudMedium extends BlockAmethystBud {
+
+ public BlockAmethystBudMedium() {
+ this(0);
+ }
+
+ public BlockAmethystBudMedium(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return MEDIUM_AMETHYST_BUD;
+ }
+
+ @Override
+ protected String getSizeName() {
+ return "Medium";
+ }
+
+ @Override
+ protected int getCrystalHeight() {
+ return 4;
+ }
+
+ @Override
+ protected int getCrystalOffset() {
+ return 3;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 2;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAmethystBudSmall.java b/src/main/java/cn/nukkit/block/BlockAmethystBudSmall.java
new file mode 100644
index 00000000000..434e4d9a8e3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAmethystBudSmall.java
@@ -0,0 +1,37 @@
+package cn.nukkit.block;
+
+public class BlockAmethystBudSmall extends BlockAmethystBud {
+
+ public BlockAmethystBudSmall() {
+ this(0);
+ }
+
+ public BlockAmethystBudSmall(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return SMALL_AMETHYST_BUD;
+ }
+
+ @Override
+ protected String getSizeName() {
+ return "Small";
+ }
+
+ @Override
+ protected int getCrystalHeight() {
+ return 3;
+ }
+
+ @Override
+ protected int getCrystalOffset() {
+ return 4;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 1;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAmethystCluster.java b/src/main/java/cn/nukkit/block/BlockAmethystCluster.java
new file mode 100644
index 00000000000..6a0c27a1757
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAmethystCluster.java
@@ -0,0 +1,80 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.math.NukkitMath;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockAmethystCluster extends BlockAmethystBud {
+
+ public BlockAmethystCluster() {
+ this(0);
+ }
+
+ public BlockAmethystCluster(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return AMETHYST_CLUSTER;
+ }
+
+ @Override
+ protected int getCrystalHeight() {
+ return 7;
+ }
+
+ @Override
+ protected int getCrystalOffset() {
+ return 7;
+ }
+
+ @Override
+ protected String getSizeName() {
+ return "Cluster";
+ }
+
+ @Override
+ public String getName() {
+ return "Amethyst Cluster";
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 5;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (!item.isPickaxe()) {
+ return new Item[]{Item.get(ItemID.AMETHYST_SHARD, 0, 2)};
+ }
+
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
+ int amount = 4;
+ int fortuneLevel = NukkitMath.clamp(item.getEnchantmentLevel(Enchantment.ID_FORTUNE_DIGGING), 0, 3);
+ if (fortuneLevel > 0) {
+ int rand = ThreadLocalRandom.current().nextInt(100);
+ if (fortuneLevel == 1 && rand <= 33) {
+ amount = 8;
+ } else if (fortuneLevel == 2 && rand <= 25) {
+ amount = rand <= 12 ? 8 : 12;
+ } else if (fortuneLevel == 3 && rand <= 20) {
+ if (rand <= 6) {
+ amount = 8;
+ } else if (rand <= 13) {
+ amount = 12;
+ } else {
+ amount = 16;
+ }
+ }
+ }
+ return new Item[]{Item.get(ItemID.AMETHYST_SHARD, 0, amount)};
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAncientDebris.java b/src/main/java/cn/nukkit/block/BlockAncientDebris.java
new file mode 100644
index 00000000000..990e3ee1bd6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAncientDebris.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockAncientDebris extends BlockSolid {
+
+ public BlockAncientDebris() {
+ super();
+ }
+
+ @Override
+ public int getId() {
+ return ANCIENT_DEBRIS;
+ }
+
+ @Override
+ public String getName() {
+ return "Ancient Debris";
+ }
+
+ @Override
+ public double getResistance() {
+ return 1200;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 30;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAnvil.java b/src/main/java/cn/nukkit/block/BlockAnvil.java
index 6694f517c13..457cbf70241 100644
--- a/src/main/java/cn/nukkit/block/BlockAnvil.java
+++ b/src/main/java/cn/nukkit/block/BlockAnvil.java
@@ -6,15 +6,18 @@
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.Faceable;
/**
* Created by Pub4Game on 27.12.2015.
*/
-public class BlockAnvil extends BlockFallable implements Faceable {
+public class BlockAnvil extends BlockFallableMeta implements Faceable {
- private static final String[] NAMES = new String[]{
+ private static final int[] faces = {1, 2, 3, 0};
+
+ private static final String[] NAMES = {
"Anvil",
"Anvil",
"Anvil",
@@ -29,29 +32,17 @@ public class BlockAnvil extends BlockFallable implements Faceable {
"Very Damaged Anvil"
};
- private int meta;
-
public BlockAnvil() {
this(0);
}
public BlockAnvil(int meta) {
- this.meta = meta;
+ super(meta);
}
@Override
public int getFullId() {
- return (getId() << 4) + getDamage();
- }
-
- @Override
- public final int getDamage() {
- return this.meta;
- }
-
- @Override
- public final void setDamage(int meta) {
- this.meta = meta;
+ return (getId() << DATA_BITS) + getDamage();
}
@Override
@@ -91,19 +82,16 @@ public String getName() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (!target.isTransparent() || target.getId() == Block.SNOW_LAYER) {
- int damage = this.getDamage();
- int[] faces = {1, 2, 3, 0};
- this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
- if (damage >= 4 && damage <= 7) {
- this.setDamage(this.getDamage() | 0x04);
- } else if (damage >= 8 && damage <= 11) {
- this.setDamage(this.getDamage() | 0x08);
- }
- this.getLevel().setBlock(block, this, true);
- return true;
+ int damage = this.getDamage();
+ this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ if (damage >= 4 && damage <= 7) {
+ this.setDamage(this.getDamage() | 0x04);
+ } else if (damage >= 8 && damage <= 11) {
+ this.setDamage(this.getDamage() | 0x08);
}
- return false;
+ this.getLevel().setBlock(this, this, true, true);
+ this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_ANVIL_FALL);
+ return true;
}
@Override
@@ -128,7 +116,7 @@ public Item toItem() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
this.toItem()
};
@@ -175,4 +163,9 @@ public double getMaxZ() {
public boolean isSolid() {
return false;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockAzalea.java b/src/main/java/cn/nukkit/block/BlockAzalea.java
new file mode 100644
index 00000000000..396e7c5f88d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAzalea.java
@@ -0,0 +1,100 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Position;
+import cn.nukkit.math.AxisAlignedBB;
+
+public class BlockAzalea extends BlockTransparent {
+
+ public BlockAzalea() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Azalea";
+ }
+
+ @Override
+ public int getId() {
+ return AZALEA;
+ }
+
+ @Override
+ public boolean canPlaceOn(Block floor, Position pos) {
+ // Azaleas can be placed on grass blocks, dirt, coarse dirt, rooted dirt, podzol, moss blocks, farmland, mud, muddy mangrove roots and clay.
+ switch (floor.getId()) {
+ case GRASS:
+ case DIRT:
+ case ROOTED_DIRT:
+ case PODZOL:
+ case MOSS_BLOCK:
+ case FARMLAND:
+ case MUD:
+ case CLAY_BLOCK:
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_NONE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return true;
+ }
+
+ @Override
+ public boolean canBeClimbed() {
+ return true;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return true;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return null;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAzaleaFlowering.java b/src/main/java/cn/nukkit/block/BlockAzaleaFlowering.java
new file mode 100644
index 00000000000..abc85214985
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAzaleaFlowering.java
@@ -0,0 +1,17 @@
+package cn.nukkit.block;
+
+public class BlockAzaleaFlowering extends BlockAzalea {
+
+ public BlockAzaleaFlowering() {
+ }
+
+ @Override
+ public String getName() {
+ return "Flowering Azalea";
+ }
+
+ @Override
+ public int getId() {
+ return FLOWERING_AZALEA;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAzaleaLeaves.java b/src/main/java/cn/nukkit/block/BlockAzaleaLeaves.java
new file mode 100644
index 00000000000..6c7d7039cff
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAzaleaLeaves.java
@@ -0,0 +1,93 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.math.BlockFace;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockAzaleaLeaves extends BlockLeaves {
+
+ public BlockAzaleaLeaves() {
+ this(0);
+ }
+
+ public BlockAzaleaLeaves(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return AZALEA_LEAVES;
+ }
+
+ @Override
+ public String getName() {
+ return "Azalea Leaves";
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setPersistent(true);
+ return super.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(this, 0, 1);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isShears() || item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{ this.toItem() };
+ }
+
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ if (random.nextInt(20) != 0) {
+ return new Item[0];
+ }
+
+ int chance = random.nextInt(4);
+ if (chance == 0 || chance == 1) {
+ return new Item[]{ Item.get(random.nextBoolean() ? Item.AZALEA : Item.FLOWERING_AZALEA, 0, 1) };
+ } else if (chance == 2) {
+ return new Item[]{ Item.get(Item.SAPLING, random.nextInt(0, 6), 1) };
+ } else {
+ return new Item[]{ Item.get(Item.STICK, 0, random.nextInt(1, 2)) };
+ }
+ }
+
+ @Override
+ protected void setOnDecayDamage() {
+ this.setCheckDecay(false);
+ }
+
+ @Override
+ protected boolean canDropApple() {
+ return false;
+ }
+
+ @Override
+ public boolean isPersistent() {
+ return (this.getDamage() & 2) >>> 1 == 1;
+ }
+
+ @Override
+ public void setPersistent(boolean persistent) {
+ int value = (persistent ? 1 : 0) << 1;
+ this.setDamage(this.getDamage() & ~1 | (value & 2));
+ }
+
+ @Override
+ public boolean isCheckDecay() {
+ return (this.getDamage() & 1) == 1;
+ }
+
+ @Override
+ public void setCheckDecay(boolean updateBit) {
+ this.setDamage(this.getDamage() & ~1 | ((updateBit ? 1 : 0) & 1));
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockAzaleaLeavesFlowered.java b/src/main/java/cn/nukkit/block/BlockAzaleaLeavesFlowered.java
new file mode 100644
index 00000000000..b66f01f7a5d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAzaleaLeavesFlowered.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockAzaleaLeavesFlowered extends BlockAzaleaLeaves {
+
+ public BlockAzaleaLeavesFlowered() {
+ this(0);
+ }
+
+ public BlockAzaleaLeavesFlowered(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Flowering Azalea Leaves";
+ }
+
+ @Override
+ public int getId() {
+ return AZALEA_LEAVES_FLOWERED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBamboo.java b/src/main/java/cn/nukkit/block/BlockBamboo.java
new file mode 100644
index 00000000000..73ccadb7b22
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBamboo.java
@@ -0,0 +1,318 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.*;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.MathHelper;
+import cn.nukkit.network.protocol.AnimatePacket;
+import cn.nukkit.utils.BlockColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockBamboo extends BlockTransparentMeta {
+
+ public static final int LEAF_SIZE_NONE = 0;
+ public static final int LEAF_SIZE_SMALL = 1;
+ public static final int LEAF_SIZE_LARGE = 2;
+
+ public BlockBamboo() {
+ this(0);
+ }
+
+ public BlockBamboo(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return BAMBOO;
+ }
+
+ @Override
+ public String getName() {
+ return "Bamboo";
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (this.isSupportInvalid()) {
+ this.level.scheduleUpdate(this, 0);
+ }
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.level.useBreakOn(this, null, null, true);
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ Block up = this.up();
+ if (this.getAge() == 0 && up.getId() == AIR && level.isAnimalSpawningAllowedByTime()/*this.level.getFullLight(up) >= BlockCrops.MINIMUM_LIGHT_LEVEL*/ && ThreadLocalRandom.current().nextInt(3) == 0) {
+ this.grow(up);
+ }
+ return type;
+ }
+ return 0;
+ }
+
+ public boolean grow(Block up) {
+ BlockBamboo newState = (BlockBamboo) Block.get(Block.BAMBOO);
+ if (this.isThick()) {
+ newState.setThick(true);
+ newState.setLeafSize(LEAF_SIZE_LARGE);
+ } else {
+ newState.setLeafSize(LEAF_SIZE_SMALL);
+ }
+ BlockGrowEvent blockGrowEvent = new BlockGrowEvent(up, newState);
+ level.getServer().getPluginManager().callEvent(blockGrowEvent);
+ if (!blockGrowEvent.isCancelled()) {
+ Block newState1 = blockGrowEvent.getNewState();
+ newState1.x = x;
+ newState1.y = up.y;
+ newState1.z = z;
+ newState1.level = level;
+ newState1.place(this.toItem(), up, this, BlockFace.DOWN, 0.5, 0.5, 0.5, null);
+ return true;
+ }
+ return false;
+ }
+
+ private int countHeight() {
+ int count = 0;
+ Block opt;
+ Block down = this;
+ while ((opt = down.down()).getId() == BAMBOO) {
+ down = opt;
+ if (++count >= 16) {
+ break;
+ }
+ }
+ return count;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ Block down = this.down();
+ int downId = down.getId();
+ if (downId != BAMBOO && downId != BAMBOO_SAPLING) {
+ BlockBambooSapling sampling = (BlockBambooSapling) Block.get(Block.BAMBOO_SAPLING);
+ sampling.x = x;
+ sampling.y = y;
+ sampling.z = z;
+ sampling.level = level;
+ return sampling.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ boolean canGrow = true;
+
+ if (downId == BAMBOO_SAPLING) {
+ if (player != null) {
+ AnimatePacket animatePacket = new AnimatePacket();
+ animatePacket.action = AnimatePacket.Action.SWING_ARM;
+ animatePacket.eid = player.getId();
+ this.getLevel().addChunkPacket(player.getChunkX(), player.getChunkZ(), animatePacket);
+ }
+ this.setLeafSize(LEAF_SIZE_SMALL);
+ } if (down instanceof BlockBamboo) {
+ BlockBamboo bambooDown = (BlockBamboo) down;
+ canGrow = bambooDown.getAge() == 0;
+ boolean thick = bambooDown.isThick();
+ if (!thick) {
+ boolean setThick = true;
+ for (int i = 2; i <= 3; i++) {
+ if (this.getSide(BlockFace.DOWN, i).getId() != BAMBOO) {
+ setThick = false;
+ }
+ }
+ if (setThick) {
+ this.setThick(true);
+ this.setLeafSize(LEAF_SIZE_LARGE);
+ bambooDown.setLeafSize(LEAF_SIZE_SMALL);
+ bambooDown.setThick(true);
+ bambooDown.setAge(1);
+ this.level.setBlock(bambooDown, bambooDown, false, true);
+ while ((down = down.down()) instanceof BlockBamboo) {
+ bambooDown = (BlockBamboo) down;
+ bambooDown.setThick(true);
+ bambooDown.setLeafSize(LEAF_SIZE_NONE);
+ bambooDown.setAge(1);
+ this.level.setBlock(bambooDown, bambooDown, false, true);
+ }
+ } else {
+ this.setLeafSize(LEAF_SIZE_SMALL);
+ bambooDown.setAge(1);
+ this.level.setBlock(bambooDown, bambooDown, false, true);
+ }
+ } else {
+ this.setThick(true);
+ this.setLeafSize(LEAF_SIZE_LARGE);
+ this.setAge(0);
+ bambooDown.setLeafSize(LEAF_SIZE_LARGE);
+ bambooDown.setAge(1);
+ this.level.setBlock(bambooDown, bambooDown, false, true);
+ down = bambooDown.down();
+ if (down instanceof BlockBamboo) {
+ bambooDown = (BlockBamboo) down;
+ bambooDown.setLeafSize(LEAF_SIZE_SMALL);
+ bambooDown.setAge(1);
+ this.level.setBlock(bambooDown, bambooDown, false, true);
+ down = bambooDown.down();
+ if (down instanceof BlockBamboo) {
+ bambooDown = (BlockBamboo) down;
+ bambooDown.setLeafSize(LEAF_SIZE_NONE);
+ bambooDown.setAge(1);
+ this.level.setBlock(bambooDown, bambooDown, false, true);
+ }
+ }
+ }
+ } else if (this.isSupportInvalid()) {
+ return false;
+ }
+
+ int height = canGrow? this.countHeight() : 0;
+ if (!canGrow || height >= 15 || height >= 11 && ThreadLocalRandom.current().nextFloat() < 0.25F) {
+ this.setAge(1);
+ }
+
+ this.level.setBlock(this, this, false, true);
+ return true;
+ }
+
+ @Override
+ public boolean onBreak(Item item) {
+ Block down = this.down();
+ if (down instanceof BlockBamboo) {
+ BlockBamboo bambooDown = (BlockBamboo) down;
+ int height = bambooDown.countHeight();
+ if (height < 15 && (height < 11 || !(ThreadLocalRandom.current().nextFloat() < 0.25F))) {
+ bambooDown.setAge(0);
+ this.level.setBlock(bambooDown, bambooDown, false, true);
+ }
+ }
+ return super.onBreak(item);
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+
+ private boolean isSupportInvalid() {
+ int downId = this.down().getId();
+ return downId != BAMBOO && downId != DIRT && downId != GRASS && downId != SAND && downId != GRAVEL && downId != PODZOL && downId != BAMBOO_SAPLING;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.FOLIAGE_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 2;
+ }
+
+ public boolean isThick() {
+ return (this.getDamage() & 0x1) == 0x1;
+ }
+
+ public void setThick(boolean thick) {
+ this.setDamage(this.getDamage() & (15 ^ 0x1) | (thick? 0x1 : 0x0));
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ public int getLeafSize() {
+ return (this.getDamage() >> 1) & 0x3;
+ }
+
+ public void setLeafSize(int leafSize) {
+ leafSize = MathHelper.clamp(leafSize, LEAF_SIZE_NONE, LEAF_SIZE_LARGE) & 0b11;
+ this.setDamage(this.getDamage() & (15 ^ 0b110) | (leafSize << 1));
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() == ItemID.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ int top = (int) y;
+ int count = 1;
+
+ for (int i = 1; i <= 16; i++) {
+ int id = this.level.getBlockIdAt(this.getFloorX(), this.getFloorY() - i, this.getFloorZ());
+ if (id == BAMBOO) {
+ count++;
+ } else {
+ break;
+ }
+ }
+
+ for (int i = 1; i <= 16; i++) {
+ int id = this.level.getBlockIdAt(this.getFloorX(), this.getFloorY() + i, this.getFloorZ());
+ if (id == BAMBOO) {
+ top++;
+ count++;
+ } else {
+ break;
+ }
+ }
+
+ if (count >= 15) {
+ return false;
+ }
+
+ boolean success = false;
+
+ Block block = this.up(top - (int)y + 1);
+ if (block.getId() == BlockID.AIR) {
+ success = this.grow(block);
+ }
+
+ if (success) {
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ level.addParticle(new BoneMealParticle(this));
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ public int getAge() {
+ return (this.getDamage() & 0x8) >> 3;
+ }
+
+ public void setAge(int age) {
+ age = MathHelper.clamp(age, 0, 1) << 3;
+ this.setDamage(this.getDamage() & (15 ^ 0b1000) | age);
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBambooSapling.java b/src/main/java/cn/nukkit/block/BlockBambooSapling.java
new file mode 100644
index 00000000000..3b1800267d7
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBambooSapling.java
@@ -0,0 +1,172 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.MathHelper;
+import cn.nukkit.utils.BlockColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockBambooSapling extends BlockFlowable {
+
+ public BlockBambooSapling() {
+ this(0);
+ }
+
+ public BlockBambooSapling(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return BAMBOO_SAPLING;
+ }
+
+ @Override
+ public String getName() {
+ return "Bamboo Sapling";
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (isSupportInvalid()) {
+ level.useBreakOn(this, null, null, true);
+ } else {
+ Block up = up();
+ if (up.getId() == BAMBOO) {
+ BlockBamboo upperBamboo = (BlockBamboo) up;
+ BlockBamboo newState = (BlockBamboo) Block.get(Block.BAMBOO);
+ newState.setThick(upperBamboo.isThick());
+ level.setBlock(this, newState, true, true);
+ }
+ }
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ Block up = up();
+ if (getAge() == 0 && up.getId() == AIR && level.isAnimalSpawningAllowedByTime()/*level.getFullLight(up) >= BlockCrops.MINIMUM_LIGHT_LEVEL*/ && ThreadLocalRandom.current().nextInt(3) == 0) {
+ BlockBamboo newState = (BlockBamboo) Block.get(Block.BAMBOO);
+ newState.setLeafSize(BlockBamboo.LEAF_SIZE_SMALL);
+ BlockGrowEvent blockGrowEvent = new BlockGrowEvent(up, newState);
+ level.getServer().getPluginManager().callEvent(blockGrowEvent);
+ if (!blockGrowEvent.isCancelled()) {
+ Block newState1 = blockGrowEvent.getNewState();
+ newState1.y = up.y;
+ newState1.x = x;
+ newState1.z = z;
+ newState1.level = level;
+ newState1.place(toItem(), up, this, BlockFace.DOWN, 0.5, 0.5, 0.5, null);
+ }
+ }
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (isSupportInvalid()) {
+ return false;
+ }
+
+ if (this.getLevelBlock() instanceof BlockLiquid) {
+ return false;
+ }
+
+ this.level.setBlock(this, this, true, true);
+ return true;
+ }
+
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() == ItemID.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ boolean success = false;
+ Block block = this.up();
+ if (block.getId() == AIR) {
+ success = this.grow(block);
+ }
+
+ if (success) {
+ if (player != null && (player.gamemode & 0x01) == 0) {
+ item.count--;
+ }
+
+ this.level.addParticle(new BoneMealParticle(this));
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ public boolean grow(Block up) {
+ BlockBamboo bamboo = (BlockBamboo) Block.get(Block.BAMBOO);
+ bamboo.x = x;
+ bamboo.y = y;
+ bamboo.z = z;
+ bamboo.level = level;
+ return bamboo.grow(up);
+ }
+
+ private boolean isSupportInvalid() {
+ int downId = down().getId();
+ return downId != DIRT && downId != GRASS && downId != SAND && downId != GRAVEL && downId != PODZOL;
+ }
+
+ public int getAge() {
+ return getDamage() & 0x1;
+ }
+
+ public void setAge(int age) {
+ age = MathHelper.clamp(age, 0, 1) & 0x1;
+ setDamage(getDamage() & (15 ^ 0x1) | age);
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(BAMBOO), 0);
+ }
+
+ @Override
+ public double getMinX() {
+ return this.x + 0.125;
+ }
+
+ @Override
+ public double getMinZ() {
+ return this.z + 0.125;
+ }
+
+ @Override
+ public double getMaxX() {
+ return this.x + 0.875;
+ }
+
+ @Override
+ public double getMaxY() {
+ return this.y + 0.875;
+ }
+
+ @Override
+ public double getMaxZ() {
+ return this.z + 0.875;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.FOLIAGE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBanner.java b/src/main/java/cn/nukkit/block/BlockBanner.java
index 2c4e293b72d..d870e12fdce 100644
--- a/src/main/java/cn/nukkit/block/BlockBanner.java
+++ b/src/main/java/cn/nukkit/block/BlockBanner.java
@@ -17,9 +17,6 @@
import cn.nukkit.utils.DyeColor;
import cn.nukkit.utils.Faceable;
-/**
- * Created by PetteriM1
- */
public class BlockBanner extends BlockTransparentMeta implements Faceable {
public BlockBanner() {
@@ -56,7 +53,7 @@ public String getName() {
}
@Override
- protected AxisAlignedBB recalculateBoundingBox() {
+ public AxisAlignedBB getBoundingBox() {
return null;
}
@@ -71,29 +68,34 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
if (face == BlockFace.UP) {
this.setDamage(NukkitMath.floorDouble(((player.yaw + 180) * 16 / 360) + 0.5) & 0x0f);
this.getLevel().setBlock(block, this, true);
+ } else if (target.canBeReplaced()) {
+ this.setDamage(NukkitMath.floorDouble(((player.yaw + 180) * 16 / 360) + 0.5) & 0x0f);
+ this.getLevel().setBlock(target, this, true);
} else {
this.setDamage(face.getIndex());
- this.getLevel().setBlock(block, Block.get(BlockID.WALL_BANNER, this.getDamage()), true);
+ this.getLevel().setBlock(block, Block.get(WALL_BANNER, this.getDamage()), true);
}
CompoundTag nbt = BlockEntity.getDefaultCompound(this, BlockEntity.BANNER)
.putInt("Base", item.getDamage() & 0xf);
Tag type = item.getNamedTagEntry("Type");
+
if (type instanceof IntTag) {
nbt.put("Type", type);
}
+
Tag patterns = item.getNamedTagEntry("Patterns");
if (patterns instanceof ListTag) {
nbt.put("Patterns", patterns);
}
- BlockEntityBanner banner = (BlockEntityBanner) BlockEntity.createBlockEntity(BlockEntity.BANNER, this.getChunk(), nbt);
- return banner != null;
+ BlockEntity.createBlockEntity(BlockEntity.BANNER, this.getChunk(), nbt);
+ return true;
}
return false;
}
-
+
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
@@ -154,4 +156,14 @@ public DyeColor getDyeColor() {
public boolean isSolid() {
return false;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockBarrel.java b/src/main/java/cn/nukkit/block/BlockBarrel.java
new file mode 100644
index 00000000000..97bb9fe49f3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBarrel.java
@@ -0,0 +1,171 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityBarrel;
+import cn.nukkit.inventory.ContainerInventory;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+import cn.nukkit.nbt.tag.StringTag;
+import cn.nukkit.nbt.tag.Tag;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Faceable;
+
+import java.util.Map;
+
+public class BlockBarrel extends BlockSolidMeta implements Faceable {
+
+ public BlockBarrel() {
+ this(0);
+ }
+
+ public BlockBarrel(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Barrel";
+ }
+
+ @Override
+ public int getId() {
+ return BARREL;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) {
+ double y = player.y + player.getEyeHeight();
+ if (y - this.y > 2) {
+ this.setDamage(BlockFace.UP.getIndex());
+ } else if (this.y - y > 0) {
+ this.setDamage(BlockFace.DOWN.getIndex());
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+
+ this.level.setBlock(block, this, true, true);
+
+ CompoundTag nbt = new CompoundTag("")
+ .putList(new ListTag<>("Items"))
+ .putString("id", BlockEntity.BARREL)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ if (item.hasCustomName()) {
+ nbt.putString("CustomName", item.getCustomName());
+ }
+
+ if (item.hasCustomBlockData()) {
+ Map customData = item.getCustomBlockData().getTags();
+ for (Map.Entry tag : customData.entrySet()) {
+ nbt.put(tag.getKey(), tag.getValue());
+ }
+ }
+
+ BlockEntity.createBlockEntity(BlockEntity.BARREL, this.getChunk(), nbt);
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player == null) {
+ return false;
+ }
+
+ BlockEntity blockEntity = level.getBlockEntity(this);
+ if (!(blockEntity instanceof BlockEntityBarrel)) {
+ return false;
+ }
+
+ BlockEntityBarrel barrel = (BlockEntityBarrel) blockEntity;
+
+ if (barrel.namedTag.contains("Lock") && barrel.namedTag.get("Lock") instanceof StringTag) {
+ if (!barrel.namedTag.getString("Lock").equals(item.getCustomName())) {
+ return true;
+ }
+ }
+
+ player.addWindow(barrel.getInventory());
+
+ return true;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 12.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(BARREL));
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ int index = getDamage() & 0x7;
+ return BlockFace.fromIndex(index);
+ }
+
+ public void setBlockFace(BlockFace face) {
+ this.setDamage((this.getDamage() & 0x8) | (face.getIndex() & 0x7));
+ }
+
+ public boolean isOpen() {
+ return (this.getDamage() & 0x8) == 0x8;
+ }
+
+ public void setOpen(boolean open) {
+ this.setDamage((this.getDamage() & 0x7) | (open? 0x8 : 0x0));
+ }
+
+ @Override
+ public boolean hasComparatorInputOverride() {
+ return true;
+ }
+
+ @Override
+ public int getComparatorInputOverride() {
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (blockEntity instanceof BlockEntityBarrel) {
+ return ContainerInventory.calculateRedstone(((BlockEntityBarrel) blockEntity).getInventory());
+ }
+
+ return super.getComparatorInputOverride();
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBarrier.java b/src/main/java/cn/nukkit/block/BlockBarrier.java
new file mode 100644
index 00000000000..5d73f67917b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBarrier.java
@@ -0,0 +1,57 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockBarrier extends BlockTransparent {
+
+ @Override
+ public String getName() {
+ return "Barrier";
+ }
+
+ @Override
+ public int getId() {
+ return BARRIER;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.TRANSPARENT_BLOCK_COLOR;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBasalt.java b/src/main/java/cn/nukkit/block/BlockBasalt.java
new file mode 100644
index 00000000000..f8a03e5475e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBasalt.java
@@ -0,0 +1,97 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockBasalt extends BlockSolidMeta {
+
+ public BlockBasalt() {
+ this(0);
+ }
+
+ public BlockBasalt(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.25;
+ }
+
+ @Override
+ public double getResistance() {
+ return 4.2;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setPillarAxis(face.getAxis());
+ this.getLevel().setBlock(block, this, true, true);
+ return true;
+ }
+
+ public void setPillarAxis(BlockFace.Axis axis) {
+ switch (axis) {
+ case Y:
+ this.setDamage(0);
+ break;
+ case X:
+ this.setDamage(1);
+ break;
+ case Z:
+ this.setDamage(2);
+ break;
+ }
+ }
+
+ public BlockFace.Axis getPillarAxis() {
+ switch (this.getDamage() % 3) {
+ case 2:
+ return BlockFace.Axis.Z;
+ case 1:
+ return BlockFace.Axis.X;
+ case 0:
+ default:
+ return BlockFace.Axis.Y;
+ }
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public String getName() {
+ return "Basalt";
+ }
+
+ @Override
+ public int getId() {
+ return BlockID.BASALT;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{
+ toItem()
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GRAY_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBasaltSmooth.java b/src/main/java/cn/nukkit/block/BlockBasaltSmooth.java
new file mode 100644
index 00000000000..6e1394b16fe
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBasaltSmooth.java
@@ -0,0 +1,60 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockBasaltSmooth extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Smooth Basalt";
+ }
+
+ @Override
+ public int getId() {
+ return SMOOTH_BASALT;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.25;
+ }
+
+ @Override
+ public double getResistance() {
+ return 4.2;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{
+ toItem()
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GRAY_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBeacon.java b/src/main/java/cn/nukkit/block/BlockBeacon.java
index 8a52c099ef9..6bba6e6309b 100644
--- a/src/main/java/cn/nukkit/block/BlockBeacon.java
+++ b/src/main/java/cn/nukkit/block/BlockBeacon.java
@@ -11,13 +11,10 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: Angelic47 Nukkit Project
+ * @author Angelic47 Nukkit Project
*/
public class BlockBeacon extends BlockTransparent {
- public BlockBeacon() {
- }
-
@Override
public int getId() {
return BEACON;
@@ -57,19 +54,8 @@ public boolean canBeActivated() {
public boolean onActivate(Item item, Player player) {
if (player != null) {
BlockEntity t = this.getLevel().getBlockEntity(this);
- BlockEntityBeacon beacon;
- if (t instanceof BlockEntityBeacon) {
- beacon = (BlockEntityBeacon) t;
- } else {
- CompoundTag nbt = new CompoundTag("")
- .putString("id", BlockEntity.BEACON)
- .putInt("x", (int) this.x)
- .putInt("y", (int) this.y)
- .putInt("z", (int) this.z);
- beacon = (BlockEntityBeacon) BlockEntity.createBlockEntity(BlockEntity.BEACON, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
- if (beacon == null) {
- return false;
- }
+ if (!(t instanceof BlockEntityBeacon)) {
+ return false;
}
player.addWindow(new BeaconInventory(player.getUIInventory(), this), Player.BEACON_WINDOW_ID);
@@ -79,21 +65,18 @@ public boolean onActivate(Item item, Player player) {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- boolean blockSuccess = super.place(item, block, target, face, fx, fy, fz, player);
-
- if (blockSuccess) {
+ if (this.getLevel().setBlock(this, this, true, true)) {
CompoundTag nbt = new CompoundTag("")
.putString("id", BlockEntity.BEACON)
.putInt("x", (int) this.x)
.putInt("y", (int) this.y)
.putInt("z", (int) this.z);
- BlockEntityBeacon beacon = (BlockEntityBeacon) BlockEntity.createBlockEntity(BlockEntity.BEACON, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
- if (beacon == null) {
- return false;
- }
+ BlockEntity.createBlockEntity(BlockEntity.BEACON, this.getChunk(), nbt);
+
+ return true;
}
- return blockSuccess;
+ return false;
}
@Override
@@ -105,4 +88,19 @@ public boolean canBePushed() {
public BlockColor getColor() {
return BlockColor.DIAMOND_BLOCK_COLOR;
}
-}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean alwaysDropsOnExplosion() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockBed.java b/src/main/java/cn/nukkit/block/BlockBed.java
index 482a7bd45c8..213641230e5 100644
--- a/src/main/java/cn/nukkit/block/BlockBed.java
+++ b/src/main/java/cn/nukkit/block/BlockBed.java
@@ -3,20 +3,27 @@
import cn.nukkit.Player;
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityBed;
-import cn.nukkit.entity.item.EntityPrimedTNT;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.mob.*;
+import cn.nukkit.event.player.PlayerBedEnterEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBed;
-import cn.nukkit.lang.TranslationContainer;
+import cn.nukkit.level.Explosion;
+import cn.nukkit.level.GameRule;
import cn.nukkit.level.Level;
+import cn.nukkit.level.particle.DestroyBlockParticle;
+import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.SimpleAxisAlignedBB;
import cn.nukkit.math.Vector3;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.DyeColor;
import cn.nukkit.utils.Faceable;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockBed extends BlockTransparentMeta implements Faceable {
@@ -39,11 +46,6 @@ public boolean canBeActivated() {
return true;
}
- @Override
- public double getResistance() {
- return 1;
- }
-
@Override
public double getHardness() {
return 0.2;
@@ -59,26 +61,23 @@ public double getMaxY() {
return this.y + 0.5625;
}
- @Override
- public boolean onActivate(Item item) {
- return this.onActivate(item, null);
- }
+ /**
+ * List of mob network IDs which make players unable to sleep when nearby the bed.
+ */
+ private static final IntSet MOB_IDS = new IntOpenHashSet(new int[]{EntityBlaze.NETWORK_ID, EntityCaveSpider.NETWORK_ID, EntityCreeper.NETWORK_ID, EntityDrowned.NETWORK_ID, EntityElderGuardian.NETWORK_ID, EntityEnderman.NETWORK_ID, EntityEndermite.NETWORK_ID, EntityEvoker.NETWORK_ID, EntityGhast.NETWORK_ID, EntityGuardian.NETWORK_ID, EntityHoglin.NETWORK_ID, EntityHusk.NETWORK_ID, EntityPiglinBrute.NETWORK_ID, EntityPillager.NETWORK_ID, EntityRavager.NETWORK_ID, EntityShulker.NETWORK_ID, EntitySilverfish.NETWORK_ID, EntitySkeleton.NETWORK_ID, EntitySlime.NETWORK_ID, EntitySpider.NETWORK_ID, EntityStray.NETWORK_ID, EntityVex.NETWORK_ID, EntityVindicator.NETWORK_ID, EntityWitch.NETWORK_ID, EntityWither.NETWORK_ID, EntityWitherSkeleton.NETWORK_ID, EntityZoglin.NETWORK_ID, EntityZombie.NETWORK_ID, EntityZombiePigman.NETWORK_ID, EntityZombieVillagerV1.NETWORK_ID, EntityZombieVillager.NETWORK_ID});
@Override
public boolean onActivate(Item item, Player player) {
+ if (this.level.getDimension() != Level.DIMENSION_OVERWORLD) {
+ if (this.level.getGameRules().getBoolean(GameRule.RESPAWN_BLOCKS_EXPLODE)) {
+ if (this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, true)) {
+ this.level.addParticle(new DestroyBlockParticle(this.add(0.5, 0.5, 0.5), this));
+ }
- if (this.level.getDimension() == Level.DIMENSION_NETHER || this.level.getDimension() == Level.DIMENSION_THE_END) {
- CompoundTag tag = EntityPrimedTNT.getDefaultNBT(this).putShort("Fuse", 0);
- new EntityPrimedTNT(this.level.getChunk(this.getFloorX() >> 4, this.getFloorZ() >> 4), tag);
- return true;
- }
-
- int time = this.getLevel().getTime() % Level.TIME_FULL;
-
- boolean isNight = (time >= Level.TIME_NIGHT && time < Level.TIME_SUNRISE);
-
- if (player != null && !isNight) {
- player.sendMessage(new TranslationContainer("tile.bed.noSleep"));
+ Explosion explosion = new Explosion(this.add(0.5, 0, 0.5), 5, this);
+ explosion.explodeA();
+ explosion.explodeB();
+ }
return true;
}
@@ -91,43 +90,72 @@ public boolean onActivate(Item item, Player player) {
if ((this.getDamage() & 0x08) == 0x08) {
b = this;
} else {
- if (blockNorth.getId() == this.getId() && (blockNorth.getDamage() & 0x08) == 0x08) {
+ if (blockNorth.getId() == BED_BLOCK && (blockNorth.getDamage() & 0x08) == 0x08) {
b = blockNorth;
- } else if (blockSouth.getId() == this.getId() && (blockSouth.getDamage() & 0x08) == 0x08) {
+ } else if (blockSouth.getId() == BED_BLOCK && (blockSouth.getDamage() & 0x08) == 0x08) {
b = blockSouth;
- } else if (blockEast.getId() == this.getId() && (blockEast.getDamage() & 0x08) == 0x08) {
+ } else if (blockEast.getId() == BED_BLOCK && (blockEast.getDamage() & 0x08) == 0x08) {
b = blockEast;
- } else if (blockWest.getId() == this.getId() && (blockWest.getDamage() & 0x08) == 0x08) {
+ } else if (blockWest.getId() == BED_BLOCK && (blockWest.getDamage() & 0x08) == 0x08) {
b = blockWest;
} else {
if (player != null) {
- player.sendMessage(new TranslationContainer("tile.bed.notValid"));
+ player.sendMessage("§7%tile.bed.notValid", true);
}
return true;
}
}
- if (player != null && !player.sleepOn(b)) {
- player.sendMessage(new TranslationContainer("tile.bed.occupied"));
- }
+ if (player != null) {
+ if (player.distanceSquared(this) > 36) {
+ player.sendMessage("§7%tile.bed.tooFar", true);
+ return true;
+ }
+
+ if (!player.isCreative()) {
+ BlockFace secondPart = this.getBlockFace().getOpposite();
+ AxisAlignedBB checkArea = new SimpleAxisAlignedBB(b.x - 8, b.y - 6.5, b.z - 8, b.x + 9, b.y + 5.5, b.z + 9).addCoord(secondPart.getXOffset(), 0, secondPart.getZOffset());
+ for (Entity entity : this.getLevel().getCollidingEntities(checkArea)) {
+ if (!entity.isClosed() && MOB_IDS.contains(entity.getNetworkId())) {
+ player.sendMessage("§7%tile.bed.notSafe", true);
+ return true;
+ }
+ }
+ }
+
+ int time = this.getLevel().getTime() % Level.TIME_FULL;
+ boolean isNight = time >= Level.TIME_NIGHT && time < Level.TIME_SUNRISE;
+ if (!isNight && !this.getLevel().isThundering()) {
+ if (!b.equals(player.getSpawnPosition())) {
+ PlayerBedEnterEvent ev = new PlayerBedEnterEvent(player, this, true); // TODO: Event for setting player respawn point?
+ player.getServer().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ player.setSpawn(b);
+ player.sendMessage("§7%tile.bed.respawnSet", true);
+ }
+ }
+ player.sendMessage("§7%tile.bed.noSleep", true);
+ } else if (!player.sleepOn(b)) {
+ player.sendMessage("§7%tile.bed.occupied", true);
+ }
+ }
return true;
}
@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.isTransparent()) {
- Block next = this.getSide(player.getDirection());
- Block downNext = next.down();
+ if (canStayOnFullNonSolid(this.down())) {
+ Block next = this.getSide(player.getHorizontalFacing());
- if (next.canBeReplaced() && !downNext.isTransparent()) {
+ if (next.canBeReplaced() && canStayOnFullNonSolid(next.down())) {
int meta = player.getDirection().getHorizontalIndex();
- this.getLevel().setBlock(block, Block.get(this.getId(), meta), true, true);
- this.getLevel().setBlock(next, Block.get(this.getId(), meta | 0x08), true, true);
+ this.getLevel().setBlock(block, Block.get(BED_BLOCK, meta), true, true);
+
+ this.getLevel().setBlock(next, Block.get(BED_BLOCK, meta | 0x08), true, true);
createBlockEntity(this, item.getDamage());
createBlockEntity(next, item.getDamage());
@@ -138,50 +166,84 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
return false;
}
+ /**
+ * Internal: Can drop item when broken
+ */
+ public boolean canDropItem = true;
+
@Override
public boolean onBreak(Item item) {
- Block blockNorth = this.north(); //Gets the blocks around them
+ Block blockNorth = this.north();
Block blockSouth = this.south();
Block blockEast = this.east();
Block blockWest = this.west();
- if ((this.getDamage() & 0x08) == 0x08) { //This is the Top part of bed
- if (blockNorth.getId() == BED_BLOCK && (blockNorth.getDamage() & 0x08) != 0x08) { //Checks if the block ID&&meta are right
- this.getLevel().setBlock(blockNorth, Block.get(BlockID.AIR), true, true);
+ Block secondPart = null;
+ if ((this.getDamage() & 0x08) == 0x08) { // Top part of the bed
+ if (blockNorth.getId() == BED_BLOCK && (blockNorth.getDamage() & 0x08) != 0x08) { // Check if the block ID & meta are right
+ secondPart = blockNorth;
} else if (blockSouth.getId() == BED_BLOCK && (blockSouth.getDamage() & 0x08) != 0x08) {
- this.getLevel().setBlock(blockSouth, Block.get(BlockID.AIR), true, true);
+ secondPart = blockSouth;
} else if (blockEast.getId() == BED_BLOCK && (blockEast.getDamage() & 0x08) != 0x08) {
- this.getLevel().setBlock(blockEast, Block.get(BlockID.AIR), true, true);
+ secondPart = blockEast;
} else if (blockWest.getId() == BED_BLOCK && (blockWest.getDamage() & 0x08) != 0x08) {
- this.getLevel().setBlock(blockWest, Block.get(BlockID.AIR), true, true);
+ secondPart = blockWest;
+ }
+ } else { // Bottom part of the bed
+ if (blockNorth.getId() == BED_BLOCK && (blockNorth.getDamage() & 0x08) == 0x08) {
+ secondPart = blockNorth;
+ } else if (blockSouth.getId() == BED_BLOCK && (blockSouth.getDamage() & 0x08) == 0x08) {
+ secondPart = blockSouth;
+ } else if (blockEast.getId() == BED_BLOCK && (blockEast.getDamage() & 0x08) == 0x08) {
+ secondPart = blockEast;
+ } else if (blockWest.getId() == BED_BLOCK && (blockWest.getDamage() & 0x08) == 0x08) {
+ secondPart = blockWest;
}
- } else { //Bottom Part of Bed
- if (blockNorth.getId() == this.getId() && (blockNorth.getDamage() & 0x08) == 0x08) {
- this.getLevel().setBlock(blockNorth, Block.get(BlockID.AIR), true, true);
- } else if (blockSouth.getId() == this.getId() && (blockSouth.getDamage() & 0x08) == 0x08) {
- this.getLevel().setBlock(blockSouth, Block.get(BlockID.AIR), true, true);
- } else if (blockEast.getId() == this.getId() && (blockEast.getDamage() & 0x08) == 0x08) {
- this.getLevel().setBlock(blockEast, Block.get(BlockID.AIR), true, true);
- } else if (blockWest.getId() == this.getId() && (blockWest.getDamage() & 0x08) == 0x08) {
- this.getLevel().setBlock(blockWest, Block.get(BlockID.AIR), true, true);
+ }
+
+ if (secondPart != null) {
+ Item secondPartDrop = (secondPart.getDamage() & 0x08) == 0x08 ? secondPart.toItem() : null; // Get drops before block entity is destroyed to keep the color
+ if (this.getLevel().setBlock(secondPart, Block.get(BlockID.AIR), true, true)) {
+ if (secondPartDrop != null && this.canDropItem && this.getLevel().gameRules.getBoolean(GameRule.DO_TILE_DROPS)) {
+ this.getLevel().dropItem(this.add(0.5, 0.5, 0.5), secondPartDrop); // Drops only from the top part, prevent a dupe
+ }
}
}
- this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, false); // Do not update both parts to prevent duplication bug if there is two fallable blocks top of the bed
+ this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, secondPart == null); // Don't update both parts to prevent duplication bug if there are two fallable blocks on top of the bed
+
+ for (Entity entity : this.level.getNearbyEntities(new SimpleAxisAlignedBB(this, this).grow(2, 1, 2))) {
+ if (!(entity instanceof Player)) continue;
+ Player player = (Player) entity;
+
+ if (player.getSleepingPos() == null) continue;
+ if (!player.getSleepingPos().equals(this) && !player.getSleepingPos().equals(secondPart)) continue;
+ player.stopSleep();
+ }
+
+ if (level.getDimension() == Level.DIMENSION_OVERWORLD) {
+ Vector3 safeSpawn = null;
+ for (Player player : level.getServer().getOnlinePlayers().values()) {
+ if (player.getSpawnPosition() != null && (player.getSpawnPosition().equals(this) || player.getSpawnPosition().equals(secondPart))) {
+ player.setSpawn(safeSpawn == null ? (safeSpawn = level.getServer().getDefaultLevel().getSafeSpawn()) : safeSpawn);
+ }
+ }
+ }
return true;
}
- private void createBlockEntity(Vector3 pos, int color) {
+ private void createBlockEntity(Block pos, int color) {
CompoundTag nbt = BlockEntity.getDefaultCompound(pos, BlockEntity.BED);
nbt.putByte("color", color);
- BlockEntity.createBlockEntity(BlockEntity.BED, this.level.getChunk(pos.getFloorX() >> 4, pos.getFloorZ() >> 4), nbt);
+ BlockEntityBed be = (BlockEntityBed) BlockEntity.createBlockEntity(BlockEntity.BED, pos.getChunk(), nbt);
+ be.spawnToAll();
}
@Override
public Item toItem() {
- return new ItemBed(this.getDyeColor().getWoolData());
+ return Item.get(Item.BED, this.getDyeColor().getWoolData());
}
@Override
@@ -205,4 +267,14 @@ public DyeColor getDyeColor() {
public BlockFace getBlockFace() {
return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
+
+ /*@Override
+ public boolean breakWhenPushed() {
+ return true;
+ }*/
+
+ @Override
+ public boolean canBePushed() {
+ return false; // Temporary dupe patch
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockBedrock.java b/src/main/java/cn/nukkit/block/BlockBedrock.java
index 73e75f6ab2e..bde9f7bf001 100644
--- a/src/main/java/cn/nukkit/block/BlockBedrock.java
+++ b/src/main/java/cn/nukkit/block/BlockBedrock.java
@@ -3,14 +3,11 @@
import cn.nukkit.item.Item;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockBedrock extends BlockSolid {
- public BlockBedrock() {
- }
-
@Override
public int getId() {
return BEDROCK;
diff --git a/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java b/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java
index 3f586ca815f..a85caf29b6f 100644
--- a/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java
+++ b/src/main/java/cn/nukkit/block/BlockBedrockInvisible.java
@@ -1,7 +1,6 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBlock;
import cn.nukkit.utils.BlockColor;
/**
@@ -9,9 +8,6 @@
*/
public class BlockBedrockInvisible extends BlockSolid {
- public BlockBedrockInvisible() {
- }
-
@Override
public int getId() {
return INVISIBLE_BEDROCK;
@@ -49,6 +45,11 @@ public boolean canBePushed() {
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.AIR));
+ return Item.get(0);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockBeeNest.java b/src/main/java/cn/nukkit/block/BlockBeeNest.java
new file mode 100644
index 00000000000..a325df1e5e9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBeeNest.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockBeeNest extends BlockBeehive {
+
+ public BlockBeeNest() {
+ this(0);
+ }
+
+ public BlockBeeNest(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return BEE_NEST;
+ }
+
+ @Override
+ public String getName() {
+ return "Bee Nest";
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 30;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 60;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.3;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.YELLOW_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBeehive.java b/src/main/java/cn/nukkit/block/BlockBeehive.java
new file mode 100644
index 00000000000..9698080edcc
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBeehive.java
@@ -0,0 +1,77 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockBeehive extends BlockSolidMeta {
+
+ public BlockBeehive() {
+ this(0);
+ }
+
+ public BlockBeehive(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Beehive";
+ }
+
+ @Override
+ public int getId() {
+ return BEEHIVE;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 5;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 20;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.6;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.6;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ private static final short[] faces = {2, 3, 0, 1};
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBeetroot.java b/src/main/java/cn/nukkit/block/BlockBeetroot.java
index 155f69f58b0..90ab0f6363f 100644
--- a/src/main/java/cn/nukkit/block/BlockBeetroot.java
+++ b/src/main/java/cn/nukkit/block/BlockBeetroot.java
@@ -1,7 +1,7 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSeedsBeetroot;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/11/22 by xtypr.
@@ -28,7 +28,7 @@ public String getName() {
@Override
public Item toItem() {
- return new ItemSeedsBeetroot();
+ return Item.get(Item.BEETROOT_SEEDS);
}
@Override
@@ -36,7 +36,7 @@ public Item[] getDrops(Item item) {
if (this.getDamage() >= 0x07) {
return new Item[]{
Item.get(Item.BEETROOT, 0, 1),
- Item.get(Item.BEETROOT_SEEDS, 0, (int) (4d * Math.random()))
+ Item.get(Item.BEETROOT_SEEDS, 0, Utils.random.nextInt(0, 4))
};
} else {
return new Item[]{
diff --git a/src/main/java/cn/nukkit/block/BlockBell.java b/src/main/java/cn/nukkit/block/BlockBell.java
new file mode 100644
index 00000000000..83b5a302e99
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBell.java
@@ -0,0 +1,401 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityBell;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.item.EntityItem;
+import cn.nukkit.event.block.BellRingEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Position;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.SimpleAxisAlignedBB;
+import cn.nukkit.math.Vector3;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Faceable;
+
+public class BlockBell extends BlockTransparentMeta implements Faceable {
+
+ public static final int TYPE_ATTACHMENT_STANDING = 0;
+ public static final int TYPE_ATTACHMENT_HANGING = 1;
+ public static final int TYPE_ATTACHMENT_SIDE = 2;
+ public static final int TYPE_ATTACHMENT_MULTIPLE = 3;
+
+ public BlockBell() {
+ this(0);
+ }
+
+ public BlockBell(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Bell";
+ }
+
+ @Override
+ public int getId() {
+ return BELL;
+ }
+
+ private boolean isConnectedTo(BlockFace connectedFace, int attachmentType, BlockFace blockFace) {
+ BlockFace.Axis faceAxis = connectedFace.getAxis();
+ switch (attachmentType) {
+ case TYPE_ATTACHMENT_STANDING:
+ if (faceAxis == BlockFace.Axis.Y) {
+ return connectedFace == BlockFace.DOWN;
+ } else {
+ return blockFace.getAxis() != faceAxis;
+ }
+ case TYPE_ATTACHMENT_HANGING:
+ return connectedFace == BlockFace.UP;
+ case TYPE_ATTACHMENT_SIDE:
+ return connectedFace == blockFace.getOpposite();
+ case TYPE_ATTACHMENT_MULTIPLE:
+ return connectedFace == blockFace || connectedFace == blockFace.getOpposite();
+ }
+ return false;
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ int attachmentType = getAttachmentType();
+ BlockFace blockFace = getBlockFace();
+ boolean north = this.isConnectedTo(BlockFace.NORTH, attachmentType, blockFace);
+ boolean south = this.isConnectedTo(BlockFace.SOUTH, attachmentType, blockFace);
+ boolean west = this.isConnectedTo(BlockFace.WEST, attachmentType, blockFace);
+ boolean east = this.isConnectedTo(BlockFace.EAST, attachmentType, blockFace);
+ boolean up = this.isConnectedTo(BlockFace.UP, attachmentType, blockFace);
+ boolean down = this.isConnectedTo(BlockFace.DOWN, attachmentType, blockFace);
+
+ double n = north ? 0 : 0.25;
+ double s = south ? 1 : 0.75;
+ double w = west ? 0 : 0.25;
+ double e = east ? 1 : 0.75;
+ double d = down ? 0 : 0.25;
+ double u = up ? 1 : 0.75;
+
+ return new SimpleAxisAlignedBB(this.x + w, this.y + d, this.z + n, this.x + e, this.y + u, this.z + s);
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (entity instanceof EntityItem && entity.getMotion().lengthSquared() > 0.01) {
+ AxisAlignedBB boundingBox = entity.getBoundingBox();
+ AxisAlignedBB blockBoundingBox = this.getCollisionBoundingBox();
+ if (boundingBox.intersectsWith(blockBoundingBox)) {
+ Vector3 entityCenter = new Vector3(
+ (boundingBox.getMaxX() - boundingBox.getMinX()) / 2,
+ (boundingBox.getMaxY() - boundingBox.getMinY()) / 2,
+ (boundingBox.getMaxZ() - boundingBox.getMinZ()) / 2
+ );
+
+ Vector3 blockCenter = new Vector3(
+ (blockBoundingBox.getMaxX() - blockBoundingBox.getMinX()) / 2,
+ (blockBoundingBox.getMaxY() - blockBoundingBox.getMinY()) / 2,
+ (blockBoundingBox.getMaxZ() - blockBoundingBox.getMinZ()) / 2
+ );
+ Vector3 entityPos = entity.add(entityCenter);
+ Vector3 blockPos = this.add(
+ blockBoundingBox.getMinX() - x + blockCenter.x,
+ blockBoundingBox.getMinY() - y + blockCenter.y,
+ blockBoundingBox.getMinZ() - z + blockCenter.z
+ );
+
+ Vector3 entityVector = entityPos.subtract(blockPos);
+ entityVector = entityVector.normalize().multiply(0.4);
+ entityVector.y = Math.max(0.15, entityVector.y);
+ if (this.ring(entity, BellRingEvent.RingCause.DROPPED_ITEM)) {
+ entity.setMotion(entityVector);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateCollisionBoundingBox() {
+ return recalculateBoundingBox().expand(0.000001, 0.000001, 0.000001);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ return this.ring(player, player != null? BellRingEvent.RingCause.HUMAN_INTERACTION : BellRingEvent.RingCause.UNKNOWN);
+ }
+
+ public boolean ring(Entity causeEntity, BellRingEvent.RingCause cause) {
+ return this.ring(causeEntity, cause, null);
+ }
+
+ public boolean ring(Entity causeEntity, BellRingEvent.RingCause cause, BlockFace hitFace) {
+ BlockEntityBell bell = this.getOrCreateBlockEntity();
+ if (bell == null) {
+ return true;
+ }
+ boolean addException = true;
+ BlockFace blockFace = getBlockFace();
+ if (hitFace == null) {
+ if (causeEntity != null) {
+ if (causeEntity instanceof EntityItem) {
+ Position blockMid = this.add(0.5, 0.5, 0.5);
+ Vector3 vector = causeEntity.subtract(blockMid).normalize();
+ int x = vector.x < 0? -1 : vector.x > 0? 1 : 0;
+ int z = vector.z < 0? -1 : vector.z > 0? 1 : 0;
+ if (x != 0 && z != 0) {
+ if (Math.abs(vector.x) < Math.abs(vector.z)) {
+ x = 0;
+ } else {
+ z = 0;
+ }
+ }
+ hitFace = blockFace;
+ for (BlockFace face : BlockFace.values()) {
+ if (face.getXOffset() == x && face.getZOffset() == z) {
+ hitFace = face;
+ break;
+ }
+ }
+ } else {
+ hitFace = causeEntity.getDirection();
+ }
+ } else {
+ hitFace = blockFace;
+ }
+ }
+ switch (this.getAttachmentType()) {
+ case TYPE_ATTACHMENT_STANDING:
+ if (hitFace.getAxis() != blockFace.getAxis()) {
+ return false;
+ }
+ break;
+ case TYPE_ATTACHMENT_MULTIPLE:
+ if (hitFace.getAxis() == blockFace.getAxis()) {
+ return false;
+ }
+ break;
+ case TYPE_ATTACHMENT_SIDE:
+ if (hitFace.getAxis() == blockFace.getAxis()) {
+ addException = false;
+ }
+ break;
+ }
+
+ BellRingEvent event = new BellRingEvent(this, cause, causeEntity);
+ this.level.getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ bell.setDirection(hitFace.getOpposite().getHorizontalIndex());
+ bell.setTicks(0);
+ bell.setRinging(true);
+ if (addException && causeEntity instanceof Player) {
+ bell.spawnExceptions.add(causeEntity.getId());
+ }
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_BELL_HIT);
+ return true;
+ }
+
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean checkSupport() {
+ switch (this.getAttachmentType()) {
+ case TYPE_ATTACHMENT_STANDING:
+ if (this.checkSupport(this.down(), BlockFace.UP)) {
+ return true;
+ }
+ break;
+ case TYPE_ATTACHMENT_HANGING:
+ if (this.checkSupport(this.up(), BlockFace.DOWN)) {
+ return true;
+ }
+ break;
+ case TYPE_ATTACHMENT_MULTIPLE:
+ BlockFace blockFace = getBlockFace();
+ if (this.checkSupport(this.getSide(blockFace), blockFace.getOpposite()) &&
+ this.checkSupport(this.getSide(blockFace.getOpposite()), blockFace)) {
+ return true;
+ }
+ break;
+ case TYPE_ATTACHMENT_SIDE:
+ blockFace = getBlockFace();
+ if (this.checkSupport(this.getSide(blockFace.getOpposite()), blockFace)) {
+ return true;
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ private boolean checkSupport(Block support, BlockFace attachmentFace) {
+ if (!support.isTransparent()) {
+ return true;
+ }
+
+ if (attachmentFace == BlockFace.DOWN) {
+ switch (support.getId()) {
+ case HOPPER_BLOCK:
+ case IRON_BARS:
+ return true;
+ default:
+ return support instanceof BlockFence;
+ }
+ }
+
+ if (support instanceof BlockCauldron) {
+ return attachmentFace == BlockFace.UP;
+ }
+ return false;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!checkSupport()) {
+ this.level.useBreakOn(this);
+ }
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_REDSTONE) {
+ if (level.isBlockPowered(this)) {
+ if (!isToggled()) {
+ setToggled(true);
+ this.level.setBlock(this, this, true, true);
+ ring(null, BellRingEvent.RingCause.REDSTONE);
+ }
+ } else if (isToggled()) {
+ setToggled(false);
+ this.level.setBlock(this, this, true, true);
+ }
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (block.canBeReplaced() && block.getId() != AIR && !(block instanceof BlockLiquid)) {
+ face = BlockFace.UP;
+ }
+ switch (face) {
+ case UP:
+ this.setAttachmentType(TYPE_ATTACHMENT_STANDING);
+ this.setBlockFace(player.getDirection().getOpposite());
+ break;
+ case DOWN:
+ this.setAttachmentType(TYPE_ATTACHMENT_HANGING);
+ this.setBlockFace(player.getDirection().getOpposite());
+ break;
+ default:
+ this.setBlockFace(face);
+ if (this.checkSupport(block.getSide(face), face.getOpposite())) {
+ this.setAttachmentType(TYPE_ATTACHMENT_MULTIPLE);
+ } else {
+ this.setAttachmentType(TYPE_ATTACHMENT_SIDE);
+ }
+ }
+ if (!this.checkSupport()) {
+ return false;
+ }
+ this.getLevel().setBlock(this, this, true, true);
+ this.createBlockEntity();
+ return true;
+ }
+
+ private BlockEntityBell createBlockEntity() {
+ CompoundTag nbt = BlockEntity.getDefaultCompound(this, BlockEntity.BELL);
+ return (BlockEntityBell) BlockEntity.createBlockEntity(BlockEntity.BELL, this.getChunk(), nbt);
+ }
+
+ private BlockEntityBell getOrCreateBlockEntity() {
+ BlockEntity blockEntity = this.getLevel().getBlockEntity(this);
+ if (!(blockEntity instanceof BlockEntityBell)) {
+ blockEntity = this.createBlockEntity();
+ }
+ return (BlockEntityBell) blockEntity;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(getDamage() & 0b11);
+ }
+
+ public void setBlockFace(BlockFace face) {
+ if (face.getHorizontalIndex() == -1) {
+ return;
+ }
+ this.setDamage(this.getDamage() & (DATA_MASK ^ 0b11) | face.getHorizontalIndex());
+ }
+
+ public int getAttachmentType() {
+ return (this.getDamage() & 0b1100) >> 2 & 0b11;
+ }
+
+ public void setAttachmentType(int attachmentType) {
+ attachmentType = attachmentType & 0b11;
+ this.setDamage(getDamage() & (DATA_MASK ^ 0b1100) | (attachmentType << 2));
+ }
+
+ public boolean isToggled() {
+ return (this.getDamage() & 0b010000) == 0b010000;
+ }
+
+ public void setToggled(boolean toggled) {
+ this.setDamage(this.getDamage() & (DATA_MASK ^ 0b010000) | (toggled? 0b010000 : 0b000000));
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public double getHardness() {
+ return 5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GOLD_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockBirchSignStanding.java b/src/main/java/cn/nukkit/block/BlockBirchSignStanding.java
new file mode 100644
index 00000000000..c45b9f2652e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBirchSignStanding.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockBirchSignStanding extends BlockSignPost {
+
+ public BlockBirchSignStanding() {
+ this(0);
+ }
+
+ public BlockBirchSignStanding(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Birch Sign Post";
+ }
+
+ @Override
+ public int getId() {
+ return BIRCH_STANDING_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.BIRCH_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return BIRCH_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return BIRCH_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBirchWallSign.java b/src/main/java/cn/nukkit/block/BlockBirchWallSign.java
new file mode 100644
index 00000000000..8fd9e0c35dc
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBirchWallSign.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockBirchWallSign extends BlockWallSign {
+
+ public BlockBirchWallSign() {
+ this(0);
+ }
+
+ public BlockBirchWallSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Birch Wall Sign";
+ }
+
+ @Override
+ public int getId() {
+ return BIRCH_WALL_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.BIRCH_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return BIRCH_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return BIRCH_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlackstone.java b/src/main/java/cn/nukkit/block/BlockBlackstone.java
new file mode 100644
index 00000000000..ace24d44ab7
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlackstone.java
@@ -0,0 +1,57 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockBlackstone extends BlockSolid {
+
+ public BlockBlackstone() {
+ }
+
+ @Override
+ public int getId() {
+ return BLACKSTONE;
+ }
+
+ @Override
+ public String getName() {
+ return "Blackstone";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{
+ toItem()
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlackstoneGilded.java b/src/main/java/cn/nukkit/block/BlockBlackstoneGilded.java
new file mode 100644
index 00000000000..6a7590a6997
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlackstoneGilded.java
@@ -0,0 +1,87 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.utils.BlockColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockBlackstoneGilded extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return GILDED_BLACKSTONE;
+ }
+
+ @Override
+ public String getName() {
+ return "Gilded Blackstone";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (!item.isPickaxe()) {
+ return new Item[0];
+ }
+
+ int dropOdds;
+ int fortune = 0;
+ Enchantment enchantment = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING);
+ if (enchantment != null) {
+ fortune = enchantment.getLevel();
+ }
+
+ switch (fortune) {
+ case 0:
+ dropOdds = 10;
+ break;
+ case 1:
+ dropOdds = 7;
+ break;
+ case 2:
+ dropOdds = 4;
+ break;
+ default:
+ dropOdds = 1;
+ }
+
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ if (dropOdds > 1 && random.nextInt(dropOdds) != 0) {
+ return new Item[] { toItem() };
+ }
+
+ return new Item[] { Item.get(ItemID.GOLD_NUGGET, 0, random.nextInt(2, 6)) };
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockBlackstonePolished.java
new file mode 100644
index 00000000000..764187eae64
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlackstonePolished.java
@@ -0,0 +1,19 @@
+package cn.nukkit.block;
+
+public class BlockBlackstonePolished extends BlockBlackstone {
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlackstonePolishedChiseled.java b/src/main/java/cn/nukkit/block/BlockBlackstonePolishedChiseled.java
new file mode 100644
index 00000000000..c04d8014f10
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlackstonePolishedChiseled.java
@@ -0,0 +1,14 @@
+package cn.nukkit.block;
+
+public class BlockBlackstonePolishedChiseled extends BlockBlackstonePolished {
+
+ @Override
+ public int getId() {
+ return CHISELED_POLISHED_BLACKSTONE;
+ }
+
+ @Override
+ public String getName() {
+ return "Chiseled Polished Blackstone";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlackstoneWall.java b/src/main/java/cn/nukkit/block/BlockBlackstoneWall.java
new file mode 100644
index 00000000000..d4fd0797f2c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlackstoneWall.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockBlackstoneWall extends BlockWall {
+
+ public BlockBlackstoneWall() {
+ this(0);
+ }
+
+ public BlockBlackstoneWall(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Blackstone Wall";
+ }
+
+ @Override
+ public int getId() {
+ return BLACKSTONE_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlastFurnace.java b/src/main/java/cn/nukkit/block/BlockBlastFurnace.java
new file mode 100644
index 00000000000..3110b37b0e7
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlastFurnace.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockBlastFurnace extends BlockBlastFurnaceLit {
+
+ public BlockBlastFurnace() {
+ this(0);
+ }
+
+ public BlockBlastFurnace(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 0;
+ }
+
+ @Override
+ public String getName() {
+ return "Blast Furnace";
+ }
+
+ @Override
+ public int getId() {
+ return BLAST_FURNACE;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlastFurnaceLit.java b/src/main/java/cn/nukkit/block/BlockBlastFurnaceLit.java
new file mode 100644
index 00000000000..cf097f3d4b3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlastFurnaceLit.java
@@ -0,0 +1,87 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityBlastFurnace;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+import cn.nukkit.nbt.tag.StringTag;
+import cn.nukkit.nbt.tag.Tag;
+
+import java.util.Map;
+
+public class BlockBlastFurnaceLit extends BlockFurnaceBurning {
+
+ public BlockBlastFurnaceLit() {
+ this(0);
+ }
+
+ public BlockBlastFurnaceLit(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Lit Blast Furnace";
+ }
+
+ @Override
+ public int getId() {
+ return LIT_BLAST_FURNACE;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(BLAST_FURNACE));
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ this.getLevel().setBlock(block, this, true, true);
+ CompoundTag nbt = new CompoundTag()
+ .putList(new ListTag<>("Items"))
+ .putString("id", BlockEntity.BLAST_FURNACE)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ if (item.hasCustomName()) {
+ nbt.putString("CustomName", item.getCustomName());
+ }
+
+ if (item.hasCustomBlockData()) {
+ Map customData = item.getCustomBlockData().getTags();
+ for (Map.Entry tag : customData.entrySet()) {
+ nbt.put(tag.getKey(), tag.getValue());
+ }
+ }
+
+ BlockEntityBlastFurnace furnace = (BlockEntityBlastFurnace) BlockEntity.createBlockEntity(BlockEntity.BLAST_FURNACE, this.getChunk(), nbt);
+ return furnace != null;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player != null) {
+ BlockEntity t = this.getLevel().getBlockEntity(this);
+ if (!(t instanceof BlockEntityBlastFurnace)) {
+ return false;
+ }
+
+ BlockEntityBlastFurnace furnace = (BlockEntityBlastFurnace) t;
+ if (furnace.namedTag.contains("Lock") && furnace.namedTag.get("Lock") instanceof StringTag) {
+ if (!furnace.namedTag.getString("Lock").equals(item.getCustomName())) {
+ return true;
+ }
+ }
+
+ player.addWindow(furnace.getInventory());
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBlueIce.java b/src/main/java/cn/nukkit/block/BlockBlueIce.java
new file mode 100644
index 00000000000..41705f5864d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlueIce.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+public class BlockBlueIce extends BlockIcePacked {
+
+ @Override
+ public String getName() {
+ return "Blue Ice";
+ }
+
+ @Override
+ public int getId() {
+ return BLUE_ICE;
+ }
+
+ @Override
+ public double getFrictionFactor() {
+ return 0.989;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2.8;
+ }
+
+ @Override
+ public double getResistance() {
+ return 14;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 4;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBone.java b/src/main/java/cn/nukkit/block/BlockBone.java
index ae747a445b9..b8ee934f677 100644
--- a/src/main/java/cn/nukkit/block/BlockBone.java
+++ b/src/main/java/cn/nukkit/block/BlockBone.java
@@ -11,9 +11,9 @@
/**
* @author CreeperFace
*/
-public class BlockBone extends BlockSolidMeta implements Faceable {
+public class BlockBone extends BlockSolid implements Faceable {
- private static final int[] FACES = {
+ private static final int[] faces = {
0,
0,
0b1000,
@@ -22,14 +22,6 @@ public class BlockBone extends BlockSolidMeta implements Faceable {
0b0100
};
- public BlockBone() {
- this(0);
- }
-
- public BlockBone(int meta) {
- super(meta);
- }
-
@Override
public int getId() {
return BONE_BLOCK;
@@ -57,7 +49,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{new ItemBlock(this)};
}
@@ -71,7 +63,7 @@ public BlockFace getBlockFace() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- this.setDamage(((this.getDamage() & 0x3) | FACES[face.getIndex()]));
+ this.setDamage(((this.getDamage() & 0x3) | faces[face.getIndex()]));
this.getLevel().setBlock(block, this, true);
return true;
}
diff --git a/src/main/java/cn/nukkit/block/BlockBookshelf.java b/src/main/java/cn/nukkit/block/BlockBookshelf.java
index 9b931281d34..dba76c928e1 100644
--- a/src/main/java/cn/nukkit/block/BlockBookshelf.java
+++ b/src/main/java/cn/nukkit/block/BlockBookshelf.java
@@ -1,22 +1,13 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBook;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
/**
* @author Nukkit Project Team
*/
-public class BlockBookshelf extends BlockSolidMeta {
-
- public BlockBookshelf(int meta) {
- super(meta);
- }
-
- public BlockBookshelf() {
- this(0);
- }
+public class BlockBookshelf extends BlockSolid {
@Override
public String getName() {
@@ -56,7 +47,7 @@ public int getBurnAbility() {
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- new ItemBook(0, 3)
+ Item.get(Item.BOOK, 0, 3)
};
}
diff --git a/src/main/java/cn/nukkit/block/BlockBorder.java b/src/main/java/cn/nukkit/block/BlockBorder.java
new file mode 100644
index 00000000000..d866be7c309
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBorder.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockBorder extends BlockTransparent {
+
+ @Override
+ public int getId() {
+ return BORDER_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public String getName() {
+ return "Border";
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBrewingStand.java b/src/main/java/cn/nukkit/block/BlockBrewingStand.java
index b2d48d44645..73473046bfc 100644
--- a/src/main/java/cn/nukkit/block/BlockBrewingStand.java
+++ b/src/main/java/cn/nukkit/block/BlockBrewingStand.java
@@ -1,11 +1,11 @@
package cn.nukkit.block;
+
import cn.nukkit.Player;
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityBrewingStand;
import cn.nukkit.inventory.ContainerInventory;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBrewingStand;
import cn.nukkit.item.ItemTool;
import cn.nukkit.math.BlockFace;
import cn.nukkit.nbt.tag.CompoundTag;
@@ -63,53 +63,39 @@ public int getLightLevel() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (!block.down().isTransparent()) {
- getLevel().setBlock(block, this, true, true);
-
- CompoundTag nbt = new CompoundTag()
- .putList(new ListTag<>("Items"))
- .putString("id", BlockEntity.BREWING_STAND)
- .putInt("x", (int) this.x)
- .putInt("y", (int) this.y)
- .putInt("z", (int) this.z);
-
- if (item.hasCustomName()) {
- nbt.putString("CustomName", item.getCustomName());
- }
+ this.getLevel().setBlock(this, this, true, true);
- if (item.hasCustomBlockData()) {
- Map customData = item.getCustomBlockData().getTags();
- for (Map.Entry tag : customData.entrySet()) {
- nbt.put(tag.getKey(), tag.getValue());
- }
- }
+ CompoundTag nbt = new CompoundTag()
+ .putList(new ListTag<>("Items"))
+ .putString("id", BlockEntity.BREWING_STAND)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
- BlockEntityBrewingStand brewing = (BlockEntityBrewingStand) BlockEntity.createBlockEntity(BlockEntity.BREWING_STAND, getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- return brewing != null;
+ if (item.hasCustomName()) {
+ nbt.putString("CustomName", item.getCustomName());
}
- return false;
+
+ if (item.hasCustomBlockData()) {
+ Map customData = item.getCustomBlockData().getTags();
+ for (Map.Entry tag : customData.entrySet()) {
+ nbt.put(tag.getKey(), tag.getValue());
+ }
+ }
+
+ BlockEntityBrewingStand brewing = (BlockEntityBrewingStand) BlockEntity.createBlockEntity(BlockEntity.BREWING_STAND, this.getChunk(), nbt);
+ return brewing != null;
}
@Override
public boolean onActivate(Item item, Player player) {
if (player != null) {
BlockEntity t = getLevel().getBlockEntity(this);
- BlockEntityBrewingStand brewing;
- if (t instanceof BlockEntityBrewingStand) {
- brewing = (BlockEntityBrewingStand) t;
- } else {
- CompoundTag nbt = new CompoundTag()
- .putList(new ListTag<>("Items"))
- .putString("id", BlockEntity.BREWING_STAND)
- .putInt("x", (int) this.x)
- .putInt("y", (int) this.y)
- .putInt("z", (int) this.z);
- brewing = (BlockEntityBrewingStand) BlockEntity.createBlockEntity(BlockEntity.BREWING_STAND, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
- if (brewing == null) {
- return false;
- }
+ if (!(t instanceof BlockEntityBrewingStand)) {
+ return false;
}
+ BlockEntityBrewingStand brewing = (BlockEntityBrewingStand) t;
if (brewing.namedTag.contains("Lock") && brewing.namedTag.get("Lock") instanceof StringTag) {
if (!brewing.namedTag.getString("Lock").equals(item.getCustomName())) {
return false;
@@ -124,12 +110,12 @@ public boolean onActivate(Item item, Player player) {
@Override
public Item toItem() {
- return new ItemBrewingStand();
+ return Item.get(Item.BREWING_STAND);
}
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -193,4 +179,14 @@ public int getComparatorInputOverride() {
public boolean canHarvestWithHand() {
return false;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockBricks.java b/src/main/java/cn/nukkit/block/BlockBricks.java
index de1eb072dbd..fadb8d5526f 100644
--- a/src/main/java/cn/nukkit/block/BlockBricks.java
+++ b/src/main/java/cn/nukkit/block/BlockBricks.java
@@ -9,9 +9,6 @@
*/
public class BlockBricks extends BlockSolid {
- public BlockBricks() {
- }
-
@Override
public String getName() {
return "Bricks";
@@ -39,7 +36,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
Item.get(Item.BRICKS_BLOCK, 0, 1)
};
@@ -49,12 +46,12 @@ public Item[] getDrops(Item item) {
}
@Override
- public BlockColor getColor() {
- return BlockColor.RED_BLOCK_COLOR;
+ public boolean canHarvestWithHand() {
+ return false;
}
@Override
- public boolean canHarvestWithHand() {
- return false;
+ public BlockColor getColor() {
+ return BlockColor.RED_BLOCK_COLOR;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockBricksBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockBricksBlackstonePolished.java
new file mode 100644
index 00000000000..6672abea2fa
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBricksBlackstonePolished.java
@@ -0,0 +1,14 @@
+package cn.nukkit.block;
+
+public class BlockBricksBlackstonePolished extends BlockBlackstonePolished {
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_BRICKS;
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone Bricks";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockBricksBlackstonePolishedCracked.java b/src/main/java/cn/nukkit/block/BlockBricksBlackstonePolishedCracked.java
new file mode 100644
index 00000000000..aeef4f683d9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBricksBlackstonePolishedCracked.java
@@ -0,0 +1,14 @@
+package cn.nukkit.block;
+
+public class BlockBricksBlackstonePolishedCracked extends BlockBricksBlackstonePolished {
+
+ @Override
+ public int getId() {
+ return CRACKED_POLISHED_BLACKSTONE_BRICKS;
+ }
+
+ @Override
+ public String getName() {
+ return "Cracked Polished Blackstone Bricks";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBricksDeepslate.java b/src/main/java/cn/nukkit/block/BlockBricksDeepslate.java
new file mode 100644
index 00000000000..0400c136f45
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBricksDeepslate.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockBricksDeepslate extends BlockSolid {
+
+ public BlockBricksDeepslate() {
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_BRICKS;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Bricks";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBricksDeepslateCracked.java b/src/main/java/cn/nukkit/block/BlockBricksDeepslateCracked.java
new file mode 100644
index 00000000000..2fe2a6fc937
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBricksDeepslateCracked.java
@@ -0,0 +1,18 @@
+package cn.nukkit.block;
+
+public class BlockBricksDeepslateCracked extends BlockBricksDeepslate {
+
+ public BlockBricksDeepslateCracked() {
+ // Does nothing
+ }
+
+ @Override
+ public int getId() {
+ return CRACKED_DEEPSLATE_BRICKS;
+ }
+
+ @Override
+ public String getName() {
+ return "Cracked Deepslate Bricks";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBricksEndStone.java b/src/main/java/cn/nukkit/block/BlockBricksEndStone.java
index 09684d99c3c..abc6095e4ec 100644
--- a/src/main/java/cn/nukkit/block/BlockBricksEndStone.java
+++ b/src/main/java/cn/nukkit/block/BlockBricksEndStone.java
@@ -6,9 +6,6 @@
public class BlockBricksEndStone extends BlockSolid {
- public BlockBricksEndStone() {
- }
-
@Override
public String getName() {
return "End Stone Bricks";
@@ -36,7 +33,7 @@ public double getResistance() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
Item.get(Item.END_BRICKS, 0, 1)
};
diff --git a/src/main/java/cn/nukkit/block/BlockBricksNether.java b/src/main/java/cn/nukkit/block/BlockBricksNether.java
index 2dab022be92..ac533f6f13c 100644
--- a/src/main/java/cn/nukkit/block/BlockBricksNether.java
+++ b/src/main/java/cn/nukkit/block/BlockBricksNether.java
@@ -10,9 +10,6 @@
*/
public class BlockBricksNether extends BlockSolid {
- public BlockBricksNether() {
- }
-
@Override
public String getName() {
return "Nether Brick";
@@ -40,7 +37,7 @@ public double getResistance() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
Item.get(Item.NETHER_BRICKS, 0, 1)
};
diff --git a/src/main/java/cn/nukkit/block/BlockBricksNetherChiseled.java b/src/main/java/cn/nukkit/block/BlockBricksNetherChiseled.java
new file mode 100644
index 00000000000..e9fe3a42f5a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBricksNetherChiseled.java
@@ -0,0 +1,14 @@
+package cn.nukkit.block;
+
+public class BlockBricksNetherChiseled extends BlockNetherBrick {
+
+ @Override
+ public int getId() {
+ return CHISELED_NETHER_BRICKS;
+ }
+
+ @Override
+ public String getName() {
+ return "Chiseled Nether Bricks";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBricksNetherCracked.java b/src/main/java/cn/nukkit/block/BlockBricksNetherCracked.java
new file mode 100644
index 00000000000..b91c793f11a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBricksNetherCracked.java
@@ -0,0 +1,14 @@
+package cn.nukkit.block;
+
+public class BlockBricksNetherCracked extends BlockNetherBrick {
+
+ @Override
+ public int getId() {
+ return CRACKED_NETHER_BRICKS;
+ }
+
+ @Override
+ public String getName() {
+ return "Cracked Nether Bricks";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBricksRedNether.java b/src/main/java/cn/nukkit/block/BlockBricksRedNether.java
index d69d0358039..8ce2ad7cad1 100644
--- a/src/main/java/cn/nukkit/block/BlockBricksRedNether.java
+++ b/src/main/java/cn/nukkit/block/BlockBricksRedNether.java
@@ -1,14 +1,10 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
public class BlockBricksRedNether extends BlockNetherBrick {
- public BlockBricksRedNether() {
- }
-
@Override
public String getName() {
return "Red Nether Bricks";
@@ -21,7 +17,7 @@ public int getId() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
Item.get(Item.RED_NETHER_BRICK, 0, 1)
};
diff --git a/src/main/java/cn/nukkit/block/BlockBricksStone.java b/src/main/java/cn/nukkit/block/BlockBricksStone.java
index 358a5729928..d086f50da5b 100644
--- a/src/main/java/cn/nukkit/block/BlockBricksStone.java
+++ b/src/main/java/cn/nukkit/block/BlockBricksStone.java
@@ -4,7 +4,7 @@
import cn.nukkit.item.ItemTool;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockBricksStone extends BlockSolidMeta {
@@ -13,6 +13,7 @@ public class BlockBricksStone extends BlockSolidMeta {
public static final int CRACKED = 2;
public static final int CHISELED = 3;
+
public BlockBricksStone() {
this(0);
}
@@ -38,7 +39,7 @@ public double getResistance() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Stone Bricks",
"Mossy Stone Bricks",
"Cracked Stone Bricks",
@@ -50,7 +51,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
Item.get(Item.STONE_BRICKS, this.getDamage() & 0x03, 1)
};
diff --git a/src/main/java/cn/nukkit/block/BlockBubbleColumn.java b/src/main/java/cn/nukkit/block/BlockBubbleColumn.java
new file mode 100644
index 00000000000..e91ba9ef164
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBubbleColumn.java
@@ -0,0 +1,180 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.EntityCreature;
+import cn.nukkit.entity.item.EntityItem;
+import cn.nukkit.event.block.BlockFormEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+
+public class BlockBubbleColumn extends BlockTransparentMeta {
+
+ public static final int DIRECTION_UP = 0;
+ public static final int DIRECTION_DOWN = 1;
+
+ public BlockBubbleColumn() {
+ this(0);
+ }
+
+ public BlockBubbleColumn(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Bubble Column";
+ }
+
+ @Override
+ public int getId() {
+ return BUBBLE_COLUMN;
+ }
+
+ @Override
+ public double getResistance() {
+ return 100;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeReplaced() {
+ return true;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(0);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (this.getLevel().setBlock(this, this, true, true)) {
+ this.getLevel().setBlock(this, Block.LAYER_WATERLOGGED, Block.get(Block.STILL_WATER), true, true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block down = this.down();
+
+ if (down.getId() == BUBBLE_COLUMN) {
+ if (down.getDamage() != this.getDamage()) {
+ this.getLevel().setBlock(this, down, false, true);
+ }
+ } else if (down.getId() == SOUL_SAND) {
+ if (this.getDamage() != DIRECTION_UP) {
+ this.setDamage(DIRECTION_UP);
+ this.getLevel().setBlock(this, this, false, true);
+ }
+ } else if (down.getId() == MAGMA) {
+ if (this.getDamage() != DIRECTION_DOWN) {
+ this.setDamage(DIRECTION_DOWN);
+ this.getLevel().setBlock(this, this, false, true);
+ }
+ } else {
+ this.getLevel().setBlock(this, Block.get(WATER), false, true);
+ return type;
+ }
+
+ Block up = this.up();
+ if (up instanceof BlockWater && (up.getDamage() == 0 || up.getDamage() == 8)) {
+ BlockFormEvent event = new BlockFormEvent(up, Block.get(BUBBLE_COLUMN, this.getDamage()));
+ Server.getInstance().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(up, event.getNewState(), false, true);
+ }
+ }
+
+ return type;
+ }
+
+ return 0;
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (entity.canBeMovedByCurrents()) {
+ if (this.isBlockAboveAir()) {
+ double motY = entity.motionY;
+
+ if (this.getDamage() == 1) {
+ motY = Math.max(-0.9, motY - 0.03);
+ } else {
+ if ((entity instanceof EntityCreature) && motY < -0.64f) {
+ motY = -0.16f;
+ }
+ motY = Math.min(1.8, motY + 0.1);
+ }
+
+ if (entity instanceof Player) {
+ ((Player) entity).setMotionLocally(entity.getMotion().setY(motY));
+ } else {
+ entity.motionY = motY;
+ }
+ } else {
+ double motY = entity.motionY;
+
+ if (this.getDamage() == 1) {
+ motY = Math.max(-0.3, motY - 0.3);
+ } else {
+ motY = Math.min(0.7, motY + 0.06);
+ }
+
+ if (entity instanceof Player) {
+ ((Player) entity).setMotionLocally(entity.getMotion().setY(motY));
+ } else {
+ entity.motionY = motY;
+ }
+ }
+ if (entity instanceof EntityItem) {
+ entity.collisionBlocks = null;
+ }
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBuddingAmethyst.java b/src/main/java/cn/nukkit/block/BlockBuddingAmethyst.java
new file mode 100644
index 00000000000..1ef794af177
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBuddingAmethyst.java
@@ -0,0 +1,96 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.BlockSpreadEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.particle.DestroyBlockParticle;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockBuddingAmethyst extends BlockSolid {
+
+ public BlockBuddingAmethyst() {
+ }
+
+ @Override
+ public int getId() {
+ return BUDDING_AMETHYST;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Budding Amethyst";
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type != Level.BLOCK_UPDATE_RANDOM || ThreadLocalRandom.current().nextInt(4) != 0) {
+ return type;
+ }
+
+ BlockFace face = BlockFace.values()[ThreadLocalRandom.current().nextInt(BlockFace.values().length)];
+ Block block = this.getSide(face);
+
+ BlockAmethystBud targetBlock = null;
+ if (block.getId() == AIR || ((block.getId() == WATER || block.getId() == STILL_WATER) && block.getDamage() == 8)) {
+ targetBlock = (BlockAmethystBud) Block.get(SMALL_AMETHYST_BUD);
+ } else if (block.getId() == SMALL_AMETHYST_BUD && ((BlockAmethystBud) block).getBlockFace() == face) {
+ targetBlock = (BlockAmethystBud) Block.get(MEDIUM_AMETHYST_BUD);
+ } else if (block.getId() == MEDIUM_AMETHYST_BUD && ((BlockAmethystBud) block).getBlockFace() == face) {
+ targetBlock = (BlockAmethystBud) Block.get(LARGE_AMETHYST_BUD);
+ } else if (block.getId() == LARGE_AMETHYST_BUD && ((BlockAmethystBud) block).getBlockFace() == face) {
+ targetBlock = (BlockAmethystBud) Block.get(AMETHYST_CLUSTER);
+ }
+
+ if (targetBlock != null) {
+ targetBlock.setBlockFace(face);
+
+ BlockSpreadEvent event = new BlockSpreadEvent(block, this, targetBlock);
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(block, event.getNewState(), false, true);
+ }
+ }
+ return type;
+ }
+
+ @Override
+ public boolean onBreak(Item item, Player player) {
+ for (BlockFace face : BlockFace.values()) {
+ Block side = this.getSide(face);
+ if (side instanceof BlockAmethystBud && ((BlockAmethystBud) side).getBlockFace() == face) {
+ this.getLevel().setBlock(side, Block.get(BlockID.AIR), true, true);
+ this.getLevel().addParticle(new DestroyBlockParticle(side.add(0.5), side));
+ }
+ }
+ return super.onBreak(item, player);
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.PURPLE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButton.java b/src/main/java/cn/nukkit/block/BlockButton.java
index 7ce86e1beb3..ebc9a4ba3ca 100644
--- a/src/main/java/cn/nukkit/block/BlockButton.java
+++ b/src/main/java/cn/nukkit/block/BlockButton.java
@@ -3,10 +3,9 @@
import cn.nukkit.Player;
import cn.nukkit.event.block.BlockRedstoneEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.level.GlobalBlockPalette;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.Vector3;
import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.utils.Faceable;
@@ -40,7 +39,11 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
this.setDamage(face.getIndex());
- this.level.setBlock(block, this, true, true);
+ if (this.getSide(getFacing().getOpposite()).isTransparent()) {
+ return false;
+ }
+
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@@ -58,12 +61,11 @@ public boolean onActivate(Item item, Player player) {
this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, 0, 15));
this.setDamage(this.getDamage() ^ 0x08);
this.level.setBlock(this, this, true, false);
- this.level.addLevelSoundEvent(this.add(0.5, 0.5, 0.5), LevelSoundEventPacket.SOUND_POWER_ON, GlobalBlockPalette.getOrCreateRuntimeId(this.getId(), this.getDamage()));
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_ON);
this.level.scheduleUpdate(this, 30);
- Vector3 pos = getLocation();
- level.updateAroundRedstone(pos, null);
- level.updateAroundRedstone(pos.getSide(getFacing().getOpposite()), null);
+ level.updateAroundRedstone(this, null);
+ level.updateAroundRedstone(getSideVec(getFacing().getOpposite()), null);
return true;
}
@@ -80,11 +82,10 @@ public int onUpdate(int type) {
this.setDamage(this.getDamage() ^ 0x08);
this.level.setBlock(this, this, true, false);
- this.level.addLevelSoundEvent(this.add(0.5, 0.5, 0.5), LevelSoundEventPacket.SOUND_POWER_OFF, GlobalBlockPalette.getOrCreateRuntimeId(this.getId(), this.getDamage()));
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_OFF);
- Vector3 pos = getLocation();
- level.updateAroundRedstone(pos, null);
- level.updateAroundRedstone(pos.getSide(getFacing().getOpposite()), null);
+ level.updateAroundRedstone(this, null);
+ level.updateAroundRedstone(getSideVec(getFacing().getOpposite()), null);
}
return Level.BLOCK_UPDATE_SCHEDULED;
@@ -126,11 +127,26 @@ public boolean onBreak(Item item) {
@Override
public Item toItem() {
- return Item.get(this.getId(), 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public BlockFace getBlockFace() {
return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonAcacia.java b/src/main/java/cn/nukkit/block/BlockButtonAcacia.java
new file mode 100644
index 00000000000..e434650f9f8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonAcacia.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonAcacia extends BlockButtonWooden {
+
+ public BlockButtonAcacia() {
+ this(0);
+ }
+
+ public BlockButtonAcacia(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Acacia Button";
+ }
+
+ @Override
+ public int getId() {
+ return ACACIA_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonBirch.java b/src/main/java/cn/nukkit/block/BlockButtonBirch.java
new file mode 100644
index 00000000000..2a80276e790
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonBirch.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonBirch extends BlockButtonWooden {
+
+ public BlockButtonBirch() {
+ this(0);
+ }
+
+ public BlockButtonBirch(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Birch Button";
+ }
+
+ @Override
+ public int getId() {
+ return BIRCH_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonCrimson.java b/src/main/java/cn/nukkit/block/BlockButtonCrimson.java
new file mode 100644
index 00000000000..426e22e2b91
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonCrimson.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonCrimson extends BlockButtonWooden {
+
+ public BlockButtonCrimson() {
+ this(0);
+ }
+
+ public BlockButtonCrimson(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Button";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonDarkOak.java b/src/main/java/cn/nukkit/block/BlockButtonDarkOak.java
new file mode 100644
index 00000000000..c886ca68b20
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonDarkOak.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonDarkOak extends BlockButtonWooden {
+
+ public BlockButtonDarkOak() {
+ this(0);
+ }
+
+ public BlockButtonDarkOak(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Dark Oak Button";
+ }
+
+ @Override
+ public int getId() {
+ return DARK_OAK_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonJungle.java b/src/main/java/cn/nukkit/block/BlockButtonJungle.java
new file mode 100644
index 00000000000..81f68c4e94e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonJungle.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonJungle extends BlockButtonWooden {
+
+ public BlockButtonJungle() {
+ this(0);
+ }
+
+ public BlockButtonJungle(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Jungle Button";
+ }
+
+ @Override
+ public int getId() {
+ return JUNGLE_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonPolishedBlackstone.java b/src/main/java/cn/nukkit/block/BlockButtonPolishedBlackstone.java
new file mode 100644
index 00000000000..53718653044
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonPolishedBlackstone.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonPolishedBlackstone extends BlockButtonStone {
+
+ public BlockButtonPolishedBlackstone() {
+ this(0);
+ }
+
+ public BlockButtonPolishedBlackstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone Button";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonSpruce.java b/src/main/java/cn/nukkit/block/BlockButtonSpruce.java
new file mode 100644
index 00000000000..38c7a7ef929
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonSpruce.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonSpruce extends BlockButtonWooden {
+
+ public BlockButtonSpruce() {
+ this(0);
+ }
+
+ public BlockButtonSpruce(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Spruce Button";
+ }
+
+ @Override
+ public int getId() {
+ return SPRUCE_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockButtonStone.java b/src/main/java/cn/nukkit/block/BlockButtonStone.java
index f591330ca4d..1657ad1f477 100644
--- a/src/main/java/cn/nukkit/block/BlockButtonStone.java
+++ b/src/main/java/cn/nukkit/block/BlockButtonStone.java
@@ -33,7 +33,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
this.toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockButtonWarped.java b/src/main/java/cn/nukkit/block/BlockButtonWarped.java
new file mode 100644
index 00000000000..ddad2f9ee2e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockButtonWarped.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockButtonWarped extends BlockButtonWooden {
+
+ public BlockButtonWarped() {
+ this(0);
+ }
+
+ public BlockButtonWarped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Button";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_BUTTON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCactus.java b/src/main/java/cn/nukkit/block/BlockCactus.java
index 9cdc6921e43..2c3aca1a550 100644
--- a/src/main/java/cn/nukkit/block/BlockCactus.java
+++ b/src/main/java/cn/nukkit/block/BlockCactus.java
@@ -8,10 +8,8 @@
import cn.nukkit.event.entity.EntityDamageEvent.DamageCause;
import cn.nukkit.item.Item;
import cn.nukkit.level.Level;
-import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.level.format.FullChunk;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.SimpleAxisAlignedBB;
-import cn.nukkit.math.Vector3;
import cn.nukkit.utils.BlockColor;
/**
@@ -47,16 +45,11 @@ public boolean hasEntityCollision() {
return true;
}
- @Override
+ /*@Override
public double getMinX() {
return this.x + 0.0625;
}
- @Override
- public double getMinY() {
- return this.y;
- }
-
@Override
public double getMinZ() {
return this.z + 0.0625;
@@ -67,19 +60,18 @@ public double getMaxX() {
return this.x + 0.9375;
}
- @Override
- public double getMaxY() {
- return this.y + 0.9375;
- }
-
@Override
public double getMaxZ() {
return this.z + 0.9375;
- }
+ }*/
+
+ // Hack: Fix entity collisions
+ // No need for separate collision box
+ // Y-collisions need another fix anyway
@Override
- protected AxisAlignedBB recalculateCollisionBoundingBox() {
- return new SimpleAxisAlignedBB(this.x, this.y, this.z, this.x + 1, this.y + 1, this.z + 1);
+ public double getMaxY() {
+ return this.y + 0.9375;
}
@Override
@@ -104,13 +96,14 @@ public int onUpdate(int type) {
} else if (type == Level.BLOCK_UPDATE_RANDOM) {
if (down().getId() != CACTUS) {
if (this.getDamage() == 0x0F) {
+ FullChunk chunk = this.level.getChunk((int) x >> 4, (int) z >> 4);
for (int y = 1; y < 3; ++y) {
- Block b = this.getLevel().getBlock(new Vector3(this.x, this.y + y, this.z));
+ Block b = this.getLevel().getBlock(chunk, (int) this.x, (int) this.y + y, (int) this.z, true);
if (b.getId() == AIR) {
- BlockGrowEvent event = new BlockGrowEvent(b, Block.get(BlockID.CACTUS));
+ BlockGrowEvent event = new BlockGrowEvent(b, Block.get(CACTUS));
Server.getInstance().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
- this.getLevel().setBlock(b, event.getNewState(), true);
+ this.getLevel().setBlock(b, event.getNewState(), true, true);
}
break;
}
@@ -119,7 +112,7 @@ public int onUpdate(int type) {
} else {
this.setDamage(this.getDamage() + 1);
}
- this.getLevel().setBlock(this, this);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, true, false); // No need to send this to client
}
}
@@ -135,8 +128,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
Block block2 = west();
Block block3 = east();
if (block0.canBeFlowedInto() && block1.canBeFlowedInto() && block2.canBeFlowedInto() && block3.canBeFlowedInto()) {
- this.getLevel().setBlock(this, this, true);
-
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
}
@@ -152,11 +144,21 @@ public String getName() {
public BlockColor getColor() {
return BlockColor.FOLIAGE_BLOCK_COLOR;
}
-
+
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- Item.get(Item.CACTUS, 0, 1)
+ Item.get(Item.CACTUS, 0, 1)
};
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockCake.java b/src/main/java/cn/nukkit/block/BlockCake.java
index 58c3bdfaa75..f2a9a56f7cd 100644
--- a/src/main/java/cn/nukkit/block/BlockCake.java
+++ b/src/main/java/cn/nukkit/block/BlockCake.java
@@ -2,10 +2,10 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemCake;
import cn.nukkit.item.food.Food;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.utils.BlockColor;
/**
@@ -43,17 +43,12 @@ public double getHardness() {
@Override
public double getResistance() {
- return 2.5;
+ return 0.5;
}
@Override
public double getMinX() {
- return this.x + (1 + getDamage() * 2) / 16;
- }
-
- @Override
- public double getMinY() {
- return this.y;
+ return this.x + ((1 + (getDamage() << 1)) >> 4);
}
@Override
@@ -79,8 +74,7 @@ public double getMaxZ() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (down().getId() != Block.AIR) {
- getLevel().setBlock(block, this, true, true);
-
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
return false;
@@ -106,12 +100,12 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemCake();
+ return Item.get(Item.CAKE);
}
@Override
public boolean onActivate(Item item, Player player) {
- if (player != null && (player.getFoodData().getLevel() < player.getFoodData().getMaxLevel() || player.isCreative() || player.getServer().getDifficulty() == 0)) {
+ if (player != null && player.canEat(false)) {
if (getDamage() <= 0x06) setDamage(getDamage() + 1);
if (getDamage() >= 0x06) {
getLevel().setBlock(this, Block.get(BlockID.AIR), true);
@@ -119,6 +113,7 @@ public boolean onActivate(Item item, Player player) {
Food.getByRelative(this).eatenBy(player);
getLevel().setBlock(this, this, true);
}
+ player.getLevel().addLevelSoundEvent(player, LevelSoundEventPacket.SOUND_BURP);
return true;
}
return false;
@@ -130,10 +125,20 @@ public BlockColor getColor() {
}
public int getComparatorInputOverride() {
- return (7 - this.getDamage()) * 2;
+ return (7 - this.getDamage()) << 1;
}
public boolean hasComparatorInputOverride() {
return true;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockCalcite.java b/src/main/java/cn/nukkit/block/BlockCalcite.java
new file mode 100644
index 00000000000..68866eb270c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCalcite.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockCalcite extends BlockSolid {
+
+ public BlockCalcite() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Calcite";
+ }
+
+ @Override
+ public int getId() {
+ return CALCITE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.75;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.75;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ // TODO:
+ /*@Override
+ public boolean isLavaResistant() {
+ return true;
+ }*/
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCampfire.java b/src/main/java/cn/nukkit/block/BlockCampfire.java
new file mode 100644
index 00000000000..0b982455b10
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCampfire.java
@@ -0,0 +1,262 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityCampfire;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.event.entity.EntityDamageByBlockEvent;
+import cn.nukkit.event.entity.EntityDamageEvent;
+import cn.nukkit.inventory.CampfireInventory;
+import cn.nukkit.inventory.CampfireRecipe;
+import cn.nukkit.inventory.ContainerInventory;
+import cn.nukkit.item.*;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Sound;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.Tag;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Faceable;
+
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockCampfire extends BlockTransparentMeta implements Faceable {
+
+ public BlockCampfire() {
+ this(0);
+ }
+
+ public BlockCampfire(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Campfire";
+ }
+
+ @Override
+ public int getId() {
+ return CAMPFIRE_BLOCK;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return isExtinguished() ? 0 : 15;
+ }
+
+ @Override
+ public double getResistance() {
+ return 2;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[] {Item.get(ItemID.COAL, 0, 1 + ThreadLocalRandom.current().nextInt(1))};
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (this.down().getId() == CAMPFIRE_BLOCK) {
+ return false;
+ }
+
+ this.setDamage(player != null ? player.getDirection().getOpposite().getHorizontalIndex() : 0);
+ Block layer1 = block.getLevelBlock(BlockLayer.WATERLOGGED);
+
+ boolean defaultLayerCheck = (block instanceof BlockWater && block.getDamage() == 0 || block.getDamage() >= 8) || block instanceof BlockIceFrosted;
+ boolean layer1Check = (layer1 instanceof BlockWater && layer1.getDamage() == 0 || layer1.getDamage() >= 8) || layer1 instanceof BlockIceFrosted;
+ if (defaultLayerCheck || layer1Check) {
+ this.setExtinguished(true);
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_FIZZ);
+ this.level.setBlock(this, BlockLayer.WATERLOGGED, defaultLayerCheck ? block : layer1, false, false);
+ } else {
+ this.level.setBlock(this, BlockLayer.WATERLOGGED, Block.get(Block.AIR), false, false);
+ }
+
+ this.getLevel().setBlock(this, this, true, true);
+ this.createBlockEntity(item);
+ return true;
+ }
+
+ private BlockEntityCampfire createBlockEntity(Item item) {
+ CompoundTag nbt = new CompoundTag()
+ .putString("id", BlockEntity.CAMPFIRE)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ if (item.hasCustomBlockData()) {
+ Map customData = item.getCustomBlockData().getTags();
+ for (Map.Entry tag : customData.entrySet()) {
+ nbt.put(tag.getKey(), tag.getValue());
+ }
+ }
+
+ return (BlockEntityCampfire) BlockEntity.createBlockEntity(BlockEntity.CAMPFIRE, this.getChunk(), nbt);
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (!this.isExtinguished() && !entity.isSneaking()) {
+ entity.attack(new EntityDamageByBlockEvent(this, entity, EntityDamageEvent.DamageCause.FIRE, this instanceof BlockCampfireSoul ? 2 : 1));
+ }
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!this.isExtinguished()) {
+ Block layer1 = this.getLevelBlock(BlockLayer.WATERLOGGED);
+ if (layer1 instanceof BlockWater || layer1 instanceof BlockIceFrosted) {
+ this.setExtinguished(true);
+ this.level.setBlock(this, this, true, true);
+ this.level.addSound(this, Sound.RANDOM_FIZZ, 0.5f, 2.2f);
+ }
+ }
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() == BlockID.AIR || item.getCount() <= 0) {
+ return false;
+ }
+
+ BlockEntity entity = this.level.getBlockEntity(this);
+ if (!(entity instanceof BlockEntityCampfire)) {
+ return false;
+ }
+
+ boolean itemUsed = false;
+ if (item.isShovel() && !this.isExtinguished()) {
+ this.setExtinguished(true);
+ this.level.setBlock(this, this, true, true);
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_FIZZ);
+ itemUsed = true;
+ } else if (item.getId() == ItemID.FLINT_AND_STEEL || item.hasEnchantment(Enchantment.ID_FIRE_ASPECT)) {
+ item.useOn(this);
+ this.setExtinguished(false);
+ this.level.setBlock(this, this, true, true);
+ entity.scheduleUpdate();
+ level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_IGNITE);
+ itemUsed = true;
+ }
+
+ BlockEntityCampfire campfire = (BlockEntityCampfire) entity;
+ Item cloned = item.clone();
+ cloned.setCount(1);
+ CampfireInventory inventory = campfire.getInventory();
+ if (inventory.canAddItem(cloned)) {
+ CampfireRecipe recipe = this.level.getServer().getCraftingManager().matchCampfireRecipe(cloned);
+ if (recipe != null) {
+ inventory.addItem(cloned);
+ item.setCount(item.getCount() - 1);
+ return true;
+ }
+ }
+
+ return itemUsed;
+ }
+
+ @Override
+ public double getMaxY() {
+ return y + 0.5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SPRUCE_BLOCK_COLOR;
+ }
+
+ public boolean isExtinguished() {
+ return (this.getDamage() & 0x4) == 0x4;
+ }
+
+ public void setExtinguished(boolean extinguished) {
+ this.setDamage((this.getDamage() & 0x3) | (extinguished? 0x4 : 0x0));
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(getDamage() & 0x3);
+ }
+
+ public void setBlockFace(BlockFace face) {
+ if (face == BlockFace.UP || face == BlockFace.DOWN) {
+ return;
+ }
+
+ this.setDamage((this.getDamage() & 0x4) | face.getHorizontalIndex());
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.CAMPFIRE);
+ }
+
+ public boolean hasComparatorInputOverride() {
+ return true;
+ }
+
+ @Override
+ public int getComparatorInputOverride() {
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (blockEntity instanceof BlockEntityCampfire) {
+ return ContainerInventory.calculateRedstone(((BlockEntityCampfire) blockEntity).getInventory());
+ }
+
+ return super.getComparatorInputOverride();
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCampfireSoul.java b/src/main/java/cn/nukkit/block/BlockCampfireSoul.java
new file mode 100644
index 00000000000..893cd1734b5
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCampfireSoul.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemID;
+
+public class BlockCampfireSoul extends BlockCampfire {
+
+ public BlockCampfireSoul() {
+ this(0);
+ }
+
+ public BlockCampfireSoul(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return SOUL_CAMPFIRE_BLOCK;
+ }
+
+ @Override
+ public String getName() {
+ return "Soul Campfire";
+ }
+
+ @Override
+ public int getLightLevel() {
+ return isExtinguished()? 0 : 10;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.SOUL_CAMPFIRE);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[]{ new ItemBlock(Block.get(Block.SOUL_SOIL, 0), 0) };
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCarpet.java b/src/main/java/cn/nukkit/block/BlockCarpet.java
index 3cfa2db4e1d..9d46e451cd5 100644
--- a/src/main/java/cn/nukkit/block/BlockCarpet.java
+++ b/src/main/java/cn/nukkit/block/BlockCarpet.java
@@ -3,7 +3,6 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
import cn.nukkit.level.Level;
-import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.DyeColor;
@@ -13,6 +12,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockCarpet extends BlockFlowable {
+
public BlockCarpet() {
this(0);
}
@@ -30,11 +30,6 @@ public int getId() {
return CARPET;
}
- @Override
- public double getHardness() {
- return 0.1;
- }
-
@Override
public double getResistance() {
return 0.5;
@@ -55,11 +50,6 @@ public boolean canPassThrough() {
return false;
}
- @Override
- protected AxisAlignedBB recalculateBoundingBox() {
- return this;
- }
-
@Override
public double getMaxY() {
return this.y + 0.0625;
@@ -69,7 +59,7 @@ public double getMaxY() {
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.getId() != Item.AIR) {
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
return false;
@@ -97,4 +87,13 @@ public DyeColor getDyeColor() {
return DyeColor.getByWoolData(getDamage());
}
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockCarrot.java b/src/main/java/cn/nukkit/block/BlockCarrot.java
index f86a429c4c6..66d00118a05 100644
--- a/src/main/java/cn/nukkit/block/BlockCarrot.java
+++ b/src/main/java/cn/nukkit/block/BlockCarrot.java
@@ -1,9 +1,7 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemCarrot;
-
-import java.util.Random;
+import cn.nukkit.utils.Utils;
/**
* @author Nukkit Project Team
@@ -32,16 +30,16 @@ public int getId() {
public Item[] getDrops(Item item) {
if (getDamage() >= 0x07) {
return new Item[]{
- new ItemCarrot(0, new Random().nextInt(3) + 1)
+ Item.get(Item.CARROT, 0, Utils.rand(1, 5))
};
}
return new Item[]{
- new ItemCarrot()
+ Item.get(Item.CARROT)
};
}
@Override
public Item toItem() {
- return new ItemCarrot();
+ return Item.get(Item.CARROT);
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockCartographyTable.java b/src/main/java/cn/nukkit/block/BlockCartographyTable.java
new file mode 100644
index 00000000000..447373c3984
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCartographyTable.java
@@ -0,0 +1,42 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCartographyTable extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Cartography Table";
+ }
+
+ @Override
+ public int getId() {
+ return CARTOGRAPHY_TABLE;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public double getResistance() {
+ return 12.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2.5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 5;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCauldron.java b/src/main/java/cn/nukkit/block/BlockCauldron.java
index d9bae0a4acb..afb1f82d4ee 100644
--- a/src/main/java/cn/nukkit/block/BlockCauldron.java
+++ b/src/main/java/cn/nukkit/block/BlockCauldron.java
@@ -6,18 +6,32 @@
import cn.nukkit.event.player.PlayerBucketEmptyEvent;
import cn.nukkit.event.player.PlayerBucketFillEvent;
import cn.nukkit.item.*;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Sound;
+import cn.nukkit.level.biome.Biome;
+import cn.nukkit.level.particle.SmokeParticle;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.MathHelper;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.Tag;
import cn.nukkit.network.protocol.LevelEventPacket;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import cn.nukkit.utils.BlockColor;
import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
/**
- * author: CreeperFace
+ * @author CreeperFace
* Nukkit Project
*/
-public class BlockCauldron extends BlockSolidMeta {
+public class BlockCauldron extends BlockTransparentMeta {
+
+ /**
+ * Used to cache biome check for freezing
+ * 1 = can't freeze, 2 = can freeze
+ */
+ private byte freezing;
public BlockCauldron() {
super(0);
@@ -57,13 +71,22 @@ public boolean canBeActivated() {
}
public boolean isFull() {
- return this.getDamage() == 0x06;
+ return (this.getDamage() & 0x06) == 0x06;
}
public boolean isEmpty() {
return this.getDamage() == 0x00;
}
+ public int getFillLevel() {
+ return (getDamage() & 0x6) >> 1;
+ }
+
+ public void setFillLevel(int fillLevel) {
+ fillLevel = MathHelper.clamp(fillLevel, 0, 3);
+ setDamage(fillLevel << 1);
+ }
+
@Override
public boolean onActivate(Item item, Player player) {
BlockEntity be = this.level.getBlockEntity(this);
@@ -89,14 +112,13 @@ public boolean onActivate(Item item, Player player) {
this.level.getServer().getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
replaceBucket(item, player, ev.getItem());
- this.setDamage(0);//empty
+ this.setFillLevel(0);//empty
this.level.setBlock(this, this, true);
cauldron.clearCustomColor();
- this.getLevel().addLevelEvent(this.add(0.5, 0.375 + this.getDamage() * 0.125, 0.5), LevelEventPacket.EVENT_CAULDRON_TAKE_WATER);
+ this.getLevel().addSound(this, Sound.CAULDRON_TAKEWATER);
}
- } else if (item.getDamage() == 8) {//water bucket
-
- if (isFull() && !cauldron.isCustomColor() && !cauldron.hasPotion()) {
+ } else if (item.getDamage() == 8 || item.getDamage() == 10) {//water and lava buckets
+ if (isFull() && !cauldron.isCustomColor() && !cauldron.hasPotion() && item.getDamage() == 8) {
break;
}
@@ -107,71 +129,144 @@ public boolean onActivate(Item item, Player player) {
PlayerBucketEmptyEvent ev = new PlayerBucketEmptyEvent(player, this, null, item, bucket);
this.level.getServer().getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
- replaceBucket(item, player, ev.getItem());
+ if (player.isSurvival() || player.isAdventure()) {
+ replaceBucket(item, player, ev.getItem());
+ }
if (cauldron.hasPotion()) {//if has potion
- this.setDamage(0);//empty
- cauldron.setPotionId(0xffff);//reset potion
- cauldron.setSplashPotion(false);
- cauldron.clearCustomColor();
- this.level.setBlock(this, this, true);
- this.level.addLevelEvent(this.add(0.5, 0.375 + this.getDamage() * 0.125, 0.5), LevelEventPacket.EVENT_SOUND_EXPLODE);
- } else {
- this.setDamage(6);//fill
+ clearWithFizz(cauldron);
+ } else if (item.getDamage() == 8) { //water bucket
+ this.setFillLevel(3);//fill
cauldron.clearCustomColor();
this.level.setBlock(this, this, true);
- this.level.addLevelEvent(this.add(0.5, 0.375 + this.getDamage() * 0.125, 0.5), LevelEventPacket.EVENT_SOUND_CAULDRON_FILL_WATER);
+ this.getLevel().addSound(this, Sound.CAULDRON_FILLWATER);
+ } else { // lava bucket
+ if (isEmpty()) {
+ BlockCauldronLava cauldronLava = new BlockCauldronLava(0xE);
+ cauldronLava.setFillLevel(3);
+ this.level.setBlock(this, cauldronLava, true, true);
+ cauldron.clearCustomColor();
+ this.getLevel().addSound(this.add(0.5, 0.5, 0.5), Sound.BUCKET_EMPTY_LAVA);
+ } else {
+ clearWithFizz(cauldron);
+ }
}
//this.update();
}
}
break;
- case Item.DYE: //TODO
+ case Item.DYE:
+ if (isEmpty() || cauldron.hasPotion()) {
+ break;
+ }
+
+ if (player.isSurvival() || player.isAdventure()) {
+ item.setCount(item.getCount() - 1);
+ player.getInventory().setItemInHand(item);
+ }
+
+ BlockColor color = new ItemDye(item.getDamage()).getDyeColor().getColor();
+ if (!cauldron.isCustomColor()) {
+ cauldron.setCustomColor(color);
+ } else {
+ BlockColor current = cauldron.getCustomColor();
+ BlockColor mixed = new BlockColor(
+ current.getRed() + ((color.getRed() - current.getRed()) >> 1),
+ current.getGreen() + ((color.getGreen() - current.getGreen()) >> 1),
+ current.getBlue() + ((color.getBlue() - current.getBlue()) >> 1)
+ );
+ cauldron.setCustomColor(mixed);
+ }
+ this.level.addSound(this, Sound.CAULDRON_ADDDYE);
break;
case Item.LEATHER_CAP:
case Item.LEATHER_TUNIC:
case Item.LEATHER_PANTS:
case Item.LEATHER_BOOTS:
+ case Item.LEATHER_HORSE_ARMOR:
+ if (isEmpty() || cauldron.hasPotion()) {
+ break;
+ }
+
+ CompoundTag compoundTag = item.getNamedTag();
+ if (compoundTag == null) compoundTag = new CompoundTag();
+ if (cauldron.isCustomColor()) {
+ compoundTag.putInt("customColor", cauldron.getCustomColor().getRGB());
+ } else {
+ compoundTag.remove("customColor");
+ }
+ item.setCompoundTag(compoundTag);
+ player.getInventory().setItemInHand(item);
+
+ setFillLevel(getFillLevel() - 1);
+ this.level.setBlock(this, this, true, true);
+ this.level.addSound(this, Sound.CAULDRON_DYEARMOR);
break;
case Item.POTION:
+ case Item.SPLASH_POTION:
+ case Item.LINGERING_POTION:
+ if (!isEmpty() && (cauldron.hasPotion() ? cauldron.getPotionId() != item.getDamage() : item.getDamage() != 0)) {
+ clearWithFizz(cauldron);
+ consumePotion(item, player);
+ break;
+ }
if (isFull()) {
break;
}
- this.setDamage(this.getDamage() + 1);
- if (this.getDamage() > 0x06)
- this.setDamage(0x06);
-
- if (item.getCount() == 1) {
- player.getInventory().setItemInHand(new ItemBlock(Block.get(BlockID.AIR)));
- } else if (item.getCount() > 1) {
- item.setCount(item.getCount() - 1);
- player.getInventory().setItemInHand(item);
-
- Item bottle = new ItemGlassBottle();
- if (player.getInventory().canAddItem(bottle)) {
- player.getInventory().addItem(bottle);
- } else {
- player.getLevel().dropItem(player.add(0, 1.3, 0), bottle, player.getDirectionVector().multiply(0.4));
- }
+ if (item.getDamage() != 0 && isEmpty()) {
+ cauldron.setPotionId(item.getDamage());
}
-
- this.level.addLevelEvent(this.add(0.5, 0.375 + this.getDamage() * 0.125, 0.5), LevelEventPacket.EVENT_CAULDRON_FILL_POTION);
+ cauldron.setPotionType(
+ item.getId() == Item.POTION ? BlockEntityCauldron.POTION_TYPE_NORMAL :
+ item.getId() == Item.SPLASH_POTION ? BlockEntityCauldron.POTION_TYPE_SPLASH :
+ BlockEntityCauldron.POTION_TYPE_LINGERING
+ );
+ cauldron.spawnToAll();
+
+ setFillLevel(getFillLevel() + 1);
+ this.level.setBlock(this, this, true);
+ consumePotion(item, player);
+ this.getLevel().addSound(this, Sound.CAULDRON_FILLPOTION);
break;
case Item.GLASS_BOTTLE:
if (isEmpty()) {
break;
}
- this.setDamage(this.getDamage() - 1);
- if (this.getDamage() < 0x00)
- this.setDamage(0x00);
+ int meta = cauldron.hasPotion() ? cauldron.getPotionId() : 0;
+ Item potion;
+ if (meta == 0) {
+ potion = Item.get(Item.POTION);
+ } else {
+ switch (cauldron.getPotionType()) {
+ case BlockEntityCauldron.POTION_TYPE_SPLASH:
+ potion = Item.get(Item.SPLASH_POTION, meta);
+ break;
+ case BlockEntityCauldron.POTION_TYPE_LINGERING:
+ potion = Item.get(Item.LINGERING_POTION, meta);
+ break;
+ case BlockEntityCauldron.POTION_TYPE_NORMAL:
+ default:
+ potion = Item.get(Item.POTION, meta);
+ break;
+ }
+ }
+
+ setFillLevel(getFillLevel() - 1);
+ if (isEmpty()) {
+ cauldron.setPotionId(0xffff);//reset potion
+ cauldron.clearCustomColor();
+ }
+ this.level.setBlock(this, this, true);
- if (item.getCount() == 1) {
- player.getInventory().setItemInHand(new ItemPotion());
+ boolean consumeBottle = player.isSurvival() || player.isAdventure();
+ if (consumeBottle && item.getCount() == 1) {
+ player.getInventory().setItemInHand(potion);
} else if (item.getCount() > 1) {
- item.setCount(item.getCount() - 1);
- player.getInventory().setItemInHand(item);
+ if (consumeBottle) {
+ item.setCount(item.getCount() - 1);
+ player.getInventory().setItemInHand(item);
+ }
- Item potion = new ItemPotion();
if (player.getInventory().canAddItem(potion)) {
player.getInventory().addItem(potion);
} else {
@@ -179,7 +274,49 @@ public boolean onActivate(Item item, Player player) {
}
}
- this.level.addLevelEvent(this.add(0.5, 0.375 + this.getDamage() * 0.125, 0.5), LevelEventPacket.EVENT_CAULDRON_TAKE_POTION);
+ this.getLevel().addSound(this, Sound.CAULDRON_TAKEPOTION);
+ break;
+ case Item.ARROW:
+ if (item.getDamage() > 1 || !cauldron.hasPotion()) {
+ break;
+ }
+
+ if (!player.isCreative() && item.getCount() == 1) {
+ item.setDamage(potion2arrow(cauldron.getPotionId()));
+ player.getInventory().setItemInHand(item);
+ } else if (item.getCount() > 1) {
+ Item newItem = item.clone();
+ newItem.setCount(1);
+ newItem.setDamage(potion2arrow(cauldron.getPotionId()));
+
+ if (!player.isCreative()) {
+ item.setCount(item.getCount() - 1);
+ player.getInventory().setItemInHand(item);
+ }
+
+ if (player.getInventory().canAddItem(newItem)) {
+ player.getInventory().addItem(newItem);
+ } else {
+ player.getLevel().dropItem(player.add(0, 1.3, 0), newItem, player.getDirectionVector().multiply(0.4));
+ }
+ }
+
+ setFillLevel(getFillLevel() - 1);
+ if (isEmpty()) {
+ cauldron.setPotionId(0xffff);
+ cauldron.clearCustomColor();
+ }
+ this.level.setBlock(this, this, true);
+ this.level.addLevelEvent(this.add(0.5, 0.375 + this.getDamage() * 0.125, 0.5), LevelEventPacket.EVENT_CAULDRON_DYE_ARMOR);
+ case BlockID.SHULKER_BOX:
+ if (isEmpty() || cauldron.isCustomColor() || cauldron.hasPotion()) {
+ break;
+ }
+
+ player.getInventory().setItemInHand(Item.get(Item.UNDYED_SHULKER_BOX).setCompoundTag(item.getCompoundTag()));
+ setFillLevel(getFillLevel() - 1);
+ this.level.setBlock(this, this, true);
+ this.getLevel().addSound(this, Sound.CAULDRON_TAKEPOTION);
break;
default:
return true;
@@ -188,7 +325,13 @@ public boolean onActivate(Item item, Player player) {
this.level.updateComparatorOutputLevel(this);
return true;
}
-
+
+ private static int potion2arrow(int potion) {
+ int id = potion & 0xffff;
+ if (id < 5 || id > 43) return 1; // if it fails don't create game crashing arrows
+ return id < 43 ? id + 1 : id;
+ }
+
protected void replaceBucket(Item oldBucket, Player player, Item newBucket) {
if (player.isSurvival() || player.isAdventure()) {
if (oldBucket.getCount() == 1) {
@@ -203,7 +346,7 @@ protected void replaceBucket(Item oldBucket, Player player, Item newBucket) {
}
}
}
-
+
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
CompoundTag nbt = new CompoundTag("")
@@ -221,26 +364,39 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- BlockEntityCauldron cauldron = (BlockEntityCauldron) BlockEntity.createBlockEntity(BlockEntity.CAULDRON, this.level.getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- if (cauldron == null) {
- return false;
- }
- this.getLevel().setBlock(block, this, true, true);
+ BlockEntity.createBlockEntity(BlockEntity.CAULDRON, this.getChunk(), nbt);
+
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@Override
public Item[] getDrops(Item item) {
if (item.getTier() >= ItemTool.TIER_WOODEN) {
- return new Item[]{new ItemCauldron()};
+ return new Item[]{Item.get(Item.CAULDRON)};
}
return new Item[0];
}
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_RANDOM && level.isRaining() && !this.isFull()) {
+ if (freezing < 1) {
+ freezing = Biome.getBiome(level.getBiomeId((int) this.x, (int) this.z)).isFreezing() ? (byte) 2 : (byte) 1;
+ }
+ if (freezing == 1 && ThreadLocalRandom.current().nextInt(20) == 0 && level.canBlockSeeSky(this)) {
+ this.setFillLevel(this.getFillLevel() + 1);
+ this.getLevel().setBlock(this, this, true, true);
+ return Level.BLOCK_UPDATE_RANDOM;
+ }
+ }
+ return super.onUpdate(type);
+ }
+
@Override
public Item toItem() {
- return new ItemCauldron();
+ return Item.get(Item.CAULDRON);
}
public boolean hasComparatorInputOverride() {
@@ -248,11 +404,55 @@ public boolean hasComparatorInputOverride() {
}
public int getComparatorInputOverride() {
- return this.getDamage();
+ return getFillLevel();
}
@Override
public boolean canHarvestWithHand() {
return false;
}
+
+ // Source: PN/#666
+ private void consumePotion(Item item, Player player) {
+ if (player.isSurvival() || player.isAdventure()) {
+ if (item.getCount() == 1) {
+ player.getInventory().setItemInHand(new ItemBlock(Block.get(AIR)));
+ } else if (item.getCount() > 1) {
+ item.setCount(item.getCount() - 1);
+ player.getInventory().setItemInHand(item);
+ Item bottle = Item.get(Item.GLASS_BOTTLE);
+ if (player.getInventory().canAddItem(bottle)) {
+ player.getInventory().addItem(bottle);
+ } else {
+ player.getLevel().dropItem(player.add(0, 1.3, 0), bottle, player.getDirectionVector().multiply(0.4));
+ }
+ }
+ }
+ }
+
+ // Source: PN/#666
+ public void clearWithFizz(BlockEntityCauldron cauldron) {
+ this.setFillLevel(0);
+ cauldron.setPotionId(0xffff);
+ cauldron.setSplashPotion(false);
+ cauldron.clearCustomColor();
+ this.level.setBlock(this, Block.get(CAULDRON_BLOCK), true);
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_FIZZ);
+ this.getLevel().addParticle(new SmokeParticle(add(Math.random(), 1.2, Math.random())), null, 8);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GRAY_BLOCK_COLOR;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockCauldronLava.java b/src/main/java/cn/nukkit/block/BlockCauldronLava.java
new file mode 100644
index 00000000000..64e524b638c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCauldronLava.java
@@ -0,0 +1,113 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityCauldron;
+import cn.nukkit.entity.BaseEntity;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.event.entity.EntityCombustByBlockEvent;
+import cn.nukkit.event.entity.EntityDamageByBlockEvent;
+import cn.nukkit.event.entity.EntityDamageEvent;
+import cn.nukkit.event.player.PlayerBucketFillEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.Sound;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.potion.Effect;
+
+public class BlockCauldronLava extends BlockCauldron {
+
+ public BlockCauldronLava() {
+ this(0x8);
+ }
+
+ public BlockCauldronLava(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Lava Cauldron";
+ }
+
+ @Override
+ public int getId() {
+ return LAVA_CAULDRON;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 15;
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public void setFillLevel(int fillLevel) {
+ super.setFillLevel(fillLevel);
+ setDamage(getDamage() | 0x8);
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (!entity.fireProof || !entity.isOnFire() || !(entity instanceof BaseEntity)) { // Improve performance
+ if (!entity.fireProof || !entity.isOnFire()) {
+
+ EntityCombustByBlockEvent ev = new EntityCombustByBlockEvent(this, entity, 8);
+ Server.getInstance().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled() && entity.isAlive() && entity.noDamageTicks == 0) {
+ entity.setOnFire(ev.getDuration());
+ }
+ }
+
+ if (!entity.hasEffect(Effect.FIRE_RESISTANCE)) {
+ entity.attack(new EntityDamageByBlockEvent(this, entity, EntityDamageEvent.DamageCause.LAVA, 4));
+ }
+ }
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() == ItemID.BUCKET) {
+ if (item.getDamage() == 0) {
+ if (!isFull()) {
+ return false;
+ }
+
+ PlayerBucketFillEvent ev = new PlayerBucketFillEvent(player, this, null, item, Item.get(ItemID.BUCKET, 10, 1));
+ this.level.getServer().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ replaceBucket(item, player, ev.getItem());
+ if (!(this.level.getBlockEntity(this) instanceof BlockEntityCauldron)) {
+ BlockEntity.createBlockEntity(BlockEntity.CAULDRON, this.getChunk(), new CompoundTag("")
+ .putString("id", BlockEntity.CAULDRON)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z)
+ .putShort("PotionId", 0xffff)
+ .putByte("SplashPotion", 0));
+ }
+ this.level.setBlock(this, Block.get(CAULDRON_BLOCK), true);
+ this.getLevel().addSound(this.add(0.5, 0.5, 0.5), Sound.BUCKET_FILL_LAVA);
+ }
+ }
+ }
+
+ this.level.updateComparatorOutputLevel(this);
+ return true;
+ }
+
+ @Override
+ public boolean isFull() {
+ return this.getDamage() == 14;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCaveVines.java b/src/main/java/cn/nukkit/block/BlockCaveVines.java
new file mode 100644
index 00000000000..7cd9186958f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCaveVines.java
@@ -0,0 +1,238 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.BlockFace;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockCaveVines extends BlockTransparentMeta {
+
+ private static final float CHANCE_OF_BERRIES_ON_GROWTH = 0.11F * 1.2F;
+
+ public BlockCaveVines() {
+ this(0);
+ }
+
+ public BlockCaveVines(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Cave Vines";
+ }
+
+ @Override
+ public int getId() {
+ return CAVE_VINES;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ public boolean canPlaceOn(Block floor, Position pos) {
+ Block up = floor.up(2);
+ return (up.isSolid() || up instanceof BlockCaveVines) && super.canPlaceOn(floor, pos);
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (!this.canPlaceOn(block.down(), target)) {
+ return false;
+ }
+
+ Block support = block.up();
+ if (isCaveVine(support)) {
+ this.setVineAge(Math.min(this.getMaxAge(), ((BlockCaveVines) support).getVineAge() + 1));
+ } else {
+ this.setVineAge(0);
+ }
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ switch (type) {
+ case Level.BLOCK_UPDATE_NORMAL:
+ Block up = this.up();
+ if (!isCaveVine(up) && !up.isSolid()) {
+ this.getLevel().scheduleUpdate(this, 1);
+ }
+ break;
+ case Level.BLOCK_UPDATE_SCHEDULED:
+ this.getLevel().useBreakOn(this, null, null, true);
+ break;
+ case Level.BLOCK_UPDATE_RANDOM:
+ if (!this.tryGrowItself()) {
+ this.tryGrow();
+ }
+ break;
+ }
+ return type;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (this.tryPickupBerries()) {
+ return true;
+ }
+
+ if (item.getId() != Item.DYE || item.getDamage() != ItemDye.BONE_MEAL) {
+ return false;
+ }
+
+ Block bottom = this;
+ BlockCaveVines plantHead = null;
+ while (bottom instanceof BlockCaveVines) {
+ plantHead = (BlockCaveVines) bottom;
+ bottom = bottom.down();
+ }
+
+ if (!plantHead.tryGrow()) {
+ return false;
+ }
+
+ this.level.addParticle(new BoneMealParticle(plantHead));
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ private boolean tryPickupBerries() {
+ if (!this.hasBerries()) {
+ return false;
+ }
+
+ BlockCaveVines blockCaveVines = this.getStateWithoutBerries(this);
+ this.getLevel().setBlock(this, blockCaveVines, false, true);
+
+ Item item = Item.get(ItemID.GLOW_BERRIES, 0, 1);
+ this.getLevel().dropItem(this.add(0.5, 0.5, 0.5), item);
+ return true;
+ }
+
+ private boolean tryGrow() {
+ if (this.getVineAge() >= this.getMaxAge()) {
+ return false;
+ }
+
+ Block down = this.down();
+ if (down.getY() <= this.getLevel().getMinBlockY() || down.getId() != Block.AIR) {
+ return false;
+ }
+
+ Block topBlock = this;
+ while (topBlock instanceof BlockCaveVines) {
+ if (topBlock.getDamage() < this.getVineAge()) {
+ break;
+ }
+ topBlock = topBlock.up();
+ }
+
+ if (topBlock.getDamage() >= this.getMaxAge()) {
+ return false;
+ }
+
+ boolean withBerries = ThreadLocalRandom.current().nextFloat() < CHANCE_OF_BERRIES_ON_GROWTH;
+ BlockCaveVines head = withBerries ? this.getStateWithBerries(down) : this.getStateWithoutBerries(down);
+
+ BlockGrowEvent event = new BlockGrowEvent(this, head);
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ this.getLevel().setBlock(down, event.getNewState(), true, true);
+
+ BlockCaveVines support = (BlockCaveVines) Block.get(this.hasBerries() ? CAVE_VINES_BODY_WITH_BERRIES : CAVE_VINES, this.getDamage());
+ this.getLevel().setBlock(this, support, true, true);
+ return true;
+ }
+
+ private boolean tryGrowItself() {
+ if (this.hasBerries() || ThreadLocalRandom.current().nextFloat() >= CHANCE_OF_BERRIES_ON_GROWTH) {
+ return false;
+ }
+
+ BlockCaveVines blockCaveVines = this.getStateWithBerries(this);
+ this.getLevel().setBlock(this, blockCaveVines, true, true);
+ return true;
+ }
+
+ public BlockCaveVines getStateWithBerries(Position position) {
+ if (this.getDamage() == 0) {
+ return (BlockCaveVines) Block.get(CAVE_VINES_HEAD_WITH_BERRIES, 0, position);
+ }
+ return (BlockCaveVines) Block.get(CAVE_VINES_HEAD_WITH_BERRIES, this.getDamage(), position);
+ }
+
+ public BlockCaveVines getStateWithoutBerries(Position position) {
+ return (BlockCaveVines) Block.get(CAVE_VINES, this.getDamage(), position);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (!this.hasBerries()) {
+ return new Item[0];
+ }
+ return new Item[]{ Item.get(ItemID.GLOW_BERRIES, 0, 1) };
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ public void setVineAge(int age) {
+ this.setDamage(age);
+ }
+
+ public int getVineAge() {
+ return this.getDamage();
+ }
+
+ public int getMaxAge() {
+ return 25;
+ }
+
+ public boolean hasBerries() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeClimbed() {
+ return true;
+ }
+
+ public static boolean isCaveVine(Block block) {
+ switch (block.getId()) {
+ case CAVE_VINES:
+ case CAVE_VINES_BODY_WITH_BERRIES:
+ case CAVE_VINES_HEAD_WITH_BERRIES:
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCaveVinesBerriesBody.java b/src/main/java/cn/nukkit/block/BlockCaveVinesBerriesBody.java
new file mode 100644
index 00000000000..f01f70b3bf8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCaveVinesBerriesBody.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+import cn.nukkit.level.Position;
+
+public class BlockCaveVinesBerriesBody extends BlockCaveVines {
+
+ public BlockCaveVinesBerriesBody() {
+ this(0);
+ }
+
+ public BlockCaveVinesBerriesBody(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return CAVE_VINES_BODY_WITH_BERRIES;
+ }
+
+ @Override
+ public BlockCaveVines getStateWithBerries(Position position) {
+ return (BlockCaveVines) Block.get(CAVE_VINES_HEAD_WITH_BERRIES, this.getDamage(), position);
+ }
+
+ @Override
+ public BlockCaveVines getStateWithoutBerries(Position position) {
+ return (BlockCaveVines) Block.get(CAVE_VINES, this.getDamage(), position);
+ }
+
+ @Override
+ public boolean hasBerries() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCaveVinesBerriesHead.java b/src/main/java/cn/nukkit/block/BlockCaveVinesBerriesHead.java
new file mode 100644
index 00000000000..8424b9559ee
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCaveVinesBerriesHead.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+import cn.nukkit.level.Position;
+
+public class BlockCaveVinesBerriesHead extends BlockCaveVines {
+
+ public BlockCaveVinesBerriesHead() {
+ this(0);
+ }
+
+ public BlockCaveVinesBerriesHead(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return CAVE_VINES_HEAD_WITH_BERRIES;
+ }
+
+ @Override
+ public BlockCaveVines getStateWithBerries(Position position) {
+ return (BlockCaveVines) Block.get(CAVE_VINES_HEAD_WITH_BERRIES, 0, position);
+ }
+
+ @Override
+ public BlockCaveVines getStateWithoutBerries(Position position) {
+ return (BlockCaveVines) Block.get(CAVE_VINES, 0, position);
+ }
+
+ @Override
+ public boolean hasBerries() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockChain.java b/src/main/java/cn/nukkit/block/BlockChain.java
new file mode 100644
index 00000000000..d1f01d4798f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockChain.java
@@ -0,0 +1,112 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+
+public class BlockChain extends BlockTransparentMeta {
+
+ public BlockChain() {
+ this(0);
+ }
+
+ public BlockChain(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Chain";
+ }
+
+ @Override
+ public int getId() {
+ return CHAIN_BLOCK;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setPillarAxis(face.getAxis());
+ if (super.place(item, block, target, face, fx, fy, fz, player)) {
+ return true;
+ }
+ return false;
+ }
+
+ public void setPillarAxis(BlockFace.Axis axis) {
+ switch (axis) {
+ case Y:
+ this.setDamage(0);
+ break;
+ case X:
+ this.setDamage(1);
+ break;
+ case Z:
+ this.setDamage(2);
+ break;
+ }
+ }
+
+ public BlockFace.Axis getPillarAxis() {
+ switch (this.getDamage() % 3) {
+ case 2:
+ return BlockFace.Axis.Z;
+ case 1:
+ return BlockFace.Axis.X;
+ case 0:
+ default:
+ return BlockFace.Axis.Y;
+ }
+ }
+
+ @Override
+ public double getHardness() {
+ return 5;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getMinX() {
+ return x + 7 / 16.0;
+ }
+
+ @Override
+ public double getMaxX() {
+ return x + 9 / 16.0;
+ }
+
+ @Override
+ public double getMinZ() {
+ return z + 7 / 16.0;
+ }
+
+ @Override
+ public double getMaxZ() {
+ return z + 9 / 16.0;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.CHAIN);
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockChest.java b/src/main/java/cn/nukkit/block/BlockChest.java
index e6620669812..0e32cdf1f2d 100644
--- a/src/main/java/cn/nukkit/block/BlockChest.java
+++ b/src/main/java/cn/nukkit/block/BlockChest.java
@@ -18,7 +18,7 @@
import java.util.Map;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockChest extends BlockTransparentMeta implements Faceable {
@@ -66,11 +66,6 @@ public double getMinX() {
return this.x + 0.0625;
}
- @Override
- public double getMinY() {
- return this.y;
- }
-
@Override
public double getMinZ() {
return this.z + 0.0625;
@@ -91,12 +86,10 @@ public double getMaxZ() {
return this.z + 0.9375;
}
-
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
BlockEntityChest chest = null;
- int[] faces = {2, 5, 3, 4};
- this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
for (int side = 2; side <= 5; ++side) {
if ((this.getDamage() == 4 || this.getDamage() == 5) && (side == 4 || side == 5)) {
@@ -114,7 +107,8 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
+
CompoundTag nbt = new CompoundTag("")
.putList(new ListTag<>("Items"))
.putString("id", BlockEntity.CHEST)
@@ -133,11 +127,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- BlockEntityChest blockEntity = (BlockEntityChest) BlockEntity.createBlockEntity(BlockEntity.CHEST, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
-
- if (blockEntity == null) {
- return false;
- }
+ BlockEntityChest blockEntity = (BlockEntityChest) BlockEntity.createBlockEntity(BlockEntity.CHEST, this.getChunk(), nbt);
if (chest != null) {
chest.pairWith(blockEntity);
@@ -161,28 +151,17 @@ public boolean onBreak(Item item) {
@Override
public boolean onActivate(Item item, Player player) {
if (player != null) {
- Block top = up();
- if (!top.isTransparent()) {
+ Block top = this.up();
+ if ((!(top instanceof BlockSlab) && !top.isTransparent()) || (top instanceof BlockSlab && top.isTransparent())) { // avoid issues with the slab hack
return true;
}
BlockEntity t = this.getLevel().getBlockEntity(this);
- BlockEntityChest chest;
- if (t instanceof BlockEntityChest) {
- chest = (BlockEntityChest) t;
- } else {
- CompoundTag nbt = new CompoundTag("")
- .putList(new ListTag<>("Items"))
- .putString("id", BlockEntity.CHEST)
- .putInt("x", (int) this.x)
- .putInt("y", (int) this.y)
- .putInt("z", (int) this.z);
- chest = (BlockEntityChest) BlockEntity.createBlockEntity(BlockEntity.CHEST, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
- if (chest == null) {
- return false;
- }
+ if (!(t instanceof BlockEntityChest)) {
+ return false;
}
+ BlockEntityChest chest = (BlockEntityChest) t;
if (chest.namedTag.contains("Lock") && chest.namedTag.get("Lock") instanceof StringTag) {
if (!chest.namedTag.getString("Lock").equals(item.getCustomName())) {
return true;
@@ -216,11 +195,21 @@ public int getComparatorInputOverride() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public BlockFace getBlockFace() {
return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockChorusFlower.java b/src/main/java/cn/nukkit/block/BlockChorusFlower.java
index 46ad4e2a2c9..68308fd6b19 100644
--- a/src/main/java/cn/nukkit/block/BlockChorusFlower.java
+++ b/src/main/java/cn/nukkit/block/BlockChorusFlower.java
@@ -1,11 +1,30 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.projectile.EntityArrow;
+import cn.nukkit.entity.projectile.EntitySnowball;
+import cn.nukkit.event.block.BlockGrowEvent;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import cn.nukkit.utils.BlockColor;
-public class BlockChorusFlower extends BlockTransparent {
+import java.util.concurrent.ThreadLocalRandom;
+public class BlockChorusFlower extends BlockTransparentMeta {
+
+ // Version 7a3d8a5
public BlockChorusFlower() {
+ super(0);
+ }
+
+ public BlockChorusFlower(int meta) {
+ super(meta);
}
@Override
@@ -25,16 +44,206 @@ public double getHardness() {
@Override
public double getResistance() {
- return 2;
+ return 0.4;
}
@Override
public int getToolType() {
- return ItemTool.TYPE_NONE;
+ return ItemTool.TYPE_AXE;
+ }
+
+ private boolean isPositionValid() {
+ // Chorus flowers must be above end stone or chorus plant, or be above air and horizontally adjacent to exactly one chorus plant.
+ // If these conditions are not met, the block breaks without dropping anything.
+ Block down = down();
+ if (down.getId() == CHORUS_PLANT || down.getId() == END_STONE) {
+ return true;
+ }
+ if (down.getId() != AIR) {
+ return false;
+ }
+ boolean foundPlant = false;
+ for (BlockFace face : BlockFace.Plane.HORIZONTAL) {
+ Block side = getSide(face);
+ if (side.getId() == CHORUS_PLANT) {
+ if (foundPlant) {
+ return false;
+ }
+ foundPlant = true;
+ }
+ }
+
+ return foundPlant;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!isPositionValid()) {
+ this.getLevel().scheduleUpdate(this, 1);
+ return type;
+ }
+ } else if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.getLevel().useBreakOn(this, null, null, true);
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ // Check limit
+ Block up;
+ if (this.y < level.getMaxBlockY() && (up = this.up()).getId() == AIR) {
+ if (!isFullyAged()) {
+ boolean growUp = false; // Grow upward?
+ boolean ground = false; // Is on the ground directly?
+ Block down = this.down();
+ if (down.getId() == AIR || down.getId() == END_STONE) {
+ growUp = true;
+ } else if (down.getId() == CHORUS_PLANT) {
+ int height = 1;
+ for (int y = 2; y < 6; y++) {
+ Block downY = this.down(y);
+ if (downY.getId() == CHORUS_PLANT) {
+ height++;
+ } else {
+ if (downY.getId() == END_STONE) {
+ ground = true;
+ }
+ break;
+ }
+ }
+
+ if (height < 2 || height <= ThreadLocalRandom.current().nextInt(ground ? 5 : 4)) {
+ growUp = true;
+ }
+ }
+
+ // Grow Upward
+ if (growUp && up.up().getId() == AIR && isHorizontalAir(up)) {
+ BlockChorusFlower block = (BlockChorusFlower) this.clone();
+ block.y = this.y + 1;
+ BlockGrowEvent ev = new BlockGrowEvent(this, block);
+ Server.getInstance().getPluginManager().callEvent(ev);
+
+ if (!ev.isCancelled()) {
+ this.getLevel().setBlock(this, Block.get(CHORUS_PLANT));
+ this.getLevel().setBlock(block, ev.getNewState());
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_CHORUSGROW);
+ } else {
+ return Level.BLOCK_UPDATE_RANDOM;
+ }
+ // Grow Horizontally
+ } else if (!isFullyAged()) {
+ for (int i = 0; i < ThreadLocalRandom.current().nextInt(ground ? 5 : 4); i++) {
+ BlockFace face = BlockFace.Plane.HORIZONTAL.random();
+ Block check = this.getSide(face);
+ if (check.getId() == AIR && check.down().getId() == AIR && isHorizontalAirExcept(check, face.getOpposite())) {
+ BlockChorusFlower block = (BlockChorusFlower) this.clone();
+ block.x = check.x;
+ block.y = check.y;
+ block.z = check.z;
+ block.setAge(getAge() + 1);
+ BlockGrowEvent ev = new BlockGrowEvent(this, block);
+ Server.getInstance().getPluginManager().callEvent(ev);
+
+ if (!ev.isCancelled()) {
+ this.getLevel().setBlock(this, Block.get(CHORUS_PLANT));
+ this.getLevel().setBlock(block, ev.getNewState());
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_CHORUSGROW);
+ } else {
+ return Level.BLOCK_UPDATE_RANDOM;
+ }
+ }
+ }
+ // Death
+ } else {
+ BlockChorusFlower block = (BlockChorusFlower) this.clone();
+ block.setAge(getMaxAge());
+ BlockGrowEvent ev = new BlockGrowEvent(this, block);
+ Server.getInstance().getPluginManager().callEvent(ev);
+
+ if (!ev.isCancelled()) {
+ this.getLevel().setBlock(block, ev.getNewState());
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_CHORUSDEATH);
+ } else {
+ return Level.BLOCK_UPDATE_RANDOM;
+ }
+ }
+ }
+ } else {
+ return Level.BLOCK_UPDATE_RANDOM;
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (!isPositionValid()) {
+ return false;
+ }
+ return super.place(item, block, target, face, fx, fy, fz, player);
}
@Override
public Item[] getDrops(Item item) {
return new Item[]{this.toItem()};
}
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (entity instanceof EntityArrow || entity instanceof EntitySnowball) {
+ entity.close();
+ this.getLevel().useBreakOn(this);
+ }
+ }
+
+ public int getMaxAge() {
+ return 5;
+ }
+
+ public int getAge() {
+ return getDamage();
+ }
+
+ public void setAge(int age) {
+ this.setDamage(age);
+ }
+
+ public boolean isFullyAged() {
+ return getAge() >= getMaxAge();
+ }
+
+ private boolean isHorizontalAir(Block block) {
+ for (BlockFace face : BlockFace.Plane.HORIZONTAL) {
+ if (block.getSide(face).getId() != AIR) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isHorizontalAirExcept(Block block, BlockFace except) {
+ for (BlockFace face : BlockFace.Plane.HORIZONTAL) {
+ if (face != except) {
+ if (block.getSide(face).getId() != AIR) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.PURPLE_BLOCK_COLOR;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockChorusPlant.java b/src/main/java/cn/nukkit/block/BlockChorusPlant.java
index f4363f5ecf3..9424205aeaf 100644
--- a/src/main/java/cn/nukkit/block/BlockChorusPlant.java
+++ b/src/main/java/cn/nukkit/block/BlockChorusPlant.java
@@ -1,17 +1,16 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemID;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
public class BlockChorusPlant extends BlockTransparent {
- public BlockChorusPlant() {
- }
-
@Override
public int getId() {
return CHORUS_PLANT;
@@ -29,21 +28,76 @@ public double getHardness() {
@Override
public double getResistance() {
- return 2;
+ return 0.4;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (!isSupportValid()) {
+ return false;
+ }
+ return super.place(item, block, target, face, fx, fy, fz, player);
}
@Override
public int getToolType() {
- return ItemTool.TYPE_NONE;
+ return ItemTool.TYPE_AXE;
+ }
+
+ private boolean isSupportValid() {
+ // Must be on end stone or chorus plant, or be above air and horizontally adjacent to exactly one chorus plant, otherwise breaks without dropping anything
+ int down = down().getId();
+ if (down == CHORUS_PLANT || down == END_STONE) {
+ return true;
+ }
+
+ if (down != AIR) {
+ return false;
+ }
+
+ boolean plantFound = false;
+ for (BlockFace face : BlockFace.Plane.HORIZONTAL) {
+ if (getSide(face).getId() == CHORUS_PLANT) {
+ if (plantFound) {
+ return false;
+ }
+ plantFound = true;
+ }
+ }
+
+ return plantFound;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!isSupportValid()) {
+ this.getLevel().scheduleUpdate(this, 1);
+ return type;
+ }
+ } else if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.getLevel().useBreakOn(this, null, null, true);
+ return type;
+ }
+
+ return 0;
}
@Override
public Item[] getDrops(Item item) {
- return ThreadLocalRandom.current().nextBoolean() ? new Item[]{Item.get(ItemID.CHORUS_FRUIT, 0, 1)} : new Item[0];
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+ return Utils.rand() ? new Item[]{Item.get(Item.CHORUS_FRUIT, 0, 1)} : new Item[0];
}
@Override
public BlockColor getColor() {
return BlockColor.PURPLE_BLOCK_COLOR;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockClay.java b/src/main/java/cn/nukkit/block/BlockClay.java
index b2cdee1d549..e0661d910cb 100644
--- a/src/main/java/cn/nukkit/block/BlockClay.java
+++ b/src/main/java/cn/nukkit/block/BlockClay.java
@@ -1,8 +1,8 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemClay;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.utils.BlockColor;
/**
@@ -10,9 +10,6 @@
*/
public class BlockClay extends BlockSolid {
- public BlockClay() {
- }
-
@Override
public double getHardness() {
return 0.6;
@@ -40,8 +37,11 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
return new Item[]{
- new ItemClay(0, 4)
+ Item.get(Item.CLAY, 0, 4)
};
}
diff --git a/src/main/java/cn/nukkit/block/BlockCoal.java b/src/main/java/cn/nukkit/block/BlockCoal.java
index 48fc76c6b6b..9df6085b51c 100644
--- a/src/main/java/cn/nukkit/block/BlockCoal.java
+++ b/src/main/java/cn/nukkit/block/BlockCoal.java
@@ -9,8 +9,6 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockCoal extends BlockSolid {
- public BlockCoal() {
- }
@Override
public int getId() {
@@ -49,7 +47,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockCobblestone.java b/src/main/java/cn/nukkit/block/BlockCobblestone.java
index 223f38def5b..bdf270cb198 100644
--- a/src/main/java/cn/nukkit/block/BlockCobblestone.java
+++ b/src/main/java/cn/nukkit/block/BlockCobblestone.java
@@ -4,14 +4,11 @@
import cn.nukkit.item.ItemTool;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockCobblestone extends BlockSolid {
- public BlockCobblestone() {
- }
-
@Override
public int getId() {
return COBBLESTONE;
@@ -39,7 +36,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockCobweb.java b/src/main/java/cn/nukkit/block/BlockCobweb.java
index 16ca8a689d8..cca7d8d655c 100644
--- a/src/main/java/cn/nukkit/block/BlockCobweb.java
+++ b/src/main/java/cn/nukkit/block/BlockCobweb.java
@@ -2,8 +2,8 @@
import cn.nukkit.entity.Entity;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemString;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.utils.BlockColor;
/**
@@ -11,6 +11,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockCobweb extends BlockFlowable {
+
public BlockCobweb() {
this(0);
}
@@ -44,6 +45,11 @@ public int getToolType() {
return ItemTool.TYPE_SWORD;
}
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
@Override
public void onEntityCollide(Entity entity) {
entity.resetFallDistance();
@@ -57,7 +63,7 @@ public Item[] getDrops(Item item) {
};
} else if (item.isSword()) {
return new Item[]{
- new ItemString()
+ Item.get(Item.STRING)
};
} else {
return new Item[0];
@@ -73,4 +79,19 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return this;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockCocoa.java b/src/main/java/cn/nukkit/block/BlockCocoa.java
index 66faef60934..e97b43853de 100644
--- a/src/main/java/cn/nukkit/block/BlockCocoa.java
+++ b/src/main/java/cn/nukkit/block/BlockCocoa.java
@@ -13,19 +13,30 @@
import cn.nukkit.math.SimpleAxisAlignedBB;
import cn.nukkit.utils.DyeColor;
import cn.nukkit.utils.Faceable;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
* Created by CreeperFace on 27. 10. 2016.
*/
public class BlockCocoa extends BlockTransparentMeta implements Faceable {
- protected static final AxisAlignedBB[] EAST = new SimpleAxisAlignedBB[]{new SimpleAxisAlignedBB(0.6875D, 0.4375D, 0.375D, 0.9375D, 0.75D, 0.625D), new SimpleAxisAlignedBB(0.5625D, 0.3125D, 0.3125D, 0.9375D, 0.75D, 0.6875D), new SimpleAxisAlignedBB(0.5625D, 0.3125D, 0.3125D, 0.9375D, 0.75D, 0.6875D)};
- protected static final AxisAlignedBB[] WEST = new SimpleAxisAlignedBB[]{new SimpleAxisAlignedBB(0.0625D, 0.4375D, 0.375D, 0.3125D, 0.75D, 0.625D), new SimpleAxisAlignedBB(0.0625D, 0.3125D, 0.3125D, 0.4375D, 0.75D, 0.6875D), new SimpleAxisAlignedBB(0.0625D, 0.3125D, 0.3125D, 0.4375D, 0.75D, 0.6875D)};
- protected static final AxisAlignedBB[] NORTH = new SimpleAxisAlignedBB[]{new SimpleAxisAlignedBB(0.375D, 0.4375D, 0.0625D, 0.625D, 0.75D, 0.3125D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.0625D, 0.6875D, 0.75D, 0.4375D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.0625D, 0.6875D, 0.75D, 0.4375D)};
- protected static final AxisAlignedBB[] SOUTH = new SimpleAxisAlignedBB[]{new SimpleAxisAlignedBB(0.375D, 0.4375D, 0.6875D, 0.625D, 0.75D, 0.9375D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.5625D, 0.6875D, 0.75D, 0.9375D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.5625D, 0.6875D, 0.75D, 0.9375D)};
- protected static final AxisAlignedBB[] ALL = new AxisAlignedBB[12];
+ protected static final AxisAlignedBB[] EAST = {new SimpleAxisAlignedBB(0.6875D, 0.4375D, 0.375D, 0.9375D, 0.75D, 0.625D), new SimpleAxisAlignedBB(0.5625D, 0.3125D, 0.3125D, 0.9375D, 0.75D, 0.6875D), new SimpleAxisAlignedBB(0.5625D, 0.3125D, 0.3125D, 0.9375D, 0.75D, 0.6875D)};
+ protected static final AxisAlignedBB[] WEST = {new SimpleAxisAlignedBB(0.0625D, 0.4375D, 0.375D, 0.3125D, 0.75D, 0.625D), new SimpleAxisAlignedBB(0.0625D, 0.3125D, 0.3125D, 0.4375D, 0.75D, 0.6875D), new SimpleAxisAlignedBB(0.0625D, 0.3125D, 0.3125D, 0.4375D, 0.75D, 0.6875D)};
+ protected static final AxisAlignedBB[] NORTH = {new SimpleAxisAlignedBB(0.375D, 0.4375D, 0.0625D, 0.625D, 0.75D, 0.3125D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.0625D, 0.6875D, 0.75D, 0.4375D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.0625D, 0.6875D, 0.75D, 0.4375D)};
+ protected static final AxisAlignedBB[] SOUTH = {new SimpleAxisAlignedBB(0.375D, 0.4375D, 0.6875D, 0.625D, 0.75D, 0.9375D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.5625D, 0.6875D, 0.75D, 0.9375D), new SimpleAxisAlignedBB(0.3125D, 0.3125D, 0.5625D, 0.6875D, 0.75D, 0.9375D)};
+
+ private static final short[] faces = {
+ 0,
+ 0,
+ 0,
+ 2,
+ 3,
+ 1,
+ };
+
+ private static final short[] faces2 = {
+ 3, 4, 2, 5, 3, 4, 2, 5, 3, 4, 2, 5
+ };
public BlockCocoa() {
this(0);
@@ -46,52 +57,15 @@ public String getName() {
}
@Override
- public void setDamage(int meta) {
- super.setDamage(meta);
- }
-
-
- @Override
- public double getMinX() {
- return this.x + getRelativeBoundingBox().getMinX();
- }
-
- @Override
- public double getMaxX() {
- return this.x + getRelativeBoundingBox().getMaxX();
- }
-
- @Override
- public double getMinY() {
- return this.y + getRelativeBoundingBox().getMinY();
- }
-
- @Override
- public double getMaxY() {
- return this.y + getRelativeBoundingBox().getMaxY();
- }
-
- @Override
- public double getMinZ() {
- return this.z + getRelativeBoundingBox().getMinZ();
- }
-
- @Override
- public double getMaxZ() {
- return this.z + getRelativeBoundingBox().getMaxZ();
- }
+ protected AxisAlignedBB recalculateBoundingBox() {
+ AxisAlignedBB[] bbs;
- private AxisAlignedBB getRelativeBoundingBox() {
int damage = this.getDamage();
if (damage > 11) {
- this.setDamage(damage = 11);
+ damage = 11;
}
- AxisAlignedBB boundingBox = ALL[damage];
- if (boundingBox != null) return boundingBox;
-
- AxisAlignedBB[] bbs;
- switch (getDamage()) {
+ switch (damage) {
case 1:
case 5:
case 9:
@@ -112,22 +86,13 @@ private AxisAlignedBB getRelativeBoundingBox() {
break;
}
- return ALL[damage] = bbs[this.getDamage() >> 2];
+ return bbs[(damage >> 2)].getOffsetBoundingBox(x, y, z);
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (target.getId() == Block.WOOD && (target.getDamage() & 0x03) == BlockWood.JUNGLE) {
if (face != BlockFace.DOWN && face != BlockFace.UP) {
- int[] faces = new int[]{
- 0,
- 0,
- 0,
- 2,
- 3,
- 1,
- };
-
this.setDamage(faces[face.getIndex()]);
this.level.setBlock(block, this, true, true);
return true;
@@ -139,19 +104,15 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
- int[] faces = new int[]{
- 3, 4, 2, 5, 3, 4, 2, 5, 3, 4, 2, 5
- };
-
- Block side = this.getSide(BlockFace.fromIndex(faces[this.getDamage()]));
+ Block side = this.getSide(BlockFace.fromIndex(faces2[this.getDamage()]));
- if (side.getId() != Block.WOOD && side.getDamage() != BlockWood.JUNGLE) {
+ if (side.getId() != Block.WOOD && (side.getDamage() & 0x03) != BlockWood.JUNGLE) {
this.getLevel().useBreakOn(this);
return Level.BLOCK_UPDATE_NORMAL;
}
} else if (type == Level.BLOCK_UPDATE_RANDOM) {
- if (ThreadLocalRandom.current().nextInt(2) == 1) {
- if (this.getDamage() / 4 < 2) {
+ if (Utils.random.nextInt(2) == 1) {
+ if (this.getDamage() >> 2 < 2) {
BlockCocoa block = (BlockCocoa) this.clone();
block.setDamage(block.getDamage() + 4);
BlockGrowEvent ev = new BlockGrowEvent(this, block);
@@ -178,9 +139,9 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.getId() == Item.DYE && item.getDamage() == 0x0f) {
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
Block block = this.clone();
- if (this.getDamage() / 4 < 2) {
+ if (this.getDamage() >> 2 < 2) {
block.setDamage(block.getDamage() + 4);
BlockGrowEvent ev = new BlockGrowEvent(this, block);
Server.getInstance().getPluginManager().callEvent(ev);
@@ -188,10 +149,11 @@ public boolean onActivate(Item item, Player player) {
if (ev.isCancelled()) {
return false;
}
+
this.getLevel().setBlock(this, ev.getNewState(), true, true);
this.level.addParticle(new BoneMealParticle(this));
- if (player != null && (player.gamemode & 0x01) == 0) {
+ if (player != null && !player.isCreative()) {
item.count--;
}
}
@@ -219,24 +181,39 @@ public int getToolType() {
@Override
public Item toItem() {
- return new ItemDye(DyeColor.BROWN.getDyeData());
+ return Item.get(Item.DYE, DyeColor.BROWN.getDyeData());
}
@Override
public Item[] getDrops(Item item) {
if (this.getDamage() >= 8) {
return new Item[]{
- new ItemDye(3, 3)
+ Item.get(Item.DYE, 3, Utils.rand(2, 3))
};
} else {
return new Item[]{
- new ItemDye(3, 1)
+ Item.get(Item.DYE, 3, 1)
};
}
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockCommandBlock.java b/src/main/java/cn/nukkit/block/BlockCommandBlock.java
new file mode 100644
index 00000000000..4f727d3d0b5
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCommandBlock.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockCommandBlock extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return COMMAND_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public String getName() {
+ return "Command Block";
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCommandBlockChain.java b/src/main/java/cn/nukkit/block/BlockCommandBlockChain.java
new file mode 100644
index 00000000000..5f11fc8c5f9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCommandBlockChain.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockCommandBlockChain extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return CHAIN_COMMAND_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public String getName() {
+ return "Chain Command Block";
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCommandBlockRepeating.java b/src/main/java/cn/nukkit/block/BlockCommandBlockRepeating.java
new file mode 100644
index 00000000000..179badce4ac
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCommandBlockRepeating.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockCommandBlockRepeating extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return REPEATING_COMMAND_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public String getName() {
+ return "Repeating Command Block";
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockComposter.java b/src/main/java/cn/nukkit/block/BlockComposter.java
new file mode 100644
index 00000000000..de45220f3e8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockComposter.java
@@ -0,0 +1,214 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.ComposterEmptyEvent;
+import cn.nukkit.event.block.ComposterFillEvent;
+import cn.nukkit.item.*;
+import cn.nukkit.level.Sound;
+import cn.nukkit.utils.DyeColor;
+import cn.nukkit.utils.Utils;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
+
+public class BlockComposter extends BlockTransparentMeta implements ItemID {
+
+ private static final Int2IntOpenHashMap items = new Int2IntOpenHashMap();
+
+ static {
+ registerItems(30, KELP, BEETROOT_SEEDS, DRIED_KELP, MELON_SEEDS, PUMPKIN_SEEDS, SWEET_BERRIES, WHEAT_SEEDS);
+ registerItems(50, MELON_SLICE, SUGAR_CANE);
+ registerItems(65, APPLE, BEETROOT, CARROT, COCOA, POTATO, WHEAT);
+ registerItems(85, BAKED_POTATOES, BREAD, COOKIE);
+ registerItems(100, CAKE, PUMPKIN_PIE);
+ registerBlocks(30, BLOCK_KELP, LEAVES, LEAVES2, SAPLINGS, SEAGRASS, SWEET_BERRY_BUSH);
+ registerBlocks(50, GRASS, CACTUS, DRIED_KELP_BLOCK, VINES, NETHER_SPROUTS_BLOCK);
+ registerBlocks(65, DANDELION, RED_FLOWER, DOUBLE_PLANT, WITHER_ROSE, LILY_PAD, MELON_BLOCK, PUMPKIN, CARVED_PUMPKIN, SEA_PICKLE, BROWN_MUSHROOM, RED_MUSHROOM, SHROOMLIGHT, CRIMSON_FUNGUS, WARPED_FUNGUS);
+ registerBlocks(85, HAY_BALE, BROWN_MUSHROOM_BLOCK, RED_MUSHROOM_BLOCK, MUSHROOM_STEW, BLOCK_NETHER_WART_BLOCK, WARPED_WART_BLOCK);
+ registerBlocks(100, CAKE_BLOCK);
+ registerBlock(50, TALL_GRASS, 0);
+ registerBlock(50, TALL_GRASS, 1);
+ registerBlock(65, TALL_GRASS, 2);
+ registerBlock(65, TALL_GRASS, 3);
+ }
+
+ public BlockComposter() {
+ this(0);
+ }
+
+ public BlockComposter(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return COMPOSTER;
+ }
+
+ @Override
+ public String getName() {
+ return "Composter";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.6;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public boolean hasComparatorInputOverride() {
+ return true;
+ }
+
+ @Override
+ public int getComparatorInputOverride() {
+ return getDamage();
+ }
+
+ public boolean incrementLevel() {
+ int fillLevel = getDamage() + 1;
+ setDamage(fillLevel);
+ this.level.setBlock(this, this, true, true);
+ return fillLevel == 8;
+ }
+
+ public boolean isFull() {
+ return getDamage() == 8;
+ }
+
+ public boolean isEmpty() {
+ return getDamage() == 0;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getCount() <= 0 || item.getId() == Item.AIR) {
+ return false;
+ }
+
+ if (isFull()) {
+ ComposterEmptyEvent event = new ComposterEmptyEvent(this, player, item, new ItemDye(DyeColor.WHITE), 0);
+ this.level.getServer().getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ this.setDamage(event.getNewLevel());
+ this.level.setBlock(this, this, true, true);
+ this.level.dropItem(add(0.5, 0.85, 0.5), event.getDrop());
+ this.level.addSound(add(0.5 , 0.5, 0.5), Sound.BLOCK_COMPOSTER_EMPTY);
+ }
+ return true;
+ }
+
+ int chance = getChance(item);
+ if (chance <= 0) {
+ return false;
+ }
+
+ boolean success = Utils.random.nextInt(100) < chance;
+ ComposterFillEvent event = new ComposterFillEvent(this, player, item, chance, success);
+ this.level.getServer().getPluginManager().callEvent(event);
+
+ if (event.isCancelled()) {
+ return true;
+ }
+
+ if (player != null && !player.isCreative()) {
+ item.setCount(item.getCount() - 1);
+ }
+
+ if (event.isSuccess()) {
+ if (incrementLevel()) {
+ level.addSound(this.add(0.5, 0.5, 0.5), Sound.BLOCK_COMPOSTER_READY);
+ } else {
+ level.addSound(this.add(0.5, 0.5, 0.5), Sound.BLOCK_COMPOSTER_FILL_SUCCESS);
+ }
+ } else {
+ level.addSound(this.add(0.5, 0.5, 0.5), Sound.BLOCK_COMPOSTER_FILL);
+ }
+
+ return true;
+ }
+
+ public Item empty() {
+ return empty(null, null);
+ }
+
+ public Item empty(Player player) {
+ return this.empty(null, player);
+ }
+
+ public Item empty(Item item, Player player) {
+ ComposterEmptyEvent event = new ComposterEmptyEvent(this, player, item, new ItemDye(DyeColor.WHITE), 0);
+ this.level.getServer().getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ this.setDamage(event.getNewLevel());
+ this.level.setBlock(this, this, true, true);
+ if (item != null) {
+ this.level.dropItem(add(0.5, 0.85, 0.5), event.getDrop());
+ }
+ this.level.addSound(add(0.5 , 0.5, 0.5), Sound.BLOCK_COMPOSTER_EMPTY);
+ return event.getDrop();
+ }
+ return null;
+ }
+
+ public static void registerItem(int chance, int itemId) {
+ registerItem(chance, itemId, 0);
+ }
+
+ public static void registerItem(int chance, int itemId, int meta) {
+ items.put(itemId << 6 | meta & 0x3F, chance);
+ }
+
+ public static void registerItems(int chance, int... itemIds) {
+ for (int itemId : itemIds) {
+ registerItem(chance, itemId, 0);
+ }
+ }
+
+ public static void registerBlocks(int chance, int... blockIds) {
+ for (int blockId : blockIds) {
+ registerBlock(chance, blockId, 0);
+ }
+ }
+
+ public static void registerBlock(int chance, int blockId) {
+ registerBlock(chance, blockId, 0);
+ }
+
+ public static void registerBlock(int chance, int blockId, int meta) {
+ if (blockId > 255) {
+ blockId = 255 - blockId;
+ }
+ registerItem(chance, blockId, meta);
+ }
+
+ public static void register(int chance, Item item) {
+ registerItem(chance, item.getId(), item.getDamage());
+ }
+
+ public static int getChance(Item item) {
+ int chance = items.get(item.getId() << 6 | item.getDamage());
+ if (chance == 0) {
+ chance = items.get(item.getId() << 6);
+ }
+ return chance;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockConcretePowder.java b/src/main/java/cn/nukkit/block/BlockConcretePowder.java
index 20811ae3608..87e4fbb0671 100644
--- a/src/main/java/cn/nukkit/block/BlockConcretePowder.java
+++ b/src/main/java/cn/nukkit/block/BlockConcretePowder.java
@@ -11,30 +11,19 @@
/**
* Created by CreeperFace on 2.6.2017.
*/
-public class BlockConcretePowder extends BlockFallable {
- private int meta;
+public class BlockConcretePowder extends BlockFallableMeta {
public BlockConcretePowder() {
- this(0);
+ super(0);
}
public BlockConcretePowder(int meta) {
- this.meta = meta;
+ super(meta);
}
@Override
public int getFullId() {
- return (getId() << 4) + getDamage();
- }
-
- @Override
- public final int getDamage() {
- return this.meta;
- }
-
- @Override
- public final void setDamage(int meta) {
- this.meta = meta;
+ return (this.getId() << Block.DATA_BITS) + getDamage();
}
@Override
@@ -70,7 +59,7 @@ public int onUpdate(int type) {
for (int side = 1; side <= 5; side++) {
Block block = this.getSide(BlockFace.fromIndex(side));
if (block.getId() == Block.WATER || block.getId() == Block.STILL_WATER) {
- this.level.setBlock(this, Block.get(Block.CONCRETE, this.meta), true, true);
+ this.level.setBlock(this, Block.get(Block.CONCRETE, this.getDamage()), true, true);
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockConduit.java b/src/main/java/cn/nukkit/block/BlockConduit.java
new file mode 100644
index 00000000000..41c3c4c7ce5
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockConduit.java
@@ -0,0 +1,59 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockConduit extends BlockSolidMeta {
+
+ public BlockConduit() {
+ this(0);
+ }
+
+ public BlockConduit(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Conduit";
+ }
+
+ @Override
+ public int getId() {
+ return CONDUIT;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 15;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean alwaysDropsOnExplosion() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopper.java b/src/main/java/cn/nukkit/block/BlockCopper.java
new file mode 100644
index 00000000000..ef174b80ad8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopper.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopper extends BlockCopperBase {
+
+ public BlockCopper() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Block of Copper";
+ }
+
+ @Override
+ public int getId() {
+ return COPPER_BLOCK;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.UNAFFECTED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperBase.java b/src/main/java/cn/nukkit/block/BlockCopperBase.java
new file mode 100644
index 00000000000..1e28a64571a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperBase.java
@@ -0,0 +1,98 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+
+public abstract class BlockCopperBase extends BlockSolid implements Oxidizable, Waxable {
+
+ public BlockCopperBase() {
+ // Does nothing
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_STONE;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ return Waxable.super.onActivate(item, player)
+ || Oxidizable.super.onActivate(item, player);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ return Oxidizable.super.onUpdate(type);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Block getStateWithOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ return Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel), this.getDamage());
+ }
+
+ @Override
+ public boolean setOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ if (this.getOxidizationLevel().equals(oxidizationLevel)) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel)));
+ }
+
+ @Override
+ public boolean setWaxed(boolean waxed) {
+ if (this.isWaxed() == waxed) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(getCopperId(waxed, getOxidizationLevel())));
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return false;
+ }
+
+ protected int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel) {
+ if (oxidizationLevel == null) {
+ return this.getId();
+ }
+ switch (oxidizationLevel) {
+ case UNAFFECTED:
+ return waxed ? WAXED_COPPER : COPPER_BLOCK;
+ case EXPOSED:
+ return waxed ? WAXED_EXPOSED_COPPER : EXPOSED_COPPER;
+ case WEATHERED:
+ return waxed ? WAXED_WEATHERED_COPPER : WEATHERED_COPPER;
+ case OXIDIZED:
+ return waxed ? WAXED_OXIDIZED_COPPER : OXIDIZED_COPPER;
+ default:
+ return this.getId();
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCut.java b/src/main/java/cn/nukkit/block/BlockCopperCut.java
new file mode 100644
index 00000000000..8dbcb53b40b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCut.java
@@ -0,0 +1,51 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopperCut extends BlockCopperBase {
+
+ public BlockCopperCut() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return CUT_COPPER;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.UNAFFECTED;
+ }
+
+ @Override
+ protected int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel) {
+ if (oxidizationLevel == null) {
+ return this.getId();
+ }
+
+ switch (oxidizationLevel) {
+ case UNAFFECTED:
+ return waxed ? WAXED_CUT_COPPER : CUT_COPPER;
+ case EXPOSED:
+ return waxed ? WAXED_EXPOSED_CUT_COPPER : EXPOSED_CUT_COPPER;
+ case WEATHERED:
+ return waxed ? WAXED_WEATHERED_CUT_COPPER : WEATHERED_CUT_COPPER;
+ case OXIDIZED:
+ return waxed ? WAXED_OXIDIZED_CUT_COPPER : OXIDIZED_CUT_COPPER;
+ default:
+ return this.getId();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCutExposed.java b/src/main/java/cn/nukkit/block/BlockCopperCutExposed.java
new file mode 100644
index 00000000000..b77eefd1b45
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCutExposed.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopperCutExposed extends BlockCopperCut {
+
+ public BlockCopperCutExposed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Exposed Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return EXPOSED_CUT_COPPER;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.LIGHT_GRAY_TERRACOTA_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.EXPOSED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCutExposedWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperCutExposedWaxed.java
new file mode 100644
index 00000000000..a36ab3d4bde
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCutExposedWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperCutExposedWaxed extends BlockCopperCutExposed {
+
+ public BlockCopperCutExposedWaxed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Exposed Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_EXPOSED_CUT_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCutOxidized.java b/src/main/java/cn/nukkit/block/BlockCopperCutOxidized.java
new file mode 100644
index 00000000000..790a6435c11
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCutOxidized.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopperCutOxidized extends BlockCopperCut {
+
+ public BlockCopperCutOxidized() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Oxidized Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return OXIDIZED_CUT_COPPER;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_NYLIUM_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.OXIDIZED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCutOxidizedWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperCutOxidizedWaxed.java
new file mode 100644
index 00000000000..13a29499f6c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCutOxidizedWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperCutOxidizedWaxed extends BlockCopperCutOxidized {
+
+ public BlockCopperCutOxidizedWaxed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Oxidized Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_OXIDIZED_CUT_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCutWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperCutWaxed.java
new file mode 100644
index 00000000000..52c8b0d2348
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCutWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperCutWaxed extends BlockCopperCut {
+
+ public BlockCopperCutWaxed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_CUT_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCutWeathered.java b/src/main/java/cn/nukkit/block/BlockCopperCutWeathered.java
new file mode 100644
index 00000000000..e8885c59d12
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCutWeathered.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopperCutWeathered extends BlockCopperCut {
+
+ public BlockCopperCutWeathered() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Weathered Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WEATHERED_CUT_COPPER;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.WEATHERED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperCutWeatheredWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperCutWeatheredWaxed.java
new file mode 100644
index 00000000000..0ffd40655b5
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperCutWeatheredWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperCutWeatheredWaxed extends BlockCopperCutWeathered {
+
+ public BlockCopperCutWeatheredWaxed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Weathered Cut Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_WEATHERED_CUT_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperExposed.java b/src/main/java/cn/nukkit/block/BlockCopperExposed.java
new file mode 100644
index 00000000000..4a6568800b2
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperExposed.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopperExposed extends BlockCopper {
+
+ public BlockCopperExposed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Exposed Copper";
+ }
+
+ @Override
+ public int getId() {
+ return EXPOSED_COPPER;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.LIGHT_GRAY_TERRACOTA_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.EXPOSED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperExposedWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperExposedWaxed.java
new file mode 100644
index 00000000000..7b24a095109
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperExposedWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperExposedWaxed extends BlockCopperExposed {
+
+ public BlockCopperExposedWaxed( ) {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Exposed Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_EXPOSED_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperOxidized.java b/src/main/java/cn/nukkit/block/BlockCopperOxidized.java
new file mode 100644
index 00000000000..2df9499a8a3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperOxidized.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopperOxidized extends BlockCopper {
+
+ public BlockCopperOxidized() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Oxidized Copper";
+ }
+
+ @Override
+ public int getId() {
+ return OXIDIZED_COPPER;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_NYLIUM_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.OXIDIZED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperOxidizedWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperOxidizedWaxed.java
new file mode 100644
index 00000000000..35bf2facce3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperOxidizedWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperOxidizedWaxed extends BlockCopperOxidized {
+
+ public BlockCopperOxidizedWaxed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Oxidized Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_OXIDIZED_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperWaxed.java
new file mode 100644
index 00000000000..b28423eddf1
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperWaxed extends BlockCopper {
+
+ public BlockCopperWaxed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Block of Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperWeathered.java b/src/main/java/cn/nukkit/block/BlockCopperWeathered.java
new file mode 100644
index 00000000000..05a62e1e7a1
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperWeathered.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCopperWeathered extends BlockCopper {
+
+ public BlockCopperWeathered() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Weathered Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WEATHERED_COPPER;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.WEATHERED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCopperWeatheredWaxed.java b/src/main/java/cn/nukkit/block/BlockCopperWeatheredWaxed.java
new file mode 100644
index 00000000000..dc381145457
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCopperWeatheredWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockCopperWeatheredWaxed extends BlockCopperWeathered {
+
+ public BlockCopperWeatheredWaxed() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Waxed Weathered Copper";
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_WEATHERED_COPPER;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCoral.java b/src/main/java/cn/nukkit/block/BlockCoral.java
new file mode 100644
index 00000000000..15da506b542
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoral.java
@@ -0,0 +1,98 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+
+public class BlockCoral extends BlockTransparentMeta {
+
+ public static final int TYPE_TUBE = 0;
+ public static final int TYPE_BRAIN = 1;
+ public static final int TYPE_BUBBLE = 2;
+ public static final int TYPE_FIRE = 3;
+ public static final int TYPE_HORN = 4;
+
+ private static final String[] names = new String[] {
+ "Tube Coral",
+ "Brain Coral",
+ "Bubble Coral",
+ "Fire Coral",
+ "Horn Coral"
+ };
+
+ public BlockCoral() {
+ this(0);
+ }
+
+ public BlockCoral(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ int variant = this.getDamage() & 0x7;
+ if (variant >= names.length) {
+ return names[0];
+ }
+ return names[variant];
+ }
+
+ @Override
+ public int getId() {
+ return CORAL;
+ }
+
+ public double getHardness() {
+ return 0;
+ }
+
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (this.down().isTransparent()) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (this.down().isTransparent()) {
+ return false;
+ }
+ if (this.getLevel().setBlock(this, this, true, true)) {
+ if (block instanceof BlockWater) {
+ this.getLevel().setBlock((int) this.x, (int) this.y, (int) this.z, Block.LAYER_WATERLOGGED, Block.get(Block.STILL_WATER), true, true);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.getEnchantment(Enchantment.ID_SILK_TOUCH) != null) {
+ return super.getDrops(item);
+ } else {
+ return new Item[0];
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCoralBlock.java b/src/main/java/cn/nukkit/block/BlockCoralBlock.java
new file mode 100644
index 00000000000..25443c1009c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralBlock.java
@@ -0,0 +1,123 @@
+package cn.nukkit.block;
+
+import cn.nukkit.event.block.BlockFadeEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockCoralBlock extends BlockSolidMeta {
+
+ private static final String[] names = new String[] {
+ "Tube Coral Block",
+ "Brain Coral Block",
+ "Bubble Coral Block",
+ "Fire Coral Block",
+ "Horn Coral Block",
+ };
+
+ public BlockCoralBlock() {
+ this(0);
+ }
+
+ public BlockCoralBlock(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!this.isDead()) {
+ this.getLevel().scheduleUpdate(this, 60 + ThreadLocalRandom.current().nextInt(40));
+ }
+ return type;
+ }
+
+ if (type != Level.BLOCK_UPDATE_SCHEDULED) {
+ return 0;
+ }
+
+ if (this.isDead()) {
+ return type;
+ }
+
+ for (BlockFace face : BlockFace.values()) {
+ if (this.getSide(BlockLayer.NORMAL, face) instanceof BlockWater || this.getSide(BlockLayer.WATERLOGGED, face) instanceof BlockWater
+ || this.getSide(BlockLayer.NORMAL, face) instanceof BlockIceFrosted || this.getSide(BlockLayer.WATERLOGGED, face) instanceof BlockIceFrosted) {
+ return type;
+ }
+ }
+
+ BlockFadeEvent event = new BlockFadeEvent(this, Block.get(CORAL_BLOCK, this.getDamage() | 0x8));
+ if (!event.isCancelled()) {
+ this.setDead(true);
+ this.getLevel().setBlock(this, event.getNewState(), true, true);
+ }
+ return type;
+ }
+
+ public double getHardness() {
+ return 1.5;
+ }
+
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public String getName() {
+ int variant = this.getDamage() & 0x7;
+ String name;
+ if (variant >= names.length) {
+ name = names[0];
+ } else {
+ name = names[variant];
+ }
+ return this.isDead() ? "Dead" + name : name;
+ }
+
+ @Override
+ public int getId() {
+ return CORAL_BLOCK;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.getEnchantment(Enchantment.ID_SILK_TOUCH) != null) {
+ return new Item[]{this.toItem() };
+ } else {
+ return new Item[]{ new ItemBlock(this.clone(), this.getDamage() | 0x8) };
+ }
+ } else {
+ return new Item[0];
+ }
+ }
+
+ public boolean isDead() {
+ return (this.getDamage() & 0x8) == 0x8;
+ }
+
+ public void setDead(boolean dead) {
+ if (dead) {
+ this.setDamage(this.getDamage() | 0x8);
+ } else {
+ this.setDamage(this.getDamage() ^ 0x8);
+ }
+ }
+
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCoralFan.java b/src/main/java/cn/nukkit/block/BlockCoralFan.java
new file mode 100644
index 00000000000..570dc433958
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralFan.java
@@ -0,0 +1,194 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.BlockFadeEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Faceable;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockCoralFan extends BlockCoral implements Faceable {
+
+ private static final String[] names = new String[] {
+ "Tube Coral Fan",
+ "Brain Coral Fan",
+ "Bubble Coral Fan",
+ "Fire Coral Fan",
+ "Horn Coral Fan"
+ };
+
+ public BlockCoralFan() {
+ this(0);
+ }
+
+ public BlockCoralFan(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block side = this.getSide(this.getRootsFace());
+ if (!side.isSolid() || side.getId() == MAGMA || side.getId() == SOUL_SAND) {
+ this.getLevel().useBreakOn(this);
+ } else {
+ this.getLevel().scheduleUpdate(this, 60 + ThreadLocalRandom.current().nextInt(40));
+ }
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ Block side = this.getSide(this.getRootsFace());
+ if (side.getId() == ICE) {
+ this.getLevel().useBreakOn(this);
+ return type;
+ }
+
+ if (!this.isDead() && !(this.getLevelBlock(BlockLayer.WATERLOGGED) instanceof BlockWater) && !(this.getLevelBlock(BlockLayer.WATERLOGGED) instanceof BlockIceFrosted)) {
+ BlockFadeEvent event = new BlockFadeEvent(this, Block.get(CORAL_FAN_DEAD, this.getDamage()));
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(this, event.getNewState(), true, true);
+ }
+ }
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ if ((this.getDamage() & 0x8) == 0) {
+ this.setDamage(this.getDamage() | 0x8);
+ } else {
+ this.setDamage(this.getDamage() ^ 0x8);
+ }
+ this.getLevel().setBlock(this, this, true, true);
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (face == BlockFace.DOWN) {
+ return false;
+ }
+
+ Block layer1 = block.getLevelBlock(BlockLayer.WATERLOGGED);
+ boolean hasWater = layer1 instanceof BlockWater;
+ if (layer1.getId() != Block.AIR && (!hasWater || layer1.getDamage() != 0 && layer1.getDamage() != 8)) {
+ return false;
+ }
+
+ if (hasWater && layer1.getDamage() == 8) {
+ this.getLevel().setBlock(this, BlockLayer.WATERLOGGED, Block.get(WATER), true, false);
+ }
+
+ if (!target.isSolid() || target.getId() == MAGMA || target.getId() == SOUL_SAND) {
+ return false;
+ }
+
+ if (face == BlockFace.UP) {
+ double rotation = player.yaw % 360;
+ if (rotation < 0) {
+ rotation += 360.0;
+ }
+ int axisBit = rotation >= 0 && rotation < 12 || (342 <= rotation && rotation < 360)? 0x0 : 0x8;
+ this.setDamage(this.getDamage() & 0x7 | axisBit);
+ this.getLevel().setBlock(this, BlockLayer.WATERLOGGED, hasWater? new BlockCoralFan(this.getDamage()) : new BlockCoralFanDead(this.getDamage()), true, true);
+ } else {
+ int type = this.getType();
+ int typeBit = type % 2;
+ int deadBit = this.isDead()? 0x1 : 0;
+ int faceBit;
+ switch (face) {
+ case WEST:
+ faceBit = 0;
+ break;
+ case EAST:
+ faceBit = 1;
+ break;
+ case NORTH:
+ faceBit = 2;
+ break;
+ default:
+ case SOUTH:
+ faceBit = 3;
+ break;
+ }
+ int deadData = faceBit << 2 | deadBit << 1 | typeBit;
+ int deadBlockId;
+ switch (type) {
+ default:
+ case BlockCoral.TYPE_TUBE:
+ case BlockCoral.TYPE_BRAIN:
+ deadBlockId = CORAL_FAN_HANG;
+ break;
+ case BlockCoral.TYPE_BUBBLE:
+ case BlockCoral.TYPE_FIRE:
+ deadBlockId = CORAL_FAN_HANG2;
+ break;
+ case BlockCoral.TYPE_HORN:
+ deadBlockId = CORAL_FAN_HANG3;
+ break;
+ }
+ this.getLevel().setBlock(this, BlockLayer.WATERLOGGED, Block.get(deadBlockId, deadData), true, true);
+ }
+ return true;
+ }
+
+
+ @Override
+ public String getName() {
+ int variant = this.getType();
+ String name;
+ if (variant >= names.length) {
+ name = names[0];
+ } else {
+ name = names[variant];
+ }
+ return name;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public int getId() {
+ return CORAL_FAN;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(this.getItemId(), this.getDamage() ^ 0x8);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.getEnchantment(Enchantment.ID_SILK_TOUCH) != null) {
+ return super.getDrops(item);
+ } else {
+ return new Item[0];
+ }
+ }
+
+ public boolean isDead() {
+ return false;
+ }
+
+ public int getType() {
+ return this.getDamage() & 0x7;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ public BlockFace getRootsFace() {
+ return BlockFace.DOWN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCoralFanDead.java b/src/main/java/cn/nukkit/block/BlockCoralFanDead.java
new file mode 100644
index 00000000000..1bffaded712
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralFanDead.java
@@ -0,0 +1,48 @@
+package cn.nukkit.block;
+
+import cn.nukkit.level.Level;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCoralFanDead extends BlockCoralFan {
+
+ public BlockCoralFanDead() {
+ this(0);
+ }
+
+ public BlockCoralFanDead(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!this.getSide(this.getRootsFace()).isSolid()) {
+ this.getLevel().useBreakOn(this);
+ }
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ return super.onUpdate(type);
+ }
+ return 0;
+ }
+
+ @Override
+ public String getName() {
+ return "Dead " + super.getName();
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GRAY_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getId() {
+ return CORAL_FAN_DEAD;
+ }
+
+ @Override
+ public boolean isDead() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCoralFanHang.java b/src/main/java/cn/nukkit/block/BlockCoralFanHang.java
new file mode 100644
index 00000000000..394218ff3d9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralFanHang.java
@@ -0,0 +1,83 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+
+public class BlockCoralFanHang extends BlockCoralFan {
+
+ public BlockCoralFanHang() {
+ this(0);
+ }
+
+ public BlockCoralFanHang(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_RANDOM) {
+ return type;
+ } else {
+ return super.onUpdate(type);
+ }
+ }
+
+ @Override
+ public int getType() {
+ if ((this.getDamage() & 0b1) == 0) {
+ return BlockCoral.TYPE_TUBE;
+ } else {
+ return BlockCoral.TYPE_BRAIN;
+ }
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ int face = this.getDamage() >> 2 & 0x3;
+ switch (face) {
+ case 0:
+ return BlockFace.WEST;
+ case 1:
+ return BlockFace.EAST;
+ case 2:
+ return BlockFace.NORTH;
+ default:
+ case 3:
+ return BlockFace.SOUTH;
+ }
+ }
+
+
+ @Override
+ public String getName() {
+ String name = super.getName();
+ name = name.substring(0, name.length() - 4);
+ if (isDead()) {
+ return "Dead " + name + " Wall Fan";
+ } else {
+ return name + " Wall Fan";
+ }
+ }
+
+ @Override
+ public boolean isDead() {
+ return (this.getDamage() & 0b10) == 0b10;
+ }
+
+ @Override
+ public BlockFace getRootsFace() {
+ return this.getBlockFace().getOpposite();
+ }
+
+ @Override
+ public int getId() {
+ return CORAL_FAN_HANG;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(this.isDead()? new BlockCoralFanDead() : new BlockCoralFan(), this.getType());
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCoralFanHang2.java b/src/main/java/cn/nukkit/block/BlockCoralFanHang2.java
new file mode 100644
index 00000000000..3dfeb9fa72c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralFanHang2.java
@@ -0,0 +1,26 @@
+package cn.nukkit.block;
+
+public class BlockCoralFanHang2 extends BlockCoralFanHang {
+
+ public BlockCoralFanHang2() {
+ this(0);
+ }
+
+ public BlockCoralFanHang2(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return CORAL_FAN_HANG2;
+ }
+
+ @Override
+ public int getType() {
+ if ((this.getDamage() & 0b1) == 0) {
+ return BlockCoral.TYPE_BUBBLE;
+ } else {
+ return BlockCoral.TYPE_FIRE;
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCoralFanHang3.java b/src/main/java/cn/nukkit/block/BlockCoralFanHang3.java
new file mode 100644
index 00000000000..434341e0783
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralFanHang3.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockCoralFanHang3 extends BlockCoralFanHang {
+
+ public BlockCoralFanHang3() {
+ this(0);
+ }
+
+ public BlockCoralFanHang3(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return CORAL_FAN_HANG3;
+ }
+
+ @Override
+ public int getType() {
+ return BlockCoral.TYPE_HORN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCraftingTable.java b/src/main/java/cn/nukkit/block/BlockCraftingTable.java
index aa5a9039dde..a5e4e0fb5cd 100644
--- a/src/main/java/cn/nukkit/block/BlockCraftingTable.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.event.player.CraftingTableOpenEvent;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
import cn.nukkit.network.protocol.ContainerOpenPacket;
@@ -11,8 +12,6 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockCraftingTable extends BlockSolid {
- public BlockCraftingTable() {
- }
@Override
public String getName() {
@@ -47,16 +46,25 @@ public int getToolType() {
@Override
public boolean onActivate(Item item, Player player) {
if (player != null) {
- player.craftingType = Player.CRAFTING_BIG;
- player.setCraftingGrid(player.getUIInventory().getBigCraftingGrid());
- ContainerOpenPacket pk = new ContainerOpenPacket();
- pk.windowId = -1;
- pk.type = 1;
- pk.x = (int) x;
- pk.y = (int) y;
- pk.z = (int) z;
- pk.entityId = player.getId();
- player.dataPacket(pk);
+ CraftingTableOpenEvent ev = new CraftingTableOpenEvent(player, this);
+ player.getServer().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ if (player.craftingType == Player.CRAFTING_BIG) {
+ player.getServer().getLogger().debug(player.getName() + " tried to activate crafting table but craftingType is already CRAFTING_BIG");
+ return true;
+ }
+ player.craftingType = Player.CRAFTING_BIG;
+ player.setCraftingGrid(player.getUIInventory().getBigCraftingGrid());
+
+ ContainerOpenPacket pk = new ContainerOpenPacket();
+ pk.windowId = -1;
+ pk.type = 1;
+ pk.x = (int) x;
+ pk.y = (int) y;
+ pk.z = (int) z;
+ pk.entityId = player.getId();
+ player.dataPacket(pk);
+ }
}
return true;
}
@@ -65,4 +73,9 @@ public boolean onActivate(Item item, Player player) {
public BlockColor getColor() {
return BlockColor.WOOD_BLOCK_COLOR;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonDoor.java b/src/main/java/cn/nukkit/block/BlockCrimsonDoor.java
new file mode 100644
index 00000000000..55e14d4c778
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonDoor.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockCrimsonDoor extends BlockDoor {
+
+ public BlockCrimsonDoor() {
+ this(0);
+ }
+
+ public BlockCrimsonDoor(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Door";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_DOOR_BLOCK;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.CRIMSON_DOOR);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonFungus.java b/src/main/java/cn/nukkit/block/BlockCrimsonFungus.java
new file mode 100644
index 00000000000..cecfac16215
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonFungus.java
@@ -0,0 +1,97 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.generator.object.tree.ObjectCrimsonTree;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.level.Position;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.math.NukkitRandom;
+import cn.nukkit.math.Vector3;
+import cn.nukkit.utils.DyeColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockCrimsonFungus extends BlockFungus {
+
+ public BlockCrimsonFungus() {
+ // Does nothing
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_FUNGUS;
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Fungus";
+ }
+
+ @Override
+ protected boolean canGrowOn(Block support) {
+ return support.getId() == CRIMSON_NYLIUM;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (this.down().isTransparent()) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean grow(Player cause) {
+ // TODO:
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+
+ public boolean canPlaceOn(Block floor, Position pos) {
+ switch (floor.getId()) {
+ case BlockID.GRASS:
+ case BlockID.DIRT:
+ case BlockID.PODZOL:
+ case BlockID.FARMLAND:
+ case BlockID.CRIMSON_NYLIUM:
+ case BlockID.WARPED_NYLIUM:
+ case BlockID.MYCELIUM:
+ case BlockID.SOUL_SOIL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() == Item.DYE && item.getDamage() == DyeColor.WHITE.getDyeData()) {
+ if (player != null && (player.gamemode & 0x01) == 0) {
+ item.count--;
+ }
+
+ if (ThreadLocalRandom.current().nextFloat() < 0.4 && this.level.getBlockIdAt((int) this.x, (int) this.y - 1, (int) this.z) == CRIMSON_NYLIUM) {
+ new ObjectCrimsonTree().placeObject(this.level, (int) this.x, (int) this.y, (int) this.z, new NukkitRandom());
+ this.level.setBlock(new Vector3((int) this.x, (int) this.y - 1, (int) this.z), Block.get(NETHERRACK), false, true);
+ }
+
+ this.level.addParticle(new BoneMealParticle(this));
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonNylium.java b/src/main/java/cn/nukkit/block/BlockCrimsonNylium.java
new file mode 100644
index 00000000000..d9a92cd64f6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonNylium.java
@@ -0,0 +1,25 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCrimsonNylium extends BlockNylium {
+
+ public BlockCrimsonNylium() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Nylium";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_NYLIUM;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CRIMSON_NYLIUM_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonPlanks.java b/src/main/java/cn/nukkit/block/BlockCrimsonPlanks.java
new file mode 100644
index 00000000000..29fedb8ca1d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonPlanks.java
@@ -0,0 +1,45 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCrimsonPlanks extends BlockSolid {
+
+ public BlockCrimsonPlanks() {
+ this(0);
+ }
+
+ public BlockCrimsonPlanks(int meta) {
+ // super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Planks";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_PLANKS;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CRIMSON_STEM_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonRoots.java b/src/main/java/cn/nukkit/block/BlockCrimsonRoots.java
new file mode 100644
index 00000000000..b583589db2f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonRoots.java
@@ -0,0 +1,39 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCrimsonRoots extends BlockRoots {
+
+ public BlockCrimsonRoots() {
+ this(0);
+ }
+
+ public BlockCrimsonRoots(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Roots";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_ROOTS;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canBeReplaced() {
+ return true;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonSign.java b/src/main/java/cn/nukkit/block/BlockCrimsonSign.java
new file mode 100644
index 00000000000..5276bbd4e1a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonSign.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockCrimsonSign extends BlockSignPost {
+
+ public BlockCrimsonSign() {
+ this(0);
+ }
+
+ public BlockCrimsonSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Sign";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_STANDING_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.CRIMSON_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return CRIMSON_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return CRIMSON_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonStairs.java b/src/main/java/cn/nukkit/block/BlockCrimsonStairs.java
new file mode 100644
index 00000000000..6c5f3d0f059
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonStairs.java
@@ -0,0 +1,39 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCrimsonStairs extends BlockStairsWood {
+
+ public BlockCrimsonStairs() {
+ this(0);
+ }
+
+ public BlockCrimsonStairs(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Wood Stairs";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonStem.java b/src/main/java/cn/nukkit/block/BlockCrimsonStem.java
new file mode 100644
index 00000000000..067f8fd6cf8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonStem.java
@@ -0,0 +1,51 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockCrimsonStem extends BlockStem {
+
+ public BlockCrimsonStem() {
+ this(0);
+ }
+
+ public BlockCrimsonStem(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Stem";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_STEM;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public int getStrippedId() {
+ return STRIPPED_CRIMSON_STEM;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CRIMSON_STEM_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonTrapdoor.java b/src/main/java/cn/nukkit/block/BlockCrimsonTrapdoor.java
new file mode 100644
index 00000000000..e58c80ca0f4
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonTrapdoor.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockCrimsonTrapdoor extends BlockTrapdoor {
+
+ public BlockCrimsonTrapdoor() {
+ this(0);
+ }
+
+ public BlockCrimsonTrapdoor(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Trapdoor";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_TRAPDOOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrimsonWallSign.java b/src/main/java/cn/nukkit/block/BlockCrimsonWallSign.java
new file mode 100644
index 00000000000..757663c0778
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCrimsonWallSign.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockCrimsonWallSign extends BlockWallSign {
+
+ public BlockCrimsonWallSign() {
+ this(0);
+ }
+
+ public BlockCrimsonWallSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson WallSign";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_WALL_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.CRIMSON_SIGN);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockCrops.java b/src/main/java/cn/nukkit/block/BlockCrops.java
index f02c05df693..028aaca5d27 100644
--- a/src/main/java/cn/nukkit/block/BlockCrops.java
+++ b/src/main/java/cn/nukkit/block/BlockCrops.java
@@ -4,15 +4,15 @@
import cn.nukkit.Server;
import cn.nukkit.event.block.BlockGrowEvent;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.level.Level;
import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockCrops extends BlockFlowable {
@@ -26,7 +26,6 @@ public boolean canBeActivated() {
return true;
}
-
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (block.down().getId() == FARMLAND) {
@@ -38,11 +37,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public boolean onActivate(Item item, Player player) {
- //Bone meal
- if (item.getId() == Item.DYE && item.getDamage() == 0x0f) {
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
if (this.getDamage() < 7) {
BlockCrops block = (BlockCrops) this.clone();
- block.setDamage(block.getDamage() + ThreadLocalRandom.current().nextInt(3) + 2);
+ block.setDamage(block.getDamage() + Utils.random.nextInt(3) + 2);
if (block.getDamage() > 7) {
block.setDamage(7);
}
@@ -54,9 +52,10 @@ public boolean onActivate(Item item, Player player) {
}
this.getLevel().setBlock(this, ev.getNewState(), false, true);
+
this.level.addParticle(new BoneMealParticle(this));
- if (player != null && (player.gamemode & 0x01) == 0) {
+ if (player != null && !player.isCreative()) {
item.count--;
}
}
@@ -75,7 +74,7 @@ public int onUpdate(int type) {
return Level.BLOCK_UPDATE_NORMAL;
}
} else if (type == Level.BLOCK_UPDATE_RANDOM) {
- if (ThreadLocalRandom.current().nextInt(2) == 1) {
+ if (Utils.random.nextInt(2) == 1) {
if (this.getDamage() < 0x07) {
BlockCrops block = (BlockCrops) this.clone();
block.setDamage(block.getDamage() + 1);
diff --git a/src/main/java/cn/nukkit/block/BlockDandelion.java b/src/main/java/cn/nukkit/block/BlockDandelion.java
index acfe445c8ab..965f7ff4d01 100644
--- a/src/main/java/cn/nukkit/block/BlockDandelion.java
+++ b/src/main/java/cn/nukkit/block/BlockDandelion.java
@@ -5,6 +5,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockDandelion extends BlockFlower {
+
public BlockDandelion() {
this(0);
}
diff --git a/src/main/java/cn/nukkit/block/BlockDarkOakSignStanding.java b/src/main/java/cn/nukkit/block/BlockDarkOakSignStanding.java
new file mode 100644
index 00000000000..255056a8490
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDarkOakSignStanding.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockDarkOakSignStanding extends BlockSignPost {
+
+ public BlockDarkOakSignStanding() {
+ this(0);
+ }
+
+ public BlockDarkOakSignStanding(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Dark Oak Sign Post";
+ }
+
+ @Override
+ public int getId() {
+ return DARK_OAK_STANDING_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.DARKOAK_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return DARK_OAK_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return DARK_OAK_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDarkOakWallSign.java b/src/main/java/cn/nukkit/block/BlockDarkOakWallSign.java
new file mode 100644
index 00000000000..3a28af6e740
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDarkOakWallSign.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockDarkOakWallSign extends BlockWallSign {
+
+ public BlockDarkOakWallSign() {
+ this(0);
+ }
+
+ public BlockDarkOakWallSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Dark Oak Wall Sign";
+ }
+
+ @Override
+ public int getId() {
+ return DARK_OAK_WALL_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.DARKOAK_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return DARK_OAK_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return DARK_OAK_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDaylightDetector.java b/src/main/java/cn/nukkit/block/BlockDaylightDetector.java
index 8e196e50c94..647630c9021 100644
--- a/src/main/java/cn/nukkit/block/BlockDaylightDetector.java
+++ b/src/main/java/cn/nukkit/block/BlockDaylightDetector.java
@@ -1,7 +1,10 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
/**
@@ -10,9 +13,6 @@
*/
public class BlockDaylightDetector extends BlockTransparent {
- public BlockDaylightDetector() {
- }
-
@Override
public int getId() {
return DAYLIGHT_DETECTOR;
@@ -33,9 +33,35 @@ public BlockColor getColor() {
return BlockColor.WOOD_BLOCK_COLOR;
}
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ this.getLevel().setBlock(this, Block.get(DAYLIGHT_DETECTOR_INVERTED));
+ return true;
+ }
+
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public boolean isPowerSource() {
+ return true;
+ }
+
+ @Override
+ public int getWeakPower(BlockFace face) {
+ return this.level.isAnimalSpawningAllowedByTime() ? 15 : 0;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
}
@Override
@@ -48,11 +74,30 @@ public double getMaxY() {
return this.y + 0.625;
}
- //This function is a suggestion that can be renamed or deleted
- protected boolean invertDetect() {
- return false;
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_SCHEDULED) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.level.updateAroundRedstone(this, null);
+ }
+ this.level.scheduleUpdate(this, 40);
+ }
+ return 0;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
}
- //todo redstone
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (this.getLevel().setBlock(this, this, true, true)) {
+ this.level.scheduleUpdate(this, 40);
+
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockDaylightDetectorInverted.java b/src/main/java/cn/nukkit/block/BlockDaylightDetectorInverted.java
index 3d0307915b6..dfb25d16c9f 100644
--- a/src/main/java/cn/nukkit/block/BlockDaylightDetectorInverted.java
+++ b/src/main/java/cn/nukkit/block/BlockDaylightDetectorInverted.java
@@ -1,7 +1,9 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
+import cn.nukkit.math.BlockFace;
/**
* Created on 2015/11/22 by CreeperFace.
@@ -9,9 +11,6 @@
*/
public class BlockDaylightDetectorInverted extends BlockDaylightDetector {
- public BlockDaylightDetectorInverted() {
- }
-
@Override
public int getId() {
return DAYLIGHT_DETECTOR_INVERTED;
@@ -22,13 +21,24 @@ public String getName() {
return "Daylight Detector Inverted";
}
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ this.getLevel().setBlock(this, Block.get(DAYLIGHT_DETECTOR));
+ return true;
+ }
+
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.DAYLIGHT_DETECTOR), 0);
+ return new ItemBlock(Block.get(DAYLIGHT_DETECTOR), 0);
}
- protected boolean invertDetect() {
+ @Override
+ public boolean isPowerSource() {
return true;
}
+ @Override
+ public int getWeakPower(BlockFace face) {
+ return this.level.isAnimalSpawningAllowedByTime() ? 0 : 15;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockDeadBush.java b/src/main/java/cn/nukkit/block/BlockDeadBush.java
index 23b4c9e2296..4c467a012e1 100644
--- a/src/main/java/cn/nukkit/block/BlockDeadBush.java
+++ b/src/main/java/cn/nukkit/block/BlockDeadBush.java
@@ -2,24 +2,22 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemStick;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/12/2 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockDeadBush extends BlockFlowable {
+
public BlockDeadBush() {
this(0);
}
public BlockDeadBush(int meta) {
- // Dead bushes can't have meta. Also stops the server from throwing an exception with the block palette.
super(0);
}
@@ -70,7 +68,7 @@ public Item[] getDrops(Item item) {
};
} else {
return new Item[]{
- new ItemStick(0, ThreadLocalRandom.current().nextInt(3))
+ Item.get(Item.STICK, 0, Utils.random.nextInt(3))
};
}
}
@@ -78,4 +76,14 @@ public Item[] getDrops(Item item) {
public BlockColor getColor() {
return BlockColor.FOLIAGE_BLOCK_COLOR;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockDeepslate.java b/src/main/java/cn/nukkit/block/BlockDeepslate.java
new file mode 100644
index 00000000000..803cf8bc4a2
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDeepslate.java
@@ -0,0 +1,106 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDeepslate extends BlockSolidMeta {
+
+ public BlockDeepslate() {
+ this(0);
+ }
+
+ public BlockDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate";
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setPillarAxis(face.getAxis());
+ this.getLevel().setBlock(block, this, true, true);
+ return true;
+ }
+
+ public void setPillarAxis(BlockFace.Axis axis) {
+ switch (axis) {
+ case Y:
+ this.setDamage(0);
+ break;
+ case X:
+ this.setDamage(1);
+ break;
+ case Z:
+ this.setDamage(2);
+ break;
+ }
+ }
+
+ public BlockFace.Axis getPillarAxis() {
+ switch (this.getDamage() % 3) {
+ case 2:
+ return BlockFace.Axis.Z;
+ case 1:
+ return BlockFace.Axis.X;
+ case 0:
+ default:
+ return BlockFace.Axis.Y;
+ }
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(DEEPSLATE), 0);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (!this.canHarvest(item)) {
+ return new Item[0];
+ }
+
+ return new Item[]{new ItemBlock(Block.get(COBBLED_DEEPSLATE), 0)};
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDeepslateChiseled.java b/src/main/java/cn/nukkit/block/BlockDeepslateChiseled.java
new file mode 100644
index 00000000000..ec1bc34b5cf
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDeepslateChiseled.java
@@ -0,0 +1,17 @@
+package cn.nukkit.block;
+
+public class BlockDeepslateChiseled extends BlockDeepslateCobbled {
+
+ public BlockDeepslateChiseled() {
+ }
+
+ @Override
+ public int getId() {
+ return CHISELED_DEEPSLATE;
+ }
+
+ @Override
+ public String getName() {
+ return "Chiseled Deepslate";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDeepslateCobbled.java b/src/main/java/cn/nukkit/block/BlockDeepslateCobbled.java
new file mode 100644
index 00000000000..3c7dfaf4dfd
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDeepslateCobbled.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockDeepslateCobbled extends BlockSolid {
+
+ public BlockDeepslateCobbled() {
+ }
+
+ @Override
+ public String getName() {
+ return "Cobbled Deepslate";
+ }
+
+ @Override
+ public int getId() {
+ return COBBLED_DEEPSLATE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6.0;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDeepslatePolished.java b/src/main/java/cn/nukkit/block/BlockDeepslatePolished.java
new file mode 100644
index 00000000000..76ed536817c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDeepslatePolished.java
@@ -0,0 +1,18 @@
+package cn.nukkit.block;
+
+public class BlockDeepslatePolished extends BlockDeepslateCobbled {
+
+ public BlockDeepslatePolished() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Deepslate";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_DEEPSLATE;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDeny.java b/src/main/java/cn/nukkit/block/BlockDeny.java
new file mode 100644
index 00000000000..26c60adca9b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDeny.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockDeny extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return DENY;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public String getName() {
+ return "Deny";
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDiamond.java b/src/main/java/cn/nukkit/block/BlockDiamond.java
index 53073452789..61f95a42c96 100644
--- a/src/main/java/cn/nukkit/block/BlockDiamond.java
+++ b/src/main/java/cn/nukkit/block/BlockDiamond.java
@@ -9,9 +9,6 @@
*/
public class BlockDiamond extends BlockSolid {
- public BlockDiamond() {
- }
-
@Override
public double getHardness() {
return 5;
@@ -34,7 +31,7 @@ public int getId() {
@Override
public String getName() {
- return "Diamond Block";
+ return "Block of Diamond";
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDirt.java b/src/main/java/cn/nukkit/block/BlockDirt.java
index dd87fb80c7e..fc94cf24c09 100644
--- a/src/main/java/cn/nukkit/block/BlockDirt.java
+++ b/src/main/java/cn/nukkit/block/BlockDirt.java
@@ -3,11 +3,15 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Sound;
+import cn.nukkit.level.generator.object.ObjectTallGrass;
+import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* AMAZING COARSE DIRT added by kvetinac97
* Nukkit Project
*/
@@ -17,7 +21,7 @@ public BlockDirt() {
this(0);
}
- public BlockDirt(int meta){
+ public BlockDirt(int meta) {
super(meta);
}
@@ -54,15 +58,35 @@ public String getName() {
@Override
public boolean onActivate(Item item, Player player) {
if (item.isHoe()) {
- if (this.up() instanceof BlockAir) {
+ Block up = this.up();
+ if (up instanceof BlockAir || up instanceof BlockFlowable) {
item.useOn(this);
this.getLevel().setBlock(this, this.getDamage() == 0 ? get(FARMLAND) : get(DIRT), true);
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
return true;
}
} else if (item.isShovel()) {
- if (this.up() instanceof BlockAir) {
+ Block up = this.up();
+ if (up instanceof BlockAir || up instanceof BlockFlowable) {
item.useOn(this);
- this.getLevel().setBlock(this, get(GRASS_PATH));
+ this.getLevel().setBlock(this, Block.get(GRASS_PATH));
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
+ return true;
+ }
+ } else if (player != null && item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ Block up = this.up();
+ if (up instanceof BlockWater) {
+ if (!player.isCreative()) {
+ item.count--;
+ }
+ this.level.addParticle(new BoneMealParticle(this));
+ if (up.up() instanceof BlockWater) {
+ ObjectTallGrass.growSeagrass(this.getLevel(), this);
+ }
return true;
}
}
@@ -72,7 +96,8 @@ public boolean onActivate(Item item, Player player) {
@Override
public Item[] getDrops(Item item) {
- return new Item[]{new ItemBlock(Block.get(BlockID.DIRT))};
+ int damage = this.getDamage() & 0x01;
+ return new Item[]{new ItemBlock(Block.get(BlockID.DIRT, damage), damage)};
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDirtRooted.java b/src/main/java/cn/nukkit/block/BlockDirtRooted.java
new file mode 100644
index 00000000000..4a888b24c70
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDirtRooted.java
@@ -0,0 +1,81 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.level.Sound;
+import cn.nukkit.level.particle.BoneMealParticle;
+
+public class BlockDirtRooted extends BlockDirt {
+
+ public BlockDirtRooted() {
+ this(0);
+ }
+
+ public BlockDirtRooted(int meta) {
+ super(0); // no different states
+ }
+
+ @Override
+ public int getId() {
+ return ROOTED_DIRT;
+ }
+
+ @Override
+ public String getName() {
+ return "Rooted Dirt";
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ Block up = this.up();
+ if (item.isShovel() && (up instanceof BlockAir || up instanceof BlockFlowable)) {
+ item.useOn(this);
+ this.getLevel().setBlock(this, Block.get(GRASS_PATH));
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
+ return true;
+ }
+
+ if (item.isHoe() && (up instanceof BlockAir || up instanceof BlockFlowable)) {
+ item.useOn(this);
+ this.getLevel().setBlock(this, Block.get(DIRT));
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
+ this.getLevel().dropItem(this.add(0.5, 0.8, 0.5), new ItemBlock(Block.get(HANGING_ROOTS), 0));
+ return true;
+ }
+
+ if (item.getId() != Item.DYE || item.getDamage() != ItemDye.BONE_MEAL) {
+ return false;
+ }
+
+ Block down = this.down();
+ BlockGrowEvent event = new BlockGrowEvent(down, Block.get(BlockID.HANGING_ROOTS, 0, down));
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ if (down.getId() == AIR || down.canBeReplaced()) {
+ this.getLevel().setBlock(down, Block.get(HANGING_ROOTS));
+ this.level.addParticle(new BoneMealParticle(down));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[]{ this.toItem() };
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDispenser.java b/src/main/java/cn/nukkit/block/BlockDispenser.java
index 3bd7bbd75f5..0976b60b37b 100644
--- a/src/main/java/cn/nukkit/block/BlockDispenser.java
+++ b/src/main/java/cn/nukkit/block/BlockDispenser.java
@@ -1,10 +1,23 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityDispenser;
+import cn.nukkit.dispenser.DispenseBehavior;
+import cn.nukkit.dispenser.DispenseBehaviorRegister;
+import cn.nukkit.inventory.ContainerInventory;
+import cn.nukkit.inventory.Inventory;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.Vector3;
+import cn.nukkit.nbt.tag.StringTag;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.utils.Faceable;
+import cn.nukkit.utils.Utils;
+
+import java.util.Map.Entry;
/**
* Created by CreeperFace on 15.4.2017.
@@ -36,22 +49,18 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(Block.DISPENSER));
}
@Override
public int getComparatorInputOverride() {
- /*BlockEntity blockEntity = this.level.getBlockEntity(this);
-
- if(blockEntity instanceof BlockEntityDispenser) {
- //return ContainerInventory.calculateRedstone(((BlockEntityDispenser) blockEntity).getInventory()); TODO: dispenser
- }*/
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
- return super.getComparatorInputOverride();
- }
+ if (blockEntity instanceof BlockEntityDispenser) {
+ return ContainerInventory.calculateRedstone(((BlockEntityDispenser) blockEntity).getInventory());
+ }
- public BlockFace getFacing() {
- return BlockFace.fromIndex(this.getDamage() & 7);
+ return 0;
}
public boolean isTriggered() {
@@ -60,7 +69,7 @@ public boolean isTriggered() {
public void setTriggered(boolean value) {
int i = 0;
- i |= getFacing().getIndex();
+ i |= getBlockFace().getIndex();
if (value) {
i |= 8;
@@ -70,20 +79,136 @@ public void setTriggered(boolean value) {
}
@Override
- public boolean canHarvestWithHand() {
- return false;
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player == null) {
+ return false;
+ }
+
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (!(blockEntity instanceof BlockEntityDispenser)) {
+ return false;
+ }
+
+ if (blockEntity.namedTag.contains("Lock") && blockEntity.namedTag.get("Lock") instanceof StringTag) {
+ if (!blockEntity.namedTag.getString("Lock").equals(item.getCustomName())) {
+ return true;
+ }
+ }
+
+ player.addWindow(((BlockEntityDispenser) blockEntity).getInventory());
+ return true;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (player != null) {
+ if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) {
+ double y = player.y + player.getEyeHeight();
+
+ if (y - this.y > 2) {
+ this.setDamage(BlockFace.UP.getIndex());
+ } else if (this.y - y > 0) {
+ this.setDamage(BlockFace.DOWN.getIndex());
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+ }
+
+ this.getLevel().setBlock(block, this, true);
+
+ BlockEntity.createBlockEntity(BlockEntity.DISPENSER, this.getChunk(), BlockEntity.getDefaultCompound(this, BlockEntity.DISPENSER));
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.setTriggered(false);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
+ dispense();
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_REDSTONE) {
+ if (!isTriggered() && (level.isBlockPowered(this) || level.isBlockPowered(this.getSideVec(BlockFace.UP)))) {
+ this.setTriggered(true);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
+ level.scheduleUpdate(this, this, 4);
+ }
+
+ return type;
+ }
+
+ return 0;
+ }
+
+ public void dispense() {
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (!(blockEntity instanceof BlockEntityDispenser)) {
+ return;
+ }
+
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_CLICK);
+
+ int r = 1;
+ int slot = -1;
+ Item target = null;
+
+ Inventory inv = ((BlockEntityDispenser) blockEntity).getInventory();
+ for (Entry entry : inv.getContents().entrySet()) {
+ Item item = entry.getValue();
+
+ if (!item.isNull() && Utils.random.nextInt(r++) == 0) {
+ target = item;
+ slot = entry.getKey();
+ }
+ }
+
+ if (target == null) {
+ return;
+ }
+ target = target.clone();
+
+ DispenseBehavior behavior = DispenseBehaviorRegister.getBehavior(target.getId());
+ Item result = behavior.dispense(this, getBlockFace(), target);
+
+ if (result == null) {
+ target.count--;
+ inv.setItem(slot, target);
+ } else if (!result.equals(target)) {
+ inv.setItem(slot, result);
+ }
}
public Vector3 getDispensePosition() {
- BlockFace facing = getFacing();
- double x = this.getX() + 0.7 * facing.getXOffset();
- double y = this.getY() + 0.7 * facing.getYOffset();
- double z = this.getZ() + 0.7 * facing.getZOffset();
- return new Vector3(x, y, z);
+ BlockFace facing = getBlockFace();
+ return this.add(
+ 0.5 + 0.7 * facing.getXOffset(),
+ 0.5 + 0.7 * facing.getYOffset(),
+ 0.5 + 0.7 * facing.getZOffset()
+ );
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromIndex(this.getDamage() & 0x07);
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockDoor.java b/src/main/java/cn/nukkit/block/BlockDoor.java
index 53995465ecb..08579d94e56 100644
--- a/src/main/java/cn/nukkit/block/BlockDoor.java
+++ b/src/main/java/cn/nukkit/block/BlockDoor.java
@@ -5,22 +5,24 @@
import cn.nukkit.event.block.DoorToggleEvent;
import cn.nukkit.item.Item;
import cn.nukkit.level.Level;
+import cn.nukkit.level.Sound;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.SimpleAxisAlignedBB;
-import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.Faceable;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockDoor extends BlockTransparentMeta implements Faceable {
- public static int DOOR_OPEN_BIT = 0x04;
- public static int DOOR_TOP_BIT = 0x08;
- public static int DOOR_HINGE_BIT = 0x01;
- public static int DOOR_POWERED_BIT = 0x02;
+ public static final int DOOR_OPEN_BIT = 0x04;
+ public static final int DOOR_TOP_BIT = 0x08;
+ public static final int DOOR_HINGE_BIT = 0x01;
+ public static final int DOOR_POWERED_BIT = 0x02;
+
+ private static final int[] faces = {1, 2, 3, 0};
protected BlockDoor(int meta) {
super(meta);
@@ -36,21 +38,27 @@ public boolean isSolid() {
return false;
}
- public int getFullDamage() {
- int meta;
-
+ private int getFullDamage() {
+ int up;
+ int down;
if (isTop()) {
- meta = this.down().getDamage();
+ down = this.down().getDamage();
+ up = this.getDamage();
} else {
- meta = this.getDamage();
+ down = this.getDamage();
+ up = this.up().getDamage();
}
- return (this.getId() << 5 ) + (meta & 0x07 | (isTop() ? 0x08 : 0) | (isRightHinged() ? 0x10 :0));
+
+ boolean isRight = (up & DOOR_HINGE_BIT) > 0;
+
+ return down & 0x07 | (isTop() ? 0x08 : 0) | (isRight ? 0x10 : 0);
}
@Override
protected AxisAlignedBB recalculateBoundingBox() {
double f = 0.1875;
+ int damage = this.getFullDamage();
AxisAlignedBB bb = new SimpleAxisAlignedBB(
this.x,
@@ -61,9 +69,9 @@ protected AxisAlignedBB recalculateBoundingBox() {
this.z + 1
);
- int j = isTop() ? (this.down().getDamage() & 0x03) : getDamage() & 0x03;
- boolean isOpen = isOpen();
- boolean isRight = isRightHinged();
+ int j = damage & 0x03;
+ boolean isOpen = ((damage & 0x04) > 0);
+ boolean isRight = ((damage & 0x10) > 0);
if (j == 0) {
if (isOpen) {
@@ -210,7 +218,8 @@ public int onUpdate(int type) {
}
if (type == Level.BLOCK_UPDATE_REDSTONE) {
- if ((!isOpen() && this.level.isBlockPowered(this.getLocation())) || (isOpen() && !this.level.isBlockPowered(this.getLocation()))) {
+ boolean powered = this.level.isBlockPowered(this);
+ if ((!isOpen() && powered) || (isOpen() && !powered)) {
this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, isOpen() ? 15 : 0, isOpen() ? 0 : 15));
this.toggle(null);
@@ -222,14 +231,13 @@ 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 (this.y > 254) return false;
+ if (this.y > target.getLevel().getMaxBlockY() - 1) return false;
if (face == BlockFace.UP) {
Block blockUp = this.up();
- Block blockDown = this.down();
- if (!blockUp.canBeReplaced() || blockDown.isTransparent()) {
+ if (!blockUp.canBeReplaced() || !canStayOnFullNonSolid(this.down())) {
return false;
}
- int[] faces = {1, 2, 3, 0};
+
int direction = faces[player != null ? player.getDirection().getHorizontalIndex() : 0];
Block left = this.getSide(player.getDirection().rotateYCCW());
@@ -240,15 +248,12 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
this.setDamage(direction);
- this.getLevel().setBlock(block, this, true, false); //Bottom
- this.getLevel().setBlock(blockUp, Block.get(this.getId(), metaUp), true, true); //Top
- if (!this.isOpen() && this.level.isBlockPowered(this.getLocation())) {
- this.toggle(null);
- metaUp |= DOOR_POWERED_BIT;
- this.getLevel().setBlockDataAt(blockUp.getFloorX(), blockUp.getFloorY(), blockUp.getFloorZ(), metaUp);
- }
+ //Bottom
+ this.getLevel().setBlock(this, this, true, true);
+ //Top
+ this.getLevel().setBlock(blockUp, Block.get(this.getId(), metaUp), true);
return true;
}
@@ -273,19 +278,9 @@ public boolean onBreak(Item item) {
return true;
}
- @Override
- public boolean onActivate(Item item) {
- return this.onActivate(item, null);
- }
-
@Override
public boolean onActivate(Item item, Player player) {
- if (!this.toggle(player)) {
- return false;
- }
-
- this.getLevel().addLevelEvent(this.add(0.5, 0.5, 0.5), LevelEventPacket.EVENT_SOUND_DOOR);
- return true;
+ return this.toggle(player);
}
public boolean toggle(Player player) {
@@ -297,19 +292,37 @@ public boolean toggle(Player player) {
}
Block down;
- if (isTop()) {
+ Block up;
+ if (isTop(this.getDamage())) {
down = this.down();
+ up = this;
} else {
down = this;
+ up = this.up();
}
- if (down.up().getId() != down.getId()) {
+
+ if (up.getId() != down.getId()) {
return false;
}
- down.setDamage(down.getDamage() ^ DOOR_OPEN_BIT);
- getLevel().setBlock(down, down, true, true);
+
+ int data = down.getDamage() ^ 0x04;
+ this.level.setBlockDataAt(down.getFloorX(), down.getFloorY(), down.getFloorZ(), data);
+ if (this.isOpenAfter(data)) {
+ this.level.addSound(this, Sound.RANDOM_DOOR_OPEN);
+ } else {
+ this.level.addSound(this, Sound.RANDOM_DOOR_CLOSE);
+ }
return true;
}
+ private boolean isOpenAfter(int data) {
+ if (isTop(data)) {
+ return (this.down().getDamage() & DOOR_OPEN_BIT) > 0;
+ } else {
+ return (data & DOOR_OPEN_BIT) > 0;
+ }
+ }
+
public boolean isOpen() {
if (isTop(this.getDamage())) {
return (this.down().getDamage() & DOOR_OPEN_BIT) > 0;
@@ -317,6 +330,7 @@ public boolean isOpen() {
return (this.getDamage() & DOOR_OPEN_BIT) > 0;
}
}
+
public boolean isTop() {
return isTop(this.getDamage());
}
@@ -334,6 +348,16 @@ public boolean isRightHinged() {
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockDoorAcacia.java b/src/main/java/cn/nukkit/block/BlockDoorAcacia.java
index 0ca8279bbf0..46503b1c544 100644
--- a/src/main/java/cn/nukkit/block/BlockDoorAcacia.java
+++ b/src/main/java/cn/nukkit/block/BlockDoorAcacia.java
@@ -1,7 +1,6 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemDoorAcacia;
import cn.nukkit.utils.BlockColor;
public class BlockDoorAcacia extends BlockDoorWood {
@@ -26,7 +25,7 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemDoorAcacia();
+ return Item.get(Item.ACACIA_DOOR);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDoorBirch.java b/src/main/java/cn/nukkit/block/BlockDoorBirch.java
index 6556596f898..66cf813df2a 100644
--- a/src/main/java/cn/nukkit/block/BlockDoorBirch.java
+++ b/src/main/java/cn/nukkit/block/BlockDoorBirch.java
@@ -1,7 +1,6 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemDoorBirch;
import cn.nukkit.utils.BlockColor;
public class BlockDoorBirch extends BlockDoorWood {
@@ -26,7 +25,7 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemDoorBirch();
+ return Item.get(Item.BIRCH_DOOR);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDoorDarkOak.java b/src/main/java/cn/nukkit/block/BlockDoorDarkOak.java
index 4da32f31c85..38c90229867 100644
--- a/src/main/java/cn/nukkit/block/BlockDoorDarkOak.java
+++ b/src/main/java/cn/nukkit/block/BlockDoorDarkOak.java
@@ -1,7 +1,6 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemDoorDarkOak;
import cn.nukkit.utils.BlockColor;
public class BlockDoorDarkOak extends BlockDoorWood {
@@ -26,7 +25,7 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemDoorDarkOak();
+ return Item.get(Item.DARK_OAK_DOOR);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDoorIron.java b/src/main/java/cn/nukkit/block/BlockDoorIron.java
index 74df6c75bd8..2a7a572c6b7 100644
--- a/src/main/java/cn/nukkit/block/BlockDoorIron.java
+++ b/src/main/java/cn/nukkit/block/BlockDoorIron.java
@@ -2,12 +2,11 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemDoorIron;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockDoorIron extends BlockDoor {
@@ -30,11 +29,6 @@ public int getId() {
return IRON_DOOR_BLOCK;
}
- @Override
- public boolean canBeActivated() {
- return true;
- }
-
@Override
public double getHardness() {
return 5;
@@ -52,7 +46,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -63,7 +57,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemDoorIron();
+ return Item.get(Item.IRON_DOOR);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDoorJungle.java b/src/main/java/cn/nukkit/block/BlockDoorJungle.java
index 5f6cb1fed5a..2af2d291a69 100644
--- a/src/main/java/cn/nukkit/block/BlockDoorJungle.java
+++ b/src/main/java/cn/nukkit/block/BlockDoorJungle.java
@@ -1,7 +1,6 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemDoorJungle;
import cn.nukkit.utils.BlockColor;
public class BlockDoorJungle extends BlockDoorWood {
@@ -26,7 +25,7 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemDoorJungle();
+ return Item.get(Item.JUNGLE_DOOR);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDoorSpruce.java b/src/main/java/cn/nukkit/block/BlockDoorSpruce.java
index 1f9accf6ebe..05b3f0f66a1 100644
--- a/src/main/java/cn/nukkit/block/BlockDoorSpruce.java
+++ b/src/main/java/cn/nukkit/block/BlockDoorSpruce.java
@@ -1,7 +1,6 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemDoorSpruce;
import cn.nukkit.utils.BlockColor;
public class BlockDoorSpruce extends BlockDoorWood {
@@ -26,7 +25,7 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemDoorSpruce();
+ return Item.get(Item.SPRUCE_DOOR);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDoorWood.java b/src/main/java/cn/nukkit/block/BlockDoorWood.java
index ad34c77d4d7..27a79562fa8 100644
--- a/src/main/java/cn/nukkit/block/BlockDoorWood.java
+++ b/src/main/java/cn/nukkit/block/BlockDoorWood.java
@@ -1,12 +1,11 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemDoorWood;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockDoorWood extends BlockDoor {
@@ -46,7 +45,7 @@ public int getToolType() {
@Override
public Item toItem() {
- return new ItemDoorWood();
+ return Item.get(Item.WOODEN_DOOR);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleMudBrickSlab.java b/src/main/java/cn/nukkit/block/BlockDoubleMudBrickSlab.java
new file mode 100644
index 00000000000..ff60c220548
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleMudBrickSlab.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+public class BlockDoubleMudBrickSlab extends BlockDoubleSlabBase {
+
+ public BlockDoubleMudBrickSlab() {
+ this(0);
+ }
+
+ public BlockDoubleMudBrickSlab(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return MUD_BRICK_DOUBLE_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Double Mud Brick Slab";
+ }
+ @Override
+ public int getSingleSlabId() {
+ return MUD_BRICK_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoublePlant.java b/src/main/java/cn/nukkit/block/BlockDoublePlant.java
index a5686af96d8..306779e0f87 100644
--- a/src/main/java/cn/nukkit/block/BlockDoublePlant.java
+++ b/src/main/java/cn/nukkit/block/BlockDoublePlant.java
@@ -2,19 +2,20 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSeedsWheat;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.level.Level;
import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/11/23 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockDoublePlant extends BlockFlowable {
+
public static final int SUNFLOWER = 0;
public static final int LILAC = 1;
public static final int TALL_GRASS = 2;
@@ -23,7 +24,7 @@ public class BlockDoublePlant extends BlockFlowable {
public static final int PEONY = 5;
public static final int TOP_HALF_BITMASK = 0x8;
- private static final String[] NAMES = new String[]{
+ private static final String[] NAMES = {
"Sunflower",
"Lilac",
"Double Tallgrass",
@@ -47,7 +48,8 @@ public int getId() {
@Override
public boolean canBeReplaced() {
- return this.getDamage() == TALL_GRASS || this.getDamage() == LARGE_FERN;
+ int damage = this.getDamage() & 0x7;
+ return damage == TALL_GRASS || damage == LARGE_FERN;
}
@Override
@@ -83,8 +85,9 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
int id = down.getId();
if (up.getId() == AIR && (id == GRASS || id == DIRT || id == PODZOL || id == FARMLAND || id == MYCELIUM)) {
- this.getLevel().setBlock(block, this, true, false); // If we update the bottom half, it will drop the item because there isn't a flower block above
- this.getLevel().setBlock(up, Block.get(BlockID.DOUBLE_PLANT, getDamage() ^ TOP_HALF_BITMASK), true, true);
+ // Place top half first in order to call block updates on bottom part, but do not update to prevent breaking.
+ this.getLevel().setBlock(up, Block.get(DOUBLE_PLANT, getDamage() ^ TOP_HALF_BITMASK), true, false);
+ this.getLevel().setBlock(block, this, true, true);
return true;
}
@@ -93,10 +96,11 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public boolean onBreak(Item item) {
- Block down = down();
-
if ((this.getDamage() & TOP_HALF_BITMASK) == TOP_HALF_BITMASK) { // Top half
- this.getLevel().useBreakOn(down);
+ Block down = down();
+ if (down instanceof BlockDoublePlant) {
+ this.getLevel().useBreakOn(down);
+ }
} else {
this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, true);
}
@@ -107,27 +111,27 @@ public boolean onBreak(Item item) {
@Override
public Item[] getDrops(Item item) {
if ((this.getDamage() & TOP_HALF_BITMASK) != TOP_HALF_BITMASK) {
- switch (this.getDamage() & 0x07) {
+ int type = this.getDamage() & 0x07;
+ switch (type) {
case TALL_GRASS:
case LARGE_FERN:
- boolean dropSeeds = ThreadLocalRandom.current().nextInt(10) == 0;
+ boolean dropSeeds = Utils.random.nextInt(10) == 0;
if (item.isShears()) {
- //todo enchantment
if (dropSeeds) {
return new Item[]{
- new ItemSeedsWheat(0, 1),
- toItem()
+ Item.get(Item.WHEAT_SEEDS),
+ Item.get(Item.TALL_GRASS, type == LARGE_FERN ? 2 : 1, 2)
};
} else {
return new Item[]{
- toItem()
+ Item.get(Item.TALL_GRASS, type == LARGE_FERN ? 2 : 1, 2)
};
}
}
if (dropSeeds) {
return new Item[]{
- new ItemSeedsWheat()
+ Item.get(Item.WHEAT_SEEDS)
};
} else {
return new Item[0];
@@ -152,22 +156,23 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.getId() == Item.DYE && item.getDamage() == 0x0f) { //Bone meal
- switch (this.getDamage() & 0x07) {
- case SUNFLOWER:
- case LILAC:
- case ROSE_BUSH:
- case PEONY:
- if (player != null && (player.gamemode & 0x01) == 0) {
- item.count--;
- }
- this.level.addParticle(new BoneMealParticle(this));
- this.level.dropItem(this, this.toItem());
- }
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ int type = this.getDamage() & 0x07;
+ if (type == SUNFLOWER || type == LILAC || type == ROSE_BUSH || type == PEONY) { // Flower
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ this.level.addParticle(new BoneMealParticle(this));
+ this.level.dropItem(this, this.toItem());
+ }
return true;
}
return false;
}
+
+ public Item toItem() {
+ return new ItemBlock(this, this.getDamage() & 0x07, 1);
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlab.java b/src/main/java/cn/nukkit/block/BlockDoubleSlab.java
index c23819a90cc..4cb9f38000e 100644
--- a/src/main/java/cn/nukkit/block/BlockDoubleSlab.java
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlab.java
@@ -1,14 +1,11 @@
package cn.nukkit.block;
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
-import cn.nukkit.utils.BlockColor;
-
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
-public class BlockDoubleSlab extends BlockSolidMeta {
+public class BlockDoubleSlab extends BlockDoubleSlabStone {
+
public static final int STONE = 0;
public static final int SANDSTONE = 1;
public static final int WOODEN = 2;
@@ -25,67 +22,4 @@ public BlockDoubleSlab() {
public BlockDoubleSlab(int meta) {
super(meta);
}
-
- @Override
- public int getId() {
- return DOUBLE_SLAB;
- }
-
- //todo hardness and residence
-
- @Override
- public double getHardness() {
- return 2;
- }
-
- @Override
- public int getToolType() {
- return ItemTool.TYPE_PICKAXE;
- }
-
- @Override
- public String getName() {
- String[] names = new String[]{
- "Stone",
- "Sandstone",
- "Wooden",
- "Cobblestone",
- "Brick",
- "Stone Brick",
- "Quartz",
- "Nether Brick"
- };
- return "Double " + names[this.getDamage() & 0x07] + " Slab";
- }
-
- @Override
- public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
- return new Item[]{
- Item.get(Item.SLAB, this.getDamage() & 0x07, 2)
- };
- } else {
- return new Item[0];
- }
- }
-
- @Override
- public BlockColor getColor() {
- switch (this.getDamage() & 0x07) {
- case BlockDoubleSlab.WOODEN:
- return BlockColor.WOOD_BLOCK_COLOR;
- default:
- case BlockDoubleSlab.COBBLESTONE:
- case BlockDoubleSlab.BRICK:
- case BlockDoubleSlab.STONE_BRICK:
- case BlockDoubleSlab.STONE:
- return BlockColor.STONE_BLOCK_COLOR;
- case BlockDoubleSlab.SANDSTONE:
- return BlockColor.SAND_BLOCK_COLOR;
- case BlockDoubleSlab.QUARTZ:
- return BlockColor.QUARTZ_BLOCK_COLOR;
- case BlockDoubleSlab.NETHER_BRICK:
- return BlockColor.NETHERRACK_BLOCK_COLOR;
- }
- }
}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabBase.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabBase.java
new file mode 100644
index 00000000000..92f341b7832
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabBase.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public abstract class BlockDoubleSlabBase extends BlockSolidMeta {
+
+ public BlockDoubleSlabBase() {
+ this(0);
+ }
+
+ public BlockDoubleSlabBase(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Double " + this.getSlabName() + " Slab";
+ }
+
+ public abstract String getSlabName();
+
+ public abstract int getSingleSlabId();
+
+ public abstract int getItemDamage();
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getSingleSlabId(), this.getItemDamage()), this.getItemDamage(), 1);
+ }
+
+ protected boolean isCorrectTool(Item item) {
+ return canHarvestWithHand() || canHarvest(item);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (isCorrectTool(item)) {
+ Item slab = toItem();
+ slab.setCount(2);
+ return new Item[]{ slab };
+ } else {
+ return new Item[0];
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabBlackstone.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabBlackstone.java
new file mode 100644
index 00000000000..b0a74134e98
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabBlackstone.java
@@ -0,0 +1,60 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabBlackstone extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabBlackstone() {
+ this(0);
+ }
+
+ protected BlockDoubleSlabBlackstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Double Blackstone Slab";
+ }
+
+ @Override
+ public int getId() {
+ return BLACKSTONE_DOUBLE_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return BLACKSTONE_SLAB;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabBlackstonePolished.java
new file mode 100644
index 00000000000..cc9da667057
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabBlackstonePolished.java
@@ -0,0 +1,60 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabBlackstonePolished extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabBlackstonePolished() {
+ this(0);
+ }
+
+ public BlockDoubleSlabBlackstonePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_DOUBLE_SLAB;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return POLISHED_BLACKSTONE_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Polished Blackstone";
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6.0;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabBrickBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabBrickBlackstonePolished.java
new file mode 100644
index 00000000000..689d81e3813
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabBrickBlackstonePolished.java
@@ -0,0 +1,32 @@
+package cn.nukkit.block;
+
+public class BlockDoubleSlabBrickBlackstonePolished extends BlockDoubleSlabBlackstonePolished {
+
+ public BlockDoubleSlabBrickBlackstonePolished() {
+ this(0);
+ }
+
+ public BlockDoubleSlabBrickBlackstonePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return POLISHED_BLACKSTONE_BRICK_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Polished Blackstone Brick";
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabBrickDeepslate.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabBrickDeepslate.java
new file mode 100644
index 00000000000..1ef60c38691
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabBrickDeepslate.java
@@ -0,0 +1,65 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabBrickDeepslate extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabBrickDeepslate() {
+ this(0);
+ }
+
+ protected BlockDoubleSlabBrickDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_BRICK_DOUBLE_SLAB;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return DEEPSLATE_BRICK_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Double Deepslate Brick Slab";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperBase.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperBase.java
new file mode 100644
index 00000000000..a5489b9b554
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperBase.java
@@ -0,0 +1,88 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public abstract class BlockDoubleSlabCopperBase extends BlockDoubleSlabBase implements Waxable, Oxidizable {
+
+ public BlockDoubleSlabCopperBase(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_STONE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ return Waxable.super.onActivate(item, player)
+ || Oxidizable.super.onActivate(item, player);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ return Oxidizable.super.onUpdate(type);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+
+ @Override
+ public Block getStateWithOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ return Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel), this.getDamage());
+ }
+
+ @Override
+ public boolean setOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ if (this.getOxidizationLevel().equals(oxidizationLevel)) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel)));
+ }
+
+ @Override
+ public boolean setWaxed(boolean waxed) {
+ if (this.isWaxed() == waxed) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(getCopperId(waxed, getOxidizationLevel())));
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return false;
+ }
+
+ protected abstract int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel);
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCut.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCut.java
new file mode 100644
index 00000000000..6982605b262
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCut.java
@@ -0,0 +1,68 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+
+public class BlockDoubleSlabCopperCut extends BlockDoubleSlabCopperBase {
+
+ public BlockDoubleSlabCopperCut() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCut(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ String name = "";
+ if (this.isWaxed()) {
+ name += "Waxed ";
+ }
+
+ OxidizationLevel oxidizationLevel = this.getOxidizationLevel();
+ if (oxidizationLevel != OxidizationLevel.UNAFFECTED) {
+ String oxidationName = oxidizationLevel.name();
+ name += oxidationName.charAt(0) + oxidationName.substring(1).toLowerCase();
+ }
+ return name + " Cut Copper";
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ protected int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel) {
+ if (oxidizationLevel == null) {
+ return getId();
+ }
+ switch (oxidizationLevel) {
+ case UNAFFECTED:
+ return waxed? WAXED_DOUBLE_CUT_COPPER_SLAB : DOUBLE_CUT_COPPER_SLAB;
+ case EXPOSED:
+ return waxed? WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB : EXPOSED_DOUBLE_CUT_COPPER_SLAB;
+ case WEATHERED:
+ return waxed? WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB : WEATHERED_DOUBLE_CUT_COPPER_SLAB;
+ case OXIDIZED:
+ return waxed? WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB : OXIDIZED_DOUBLE_CUT_COPPER_SLAB;
+ default:
+ return getId();
+ }
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.UNAFFECTED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutExposed.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutExposed.java
new file mode 100644
index 00000000000..85cec004911
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutExposed.java
@@ -0,0 +1,35 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabCopperCutExposed extends BlockDoubleSlabCopperCut {
+
+ public BlockDoubleSlabCopperCutExposed() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCutExposed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return EXPOSED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getId() {
+ return EXPOSED_DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.EXPOSED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.LIGHT_GRAY_TERRACOTA_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutExposedWaxed.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutExposedWaxed.java
new file mode 100644
index 00000000000..52f8b6ee48c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutExposedWaxed.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockDoubleSlabCopperCutExposedWaxed extends BlockDoubleSlabCopperCutExposed {
+
+ public BlockDoubleSlabCopperCutExposedWaxed() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCutExposedWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return WAXED_EXPOSED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutOxidized.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutOxidized.java
new file mode 100644
index 00000000000..aa69ae2dcab
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutOxidized.java
@@ -0,0 +1,35 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabCopperCutOxidized extends BlockDoubleSlabCopperCut {
+
+ public BlockDoubleSlabCopperCutOxidized() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCutOxidized(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return OXIDIZED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getId() {
+ return OXIDIZED_DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.OXIDIZED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_NYLIUM_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutOxidizedWaxed.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutOxidizedWaxed.java
new file mode 100644
index 00000000000..feb03bff0ad
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutOxidizedWaxed.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockDoubleSlabCopperCutOxidizedWaxed extends BlockDoubleSlabCopperCutOxidized {
+
+ public BlockDoubleSlabCopperCutOxidizedWaxed() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCutOxidizedWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return WAXED_OXIDIZED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWaxed.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWaxed.java
new file mode 100644
index 00000000000..233a09a151c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWaxed.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockDoubleSlabCopperCutWaxed extends BlockDoubleSlabCopperCut {
+
+ public BlockDoubleSlabCopperCutWaxed() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCutWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return WAXED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWeathered.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWeathered.java
new file mode 100644
index 00000000000..ba6ea011121
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWeathered.java
@@ -0,0 +1,35 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabCopperCutWeathered extends BlockDoubleSlabCopperCut {
+
+ public BlockDoubleSlabCopperCutWeathered() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCutWeathered(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return WEATHERED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getId() {
+ return WEATHERED_DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.WEATHERED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWeatheredWaxed.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWeatheredWaxed.java
new file mode 100644
index 00000000000..e5d05579f49
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCopperCutWeatheredWaxed.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockDoubleSlabCopperCutWeatheredWaxed extends BlockDoubleSlabCopperCutWeathered {
+
+ public BlockDoubleSlabCopperCutWeatheredWaxed() {
+ this(0);
+ }
+
+ public BlockDoubleSlabCopperCutWeatheredWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return WAXED_WEATHERED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabCrimson.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabCrimson.java
new file mode 100644
index 00000000000..34673b88eff
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabCrimson.java
@@ -0,0 +1,65 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabCrimson extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabCrimson() {
+ super(0);
+ }
+
+ public BlockDoubleSlabCrimson(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_DOUBLE_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Crimson";
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return CRIMSON_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabDeepslateCobbled.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabDeepslateCobbled.java
new file mode 100644
index 00000000000..7c420775846
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabDeepslateCobbled.java
@@ -0,0 +1,32 @@
+package cn.nukkit.block;
+
+public class BlockDoubleSlabDeepslateCobbled extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabDeepslateCobbled() {
+ this(0);
+ }
+
+ public BlockDoubleSlabDeepslateCobbled(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return COBBLED_DEEPSLATE_DOUBLE_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Cobbled Deepslate";
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return COBBLED_DEEPSLATE_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabDeepslatePolished.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabDeepslatePolished.java
new file mode 100644
index 00000000000..09eb2fc4387
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabDeepslatePolished.java
@@ -0,0 +1,65 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabDeepslatePolished extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabDeepslatePolished() {
+ this(0);
+ }
+
+ public BlockDoubleSlabDeepslatePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_DEEPSLATE_DOUBLE_SLAB;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return POLISHED_DEEPSLATE_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Double Polished Deepslate Slab";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabRedSandstone.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabRedSandstone.java
index 176c645a37e..3c559dfe7b5 100644
--- a/src/main/java/cn/nukkit/block/BlockDoubleSlabRedSandstone.java
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabRedSandstone.java
@@ -1,14 +1,23 @@
package cn.nukkit.block;
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
/**
* Created by CreeperFace on 26. 11. 2016.
*/
-public class BlockDoubleSlabRedSandstone extends BlockSolidMeta {
+public class BlockDoubleSlabRedSandstone extends BlockDoubleSlabBase {
+
+ private static final String[] NAMES = new String[]{
+ "Red Sandstone",
+ "Purpur",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ };
public BlockDoubleSlabRedSandstone() {
this(0);
@@ -23,6 +32,11 @@ public int getId() {
return DOUBLE_RED_SANDSTONE_SLAB;
}
+ @Override
+ public int getSingleSlabId() {
+ return RED_SANDSTONE_SLAB;
+ }
+
@Override
public double getResistance() {
return 30;
@@ -39,35 +53,13 @@ public int getToolType() {
}
@Override
- public String getName() {
- String[] names = new String[]{
- "Red Sandstone",
- "Purpur",
- "",
- "",
- "",
- "",
- "",
- ""
- };
-
- return "Double " + names[this.getDamage() & 0x07] + " Slab";
+ public String getSlabName() {
+ return NAMES[this.getDamage() & 0x07];
}
@Override
- public Item toItem() {
- return new ItemBlock(Block.get(BlockID.RED_SANDSTONE_SLAB), this.getDamage() & 0x07);
- }
-
- @Override
- public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
- return new Item[]{
- Item.get(Item.RED_SANDSTONE_SLAB, this.getDamage() & 0x07, 2)
- };
- } else {
- return new Item[0];
- }
+ public int getItemDamage() {
+ return this.getDamage() & 0x07;
}
@Override
@@ -86,4 +78,4 @@ public BlockColor getColor() {
return BlockColor.STONE_BLOCK_COLOR;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabStone.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabStone.java
index 6828f735a1e..f2f47b4b547 100644
--- a/src/main/java/cn/nukkit/block/BlockDoubleSlabStone.java
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabStone.java
@@ -1,15 +1,14 @@
package cn.nukkit.block;
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
-public class BlockDoubleSlabStone extends BlockSolidMeta {
+public class BlockDoubleSlabStone extends BlockDoubleSlabBase {
+
public static final int STONE = 0;
public static final int SANDSTONE = 1;
public static final int WOODEN = 2;
@@ -19,6 +18,17 @@ public class BlockDoubleSlabStone extends BlockSolidMeta {
public static final int QUARTZ = 6;
public static final int NETHER_BRICK = 7;
+ private static final String[] NAMES = new String[]{
+ "Stone",
+ "Sandstone",
+ "Wooden",
+ "Cobblestone",
+ "Brick",
+ "Stone Brick",
+ "Quartz",
+ "Nether Brick"
+ };
+
public BlockDoubleSlabStone() {
this(0);
}
@@ -32,6 +42,11 @@ public int getId() {
return DOUBLE_SLAB;
}
+ @Override
+ public int getSingleSlabId() {
+ return STONE_SLAB;
+ }
+
@Override
public double getResistance() {
return getToolType() > ItemTool.TIER_WOODEN ? 30 : 15;
@@ -48,45 +63,24 @@ public int getToolType() {
}
@Override
- public String getName() {
- String[] names = new String[]{
- "Stone",
- "Sandstone",
- "Wooden",
- "Cobblestone",
- "Brick",
- "Stone Brick",
- "Quartz",
- "Nether Brick"
- };
- return "Double " + names[this.getDamage() & 0x07] + " Slab";
+ public String getSlabName() {
+ return NAMES[this.getDamage() & 0x07];
}
@Override
- public Item toItem() {
- return new ItemBlock(Block.get(BlockID.STONE_SLAB), this.getDamage() & 0x07);
- }
-
- @Override
- public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
- return new Item[]{
- Item.get(Item.SLAB, this.getDamage() & 0x07, 2)
- };
- } else {
- return new Item[0];
- }
+ public int getItemDamage() {
+ return this.getDamage() & 0x07;
}
@Override
public BlockColor getColor() {
switch (this.getDamage() & 0x07) {
default:
- case BlockDoubleSlabStone.STONE:
- case BlockDoubleSlabStone.COBBLESTONE:
- case BlockDoubleSlabStone.BRICK:
- case BlockDoubleSlabStone.STONE_BRICK:
- return BlockColor.STONE_BLOCK_COLOR;
+ case BlockDoubleSlabStone.STONE:
+ case BlockDoubleSlabStone.COBBLESTONE:
+ case BlockDoubleSlabStone.BRICK:
+ case BlockDoubleSlabStone.STONE_BRICK:
+ return BlockColor.STONE_BLOCK_COLOR;
case BlockDoubleSlabStone.SANDSTONE:
return BlockColor.SAND_BLOCK_COLOR;
case BlockDoubleSlabStone.WOODEN:
@@ -102,4 +96,4 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabStone3.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabStone3.java
new file mode 100644
index 00000000000..42ddea58f7d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabStone3.java
@@ -0,0 +1,79 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabStone3 extends BlockDoubleSlabBase {
+
+ public static final int END_STONE_BRICKS = 0;
+ public static final int SMOOTH_RED_SANDSTONE = 1;
+ public static final int POLISHED_ANDESITE = 2;
+ public static final int ANDESITE = 3;
+ public static final int DIORITE = 4;
+ public static final int POLISHED_DIORITE = 5;
+ public static final int GRANITE = 6;
+ public static final int POLISHED_GRANITE = 7;
+
+ private static final String[] NAMES = new String[]{
+ "End Stone Brick",
+ "Smooth Red Sandstone",
+ "Polished Andesite",
+ "Andesite",
+ "Diorite",
+ "Polished Diorite",
+ "Granite",
+ "Polisehd Granite"
+ };
+
+ public BlockDoubleSlabStone3() {
+ this(0);
+ }
+
+ public BlockDoubleSlabStone3(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getSlabName() {
+ return NAMES[this.getDamage() & 0x07];
+ }
+
+ @Override
+ public int getId() {
+ return DOUBLE_STONE_SLAB3;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return STONE_SLAB3;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return this.getDamage() & 0x07;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ switch (this.getDamage() & 0x07) {
+ case END_STONE_BRICKS:
+ return BlockColor.SAND_BLOCK_COLOR;
+ case SMOOTH_RED_SANDSTONE:
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ default:
+ case POLISHED_ANDESITE:
+ case ANDESITE:
+ return BlockColor.STONE_BLOCK_COLOR;
+ case DIORITE:
+ case POLISHED_DIORITE:
+ return BlockColor.QUARTZ_BLOCK_COLOR;
+ case GRANITE:
+ case POLISHED_GRANITE:
+ return BlockColor.DIRT_BLOCK_COLOR;
+ }
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabStone4.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabStone4.java
new file mode 100644
index 00000000000..d18509f2f30
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabStone4.java
@@ -0,0 +1,89 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabStone4 extends BlockDoubleSlabBase {
+
+ private static final String[] NAMES = new String[]{
+ "Mossy Stone Brick",
+ "Smooth Quartz",
+ "Stone",
+ "Cut Sandstone",
+ "Cut Red Sandstone",
+ };
+
+ public static final int MOSSY_STONE_BRICKS = 0;
+ public static final int SMOOTH_QUARTZ = 1;
+ public static final int STONE = 2;
+ public static final int CUT_SANDSTONE = 3;
+ public static final int CUT_RED_SANDSTONE = 4;
+
+ public BlockDoubleSlabStone4() {
+ this(0);
+ }
+
+ public BlockDoubleSlabStone4(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DOUBLE_STONE_SLAB4;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return STONE_SLAB4;
+ }
+
+ @Override
+ public double getResistance() {
+ return this.getToolType() > ItemTool.TIER_WOODEN ? 30 : 15;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public String getSlabName() {
+ int variant = this.getDamage() & 0x07;
+ if (variant >= NAMES.length) {
+ return NAMES[0];
+ }
+ return NAMES[variant];
+ }
+
+ @Override
+ public int getItemDamage() {
+ return this.getDamage() & 0x07;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ switch (this.getDamage() & 0x07) {
+ default:
+ case MOSSY_STONE_BRICKS:
+ case STONE:
+ return BlockColor.STONE_BLOCK_COLOR;
+ case SMOOTH_QUARTZ:
+ return BlockColor.QUARTZ_BLOCK_COLOR;
+ case CUT_SANDSTONE:
+ return BlockColor.SAND_BLOCK_COLOR;
+ case CUT_RED_SANDSTONE:
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabTileDeepslate.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabTileDeepslate.java
new file mode 100644
index 00000000000..4ae1646a3eb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabTileDeepslate.java
@@ -0,0 +1,65 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabTileDeepslate extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabTileDeepslate() {
+ this(0);
+ }
+
+ protected BlockDoubleSlabTileDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_TILE_DOUBLE_SLAB;
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return DEEPSLATE_TILE_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Double Deepslate Tile Slab";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabWarped.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabWarped.java
new file mode 100644
index 00000000000..fea73e3c109
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabWarped.java
@@ -0,0 +1,65 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDoubleSlabWarped extends BlockDoubleSlabBase {
+
+ public BlockDoubleSlabWarped() {
+ super(0);
+ }
+
+ public BlockDoubleSlabWarped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_DOUBLE_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Warped";
+ }
+
+ @Override
+ public int getSingleSlabId() {
+ return WARPED_SLAB;
+ }
+
+ @Override
+ public int getItemDamage() {
+ return 0;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CYAN_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDoubleSlabWood.java b/src/main/java/cn/nukkit/block/BlockDoubleSlabWood.java
index ab83b36c568..98eeb0d8f2b 100644
--- a/src/main/java/cn/nukkit/block/BlockDoubleSlabWood.java
+++ b/src/main/java/cn/nukkit/block/BlockDoubleSlabWood.java
@@ -1,7 +1,5 @@
package cn.nukkit.block;
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
@@ -9,7 +7,18 @@
* Created on 2015/12/2 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
-public class BlockDoubleSlabWood extends BlockSolidMeta {
+public class BlockDoubleSlabWood extends BlockDoubleSlabBase {
+
+ private static final String[] NAMES = new String[]{
+ "Oak",
+ "Spruce",
+ "Birch",
+ "Jungle",
+ "Acacia",
+ "Dark Oak",
+ "",
+ ""
+ };
public BlockDoubleSlabWood() {
this(0);
@@ -24,6 +33,11 @@ public int getId() {
return DOUBLE_WOOD_SLAB;
}
+ @Override
+ public int getSingleSlabId() {
+ return WOOD_SLAB;
+ }
+
@Override
public double getHardness() {
return 2;
@@ -40,37 +54,21 @@ public int getToolType() {
}
@Override
- public String getName() {
- String[] names = new String[]{
- "Oak",
- "Spruce",
- "Birch",
- "Jungle",
- "Acacia",
- "Dark Oak",
- "",
- ""
- };
- return "Double " + names[this.getDamage() & 0x07] + " Slab";
+ public String getSlabName() {
+ return NAMES[this.getDamage() & 0x07];
}
@Override
- public Item toItem() {
- return new ItemBlock(Block.get(BlockID.WOODEN_SLAB), this.getDamage() & 0x07);
- }
-
- public Item[] getDrops(Item item) {
- return new Item[]{
- Item.get(Item.WOOD_SLAB, this.getDamage() & 0x07, 2)
- };
+ public int getItemDamage() {
+ return this.getDamage() & 0x07;
}
@Override
public BlockColor getColor() {
- switch(this.getDamage() & 0x07){
+ switch (this.getDamage() & 0x07) {
default:
- case 0: //OAK
- return BlockColor.WOOD_BLOCK_COLOR;
+ case 0: //OAK
+ return BlockColor.WOOD_BLOCK_COLOR;
case 1: //SPRUCE
return BlockColor.SPRUCE_BLOCK_COLOR;
case 2: //BIRCH
diff --git a/src/main/java/cn/nukkit/block/BlockDragonEgg.java b/src/main/java/cn/nukkit/block/BlockDragonEgg.java
index 8e02b46d3b6..dc6465562c6 100644
--- a/src/main/java/cn/nukkit/block/BlockDragonEgg.java
+++ b/src/main/java/cn/nukkit/block/BlockDragonEgg.java
@@ -4,14 +4,10 @@
import cn.nukkit.level.Level;
import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
public class BlockDragonEgg extends BlockFallable {
- public BlockDragonEgg() {
- }
-
@Override
public String getName() {
return "Dragon Egg";
@@ -29,7 +25,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 45;
+ return 9;
}
@Override
@@ -39,7 +35,7 @@ public int getLightLevel() {
@Override
public BlockColor getColor() {
- return BlockColor.OBSIDIAN_BLOCK_COLOR;
+ return BlockColor.BLACK_BLOCK_COLOR;
}
@Override
@@ -56,15 +52,13 @@ public int onUpdate(int type) {
}
public void teleport() {
- ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < 1000; ++i) {
- Block to = this.getLevel().getBlock(this.add(random.nextInt(-16, 16), random.nextInt(-16, 16), random.nextInt(-16, 16)));
+ Block to = this.getLevel().getBlock(this.add(Utils.random.nextInt(-16, 16), Utils.random.nextInt(-16, 16), Utils.random.nextInt(-16, 16)));
if (to.getId() == AIR) {
BlockFromToEvent event = new BlockFromToEvent(this, to);
this.level.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) return;
to = event.getTo();
-
int diffX = this.getFloorX() - to.getFloorX();
int diffY = this.getFloorY() - to.getFloorY();
int diffZ = this.getFloorZ() - to.getFloorZ();
@@ -74,11 +68,26 @@ public void teleport() {
pk.x = this.getFloorX();
pk.y = this.getFloorY();
pk.z = this.getFloorZ();
- this.getLevel().addChunkPacket(this.getFloorX() >> 4, this.getFloorZ() >> 4, pk);
+ this.getLevel().addChunkPacket(this.getChunkX(), this.getChunkZ(), pk);
this.getLevel().setBlock(this, get(AIR), true);
this.getLevel().setBlock(to, this, true);
return;
}
}
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean alwaysDropsOnExplosion() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockDriedKelpBlock.java b/src/main/java/cn/nukkit/block/BlockDriedKelpBlock.java
new file mode 100644
index 00000000000..eb26d53c09b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDriedKelpBlock.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockDriedKelpBlock extends Block {
+
+ @Override
+ public String getName() {
+ return "Dried Kelp Block";
+ }
+
+ @Override
+ public int getId() {
+ return DRIED_KELP_BLOCK;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ public double getHardness() {
+ return 0.5;
+ }
+
+ public double getResistance() {
+ return 2.5;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 30;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GREEN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDripleafBig.java b/src/main/java/cn/nukkit/block/BlockDripleafBig.java
new file mode 100644
index 00000000000..56bd4307239
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDripleafBig.java
@@ -0,0 +1,287 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.BlockPropertiesHelper;
+import cn.nukkit.block.properties.DripleafTilt;
+import cn.nukkit.block.properties.VanillaProperties;
+import cn.nukkit.customblock.properties.BlockProperties;
+import cn.nukkit.customblock.properties.BlockProperty;
+import cn.nukkit.customblock.properties.BooleanBlockProperty;
+import cn.nukkit.customblock.properties.EnumBlockProperty;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.event.Event;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.event.player.PlayerInteractEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.Sound;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.level.particle.DestroyBlockParticle;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Faceable;
+
+public class BlockDripleafBig extends BlockSolidMeta implements BlockPropertiesHelper, Faceable {
+
+ public static final BlockProperty TILT_PROPERTY = new EnumBlockProperty<>("big_dripleaf_tilt", false, DripleafTilt.class);
+ public static final BlockProperty HEAD_PROPERTY = new BooleanBlockProperty("big_dripleaf_head", false);
+
+ private static final BlockProperties PROPERTIES = new BlockProperties(TILT_PROPERTY, HEAD_PROPERTY, VanillaProperties.DIRECTION);
+
+ public BlockDripleafBig() {
+ this(0);
+ }
+
+ public BlockDripleafBig(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public BlockProperties getBlockProperties() {
+ return PROPERTIES;
+ }
+
+ @Override
+ public boolean canPlaceOn(Block floor, Position pos) {
+ switch (floor.getId()) {
+ case CLAY_BLOCK:
+ case DIRT:
+ case FARMLAND:
+ case GRASS:
+ case MOSS_BLOCK:
+ case MYCELIUM:
+ case BIG_DRIPLEAF:
+ case WATER:
+ case STILL_WATER:
+ return super.canPlaceOn(floor, pos);
+ }
+
+ if (floor.getLayer() == LAYER_WATERLOGGED && floor.getId() == AIR) {
+ return super.canPlaceOn(floor, pos);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ Block down = block.down();
+ if (!this.canPlaceOn(down, target)) {
+ return false;
+ }
+
+ if (down.getId() == BIG_DRIPLEAF) {
+ BlockDripleafBig floor = (BlockDripleafBig) down;
+ floor.setHasHead(false);
+ this.getLevel().setBlock(floor, floor, true, true);
+ this.setDirection(floor.getDirection());
+ } else {
+ this.setDirection(player.getDirection().getOpposite());
+ }
+
+ this.setTilt(DripleafTilt.NONE);
+ this.setHasHead(true);
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+
+ @Override
+ public boolean onBreak(Item item, Player player) {
+ Block down = this.down();
+ while (down instanceof BlockDripleafBig) {
+ this.getLevel().setBlock(down, Block.get(BlockID.AIR), true, true);
+ this.getLevel().addParticle(new DestroyBlockParticle(down.add(0.5), down));
+ down = down.down();
+ }
+
+ Block up = this.up();
+ while (up instanceof BlockDripleafBig) {
+ this.getLevel().setBlock(up, Block.get(BlockID.AIR), true, true);
+ this.getLevel().addParticle(new DestroyBlockParticle(up.add(0.5), up));
+ up = up.up();
+ }
+
+ return super.onBreak(item, player);
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (!(entity instanceof Player) || !this.hasHead() || this.getTilt() != DripleafTilt.NONE) {
+ return;
+ }
+
+ Event event = new PlayerInteractEvent((Player) entity, null, this, null, PlayerInteractEvent.Action.PHYSICAL);
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return;
+ }
+
+ this.setTiltAndScheduleTick(DripleafTilt.UNSTABLE, false);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type != Level.BLOCK_UPDATE_SCHEDULED) {
+ return super.onUpdate(type);
+ }
+
+ DripleafTilt tilt = this.getTilt();
+ if (tilt == DripleafTilt.UNSTABLE) {
+ this.setTiltAndScheduleTick(DripleafTilt.PARTIAL_TILT, true);
+ } else if (tilt == DripleafTilt.PARTIAL_TILT) {
+ this.setTiltAndScheduleTick(DripleafTilt.FULL_TILT, true);
+ } else if (tilt == DripleafTilt.FULL_TILT) {
+ this.resetTilt();
+ }
+ return 0;
+ }
+
+ private void setTiltAndScheduleTick(DripleafTilt tilt, boolean sound) {
+ this.setTilt(tilt);
+ this.getLevel().setBlock(this, this, true, true);
+
+ if (sound) {
+ this.getLevel().addSound(this, Sound.TILT_DOWN_BIG_DRIPLEAF);
+ }
+
+ int delay = tilt.getNetxStateDelay();
+ if (delay != -1) {
+ this.getLevel().scheduleUpdate(this, delay);
+ }
+ }
+
+ private void resetTilt() {
+ this.setTilt(DripleafTilt.NONE);
+ this.getLevel().setBlock(this, this, true, true);
+ this.getLevel().addSound(this, Sound.TILT_UP_BIG_DRIPLEAF);
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() != Item.DYE || item.getDamage() != ItemDye.BONE_MEAL) {
+ return false;
+ }
+
+ BlockGrowEvent event = new BlockGrowEvent(this, Block.get(BlockID.BIG_DRIPLEAF, 0, this));
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ Block up = this;
+ BlockDripleafBig highestPart = null;
+ while (up instanceof BlockDripleafBig) {
+ highestPart = (BlockDripleafBig) up;
+ up = up.up();
+ }
+
+ if (highestPart == null) {
+ return false;
+ }
+
+ highestPart.setHasHead(false);
+ this.getLevel().setBlock(highestPart, highestPart, false, true);
+
+ BlockDripleafBig block = (BlockDripleafBig) this.clone();
+ block.setHasHead(true);
+
+ this.getLevel().setBlock(highestPart.up(), block, false, true);
+
+ this.level.addParticle(new BoneMealParticle(this));
+
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ return true;
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return super.recalculateBoundingBox(); // TODO:
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ @Override
+ public String getName() {
+ return "Big Dripleaf";
+ }
+
+ @Override
+ public int getId() {
+ return BIG_DRIPLEAF;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.1;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.1;
+ }
+
+ public void setTilt(DripleafTilt tilt) {
+ this.setPropertyValue(TILT_PROPERTY, tilt);
+ }
+
+ public DripleafTilt getTilt() {
+ return this.getPropertyValue(TILT_PROPERTY);
+ }
+
+ public void setHasHead(boolean value) {
+ this.setBooleanValue(HEAD_PROPERTY, value);
+ }
+
+ public boolean hasHead() {
+ return this.getBooleanValue(HEAD_PROPERTY);
+ }
+
+ public void setDirection(BlockFace blockFace) {
+ this.setPropertyValue(VanillaProperties.DIRECTION, blockFace);
+ }
+
+ public BlockFace getDirection() {
+ return this.getPropertyValue(VanillaProperties.DIRECTION);
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return this.getDirection();
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean isTransparent() {
+ return !this.hasHead() || !this.getTilt().isStable();
+ }
+
+ @Override
+ public boolean isSolid() {
+ return this.hasHead() && this.getTilt().isStable();
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return !this.hasHead() || !this.getTilt().isStable();
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDripleafSmall.java b/src/main/java/cn/nukkit/block/BlockDripleafSmall.java
new file mode 100644
index 00000000000..df084d9909a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDripleafSmall.java
@@ -0,0 +1,197 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.BlockPropertiesHelper;
+import cn.nukkit.block.properties.VanillaProperties;
+import cn.nukkit.customblock.properties.BlockProperties;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.level.particle.DestroyBlockParticle;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Faceable;
+
+public class BlockDripleafSmall extends BlockFlowable implements BlockPropertiesHelper, Faceable {
+
+ private static final BlockProperties PROPERTIES = new BlockProperties(VanillaProperties.UPPER_BLOCK, VanillaProperties.DIRECTION);
+
+ public BlockDripleafSmall() {
+ this(0);
+ }
+
+ public BlockDripleafSmall(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public BlockProperties getBlockProperties() {
+ return PROPERTIES;
+ }
+
+ @Override
+ public boolean canPlaceOn(Block floor, Position pos) {
+ switch (floor.getId()) {
+ case CLAY_BLOCK:
+ case DIRT:
+ case FARMLAND:
+ case GRASS:
+ case MOSS_BLOCK:
+ case MYCELIUM:
+ case SMALL_DRIPLEAF:
+ case WATER:
+ case STILL_WATER:
+ return super.canPlaceOn(floor, pos);
+ }
+
+ if (floor.getLayer() == LAYER_WATERLOGGED && floor.getId() == AIR) {
+ return super.canPlaceOn(floor, pos);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ Block down = block.down();
+ if (!this.canPlaceOn(down, target)) {
+ return false;
+ }
+
+ if (down.getId() == SMALL_DRIPLEAF) {
+ BlockDripleafSmall floor = (BlockDripleafSmall) down;
+ floor.setHasHead(false);
+ this.getLevel().setBlock(floor, floor, true, true);
+ this.setDirection(floor.getDirection());
+ } else {
+ this.setDirection(player.getDirection().getOpposite());
+ }
+
+ this.setHasHead(true);
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+
+ @Override
+ public boolean onBreak(Item item, Player player) {
+ Block down = this.down();
+ while (down instanceof BlockDripleafSmall) {
+ this.getLevel().setBlock(down, Block.get(BlockID.AIR), true, true);
+ this.getLevel().addParticle(new DestroyBlockParticle(down.add(0.5), down));
+ down = down.down();
+ }
+
+ Block up = this.up();
+ while (up instanceof BlockDripleafSmall) {
+ this.getLevel().setBlock(up, Block.get(BlockID.AIR), true, true);
+ this.getLevel().addParticle(new DestroyBlockParticle(up.add(0.5), up));
+ up = up.up();
+ }
+
+ return super.onBreak(item, player);
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() != Item.DYE || item.getDamage() != ItemDye.BONE_MEAL) {
+ return false;
+ }
+
+ BlockGrowEvent event = new BlockGrowEvent(this, Block.get(BlockID.BIG_DRIPLEAF, 0, this));
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ Block down = this.down();
+ while (down instanceof BlockDripleafSmall) {
+ BlockDripleafBig block = (BlockDripleafBig) Block.get(BlockID.BIG_DRIPLEAF);
+ block.setDirection(this.getDirection());
+ this.getLevel().setBlock(down, block, false, true);
+ down = down.down();
+ }
+
+ Block up = this;
+ while (up instanceof BlockDripleafSmall) {
+ BlockDripleafBig block = (BlockDripleafBig) Block.get(BlockID.BIG_DRIPLEAF);
+ block.setDirection(this.getDirection());
+ this.getLevel().setBlock(up, block, false, true);
+ up = up.up();
+ }
+
+ Block highestPart = this.getLevel().getBlock(up.down());
+ if (highestPart instanceof BlockDripleafBig) {
+ ((BlockDripleafBig) highestPart).setHasHead(true);
+ this.getLevel().setBlock(highestPart, highestPart, false, true);
+ }
+
+ this.level.addParticle(new BoneMealParticle(this));
+
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ return true;
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return null;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "Small Dripleaf";
+ }
+
+ @Override
+ public int getId() {
+ return SMALL_DRIPLEAF;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ public void setHasHead(boolean value) {
+ this.setBooleanValue(VanillaProperties.UPPER_BLOCK, value);
+ }
+
+ public boolean hasHead() {
+ return this.getBooleanValue(VanillaProperties.UPPER_BLOCK);
+ }
+
+ public void setDirection(BlockFace blockFace) {
+ this.setPropertyValue(VanillaProperties.DIRECTION, blockFace);
+ }
+
+ public BlockFace getDirection() {
+ return this.getPropertyValue(VanillaProperties.DIRECTION);
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return this.getDirection();
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockDripstone.java b/src/main/java/cn/nukkit/block/BlockDripstone.java
new file mode 100644
index 00000000000..4a5be4bde27
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDripstone.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockDripstone extends BlockSolid {
+
+ public BlockDripstone() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Dripstone Block";
+ }
+
+ @Override
+ public int getId() {
+ return DRIPSTONE_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ // TODO:
+ /*@Override
+ public boolean isLavaResistant() {
+ return true;
+ }*/
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockDropper.java b/src/main/java/cn/nukkit/block/BlockDropper.java
new file mode 100644
index 00000000000..07efd6f5e2b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockDropper.java
@@ -0,0 +1,242 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityDropper;
+import cn.nukkit.inventory.ContainerInventory;
+import cn.nukkit.inventory.Inventory;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.Vector3;
+import cn.nukkit.nbt.tag.StringTag;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import cn.nukkit.utils.Faceable;
+import cn.nukkit.utils.Utils;
+
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockDropper extends BlockSolidMeta implements Faceable {
+
+ public BlockDropper() {
+ this(0);
+ }
+
+ public BlockDropper(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DROPPER;
+ }
+
+ @Override
+ public String getName() {
+ return "Dropper";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 17.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (player != null) {
+ if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) {
+ double y = player.y + player.getEyeHeight();
+
+ if (y - this.y > 2) {
+ this.setDamage(BlockFace.UP.getIndex());
+ } else if (this.y - y > 0) {
+ this.setDamage(BlockFace.DOWN.getIndex());
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+ }
+
+ this.getLevel().setBlock(block, this, true);
+
+ BlockEntity.createBlockEntity(BlockEntity.DROPPER, this.getChunk(), BlockEntity.getDefaultCompound(this, BlockEntity.DROPPER));
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player == null) {
+ return false;
+ }
+
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (!(blockEntity instanceof BlockEntityDropper)) {
+ return false;
+ }
+
+ if (blockEntity.namedTag.contains("Lock") && blockEntity.namedTag.get("Lock") instanceof StringTag) {
+ if (!blockEntity.namedTag.getString("Lock").equals(item.getCustomName())) {
+ return true;
+ }
+ }
+
+ player.addWindow(((BlockEntityDropper) blockEntity).getInventory());
+ return true;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(Block.DROPPER));
+ }
+
+ public Vector3 getDispensePosition() {
+ BlockFace facing = getBlockFace();
+ return this.add(
+ 0.5 + 0.7 * facing.getXOffset(),
+ 0.5 + 0.7 * facing.getYOffset(),
+ 0.5 + 0.7 * facing.getZOffset()
+ );
+ }
+
+ public void dispense() {
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (!(blockEntity instanceof BlockEntityDropper)) {
+ return;
+ }
+
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_CLICK);
+
+ int r = 1;
+ int slot = -1;
+ Item target = null;
+
+ Inventory inv = ((BlockEntityDropper) blockEntity).getInventory();
+ for (Map.Entry entry : inv.getContents().entrySet()) {
+ Item item = entry.getValue();
+
+ if (!item.isNull() && Utils.random.nextInt(r++) == 0) {
+ target = item;
+ slot = entry.getKey();
+ }
+ }
+
+ if (target != null) {
+ target = target.clone();
+ drop(target);
+
+ target.count--;
+ inv.setItem(slot, target);
+ }
+ }
+
+ public void drop(Item item) {
+ BlockFace face = this.getBlockFace();
+ Vector3 dispensePos = this.getDispensePosition();
+
+ if (face.getAxis() == BlockFace.Axis.Y) {
+ dispensePos.y -= 0.125;
+ } else {
+ dispensePos.y -= 0.15625;
+ }
+
+ ThreadLocalRandom rand = ThreadLocalRandom.current();
+ Vector3 motion = new Vector3();
+
+ double offset = rand.nextDouble() * 0.1 + 0.2;
+
+ motion.x = face.getXOffset() * offset;
+ motion.y = 0.1;
+ motion.z = face.getZOffset() * offset;
+
+ motion.x += rand.nextGaussian() * 0.007499999832361937 * 6;
+ motion.y += rand.nextGaussian() * 0.007499999832361937 * 6;
+ motion.z += rand.nextGaussian() * 0.007499999832361937 * 6;
+
+ Item i = item.clone();
+ i.setCount(1);
+ this.level.dropItem(dispensePos, i, motion);
+ }
+
+ @Override
+ public boolean hasComparatorInputOverride() {
+ return true;
+ }
+
+ @Override
+ public int getComparatorInputOverride() {
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (blockEntity instanceof BlockEntityDropper) {
+ return ContainerInventory.calculateRedstone(((BlockEntityDropper) blockEntity).getInventory());
+ }
+
+ return 0;
+ }
+
+ public boolean isTriggered() {
+ return (this.getDamage() & 8) > 0;
+ }
+
+ public void setTriggered(boolean value) {
+ int i = 0;
+ i |= getBlockFace().getIndex();
+
+ if (value) {
+ i |= 8;
+ }
+
+ this.setDamage(i);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.setTriggered(false);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
+ dispense();
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_REDSTONE) {
+ if (!isTriggered() && (level.isBlockPowered(this) || level.isBlockPowered(this.getSideVec(BlockFace.UP)))) {
+ this.setTriggered(true);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
+ level.scheduleUpdate(this, this, 4);
+ }
+
+ return type;
+ }
+
+ return 0;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockEmerald.java b/src/main/java/cn/nukkit/block/BlockEmerald.java
index 92e949c544a..e31af8631e1 100644
--- a/src/main/java/cn/nukkit/block/BlockEmerald.java
+++ b/src/main/java/cn/nukkit/block/BlockEmerald.java
@@ -10,12 +10,9 @@
*/
public class BlockEmerald extends BlockSolid {
- public BlockEmerald() {
- }
-
@Override
public String getName() {
- return "Emerald Block";
+ return "Block of Emerald";
}
@Override
@@ -40,7 +37,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockEnchantingTable.java b/src/main/java/cn/nukkit/block/BlockEnchantingTable.java
index e8c61f58fd3..4de0edde00e 100644
--- a/src/main/java/cn/nukkit/block/BlockEnchantingTable.java
+++ b/src/main/java/cn/nukkit/block/BlockEnchantingTable.java
@@ -8,7 +8,6 @@
import cn.nukkit.item.ItemTool;
import cn.nukkit.math.BlockFace;
import cn.nukkit.nbt.tag.CompoundTag;
-import cn.nukkit.nbt.tag.ListTag;
import cn.nukkit.nbt.tag.StringTag;
import cn.nukkit.nbt.tag.Tag;
import cn.nukkit.utils.BlockColor;
@@ -20,8 +19,6 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockEnchantingTable extends BlockTransparent {
- public BlockEnchantingTable() {
- }
@Override
public int getId() {
@@ -60,7 +57,7 @@ public boolean canBeActivated() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -71,7 +68,7 @@ public Item[] getDrops(Item item) {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
CompoundTag nbt = new CompoundTag()
.putString("id", BlockEntity.ENCHANT_TABLE)
@@ -90,37 +87,27 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- BlockEntityEnchantTable enchantTable = (BlockEntityEnchantTable) BlockEntity.createBlockEntity(BlockEntity.ENCHANT_TABLE, getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- return enchantTable != null;
+ BlockEntity.createBlockEntity(BlockEntity.ENCHANT_TABLE, this.getChunk(), nbt);
+
+ return true;
}
@Override
public boolean onActivate(Item item, Player player) {
if (player != null) {
BlockEntity t = this.getLevel().getBlockEntity(this);
- BlockEntityEnchantTable enchantTable;
- if (t instanceof BlockEntityEnchantTable) {
- enchantTable = (BlockEntityEnchantTable) t;
- } else {
- CompoundTag nbt = new CompoundTag()
- .putList(new ListTag<>("Items"))
- .putString("id", BlockEntity.ENCHANT_TABLE)
- .putInt("x", (int) this.x)
- .putInt("y", (int) this.y)
- .putInt("z", (int) this.z);
- enchantTable = (BlockEntityEnchantTable) BlockEntity.createBlockEntity(BlockEntity.ENCHANT_TABLE, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
- if (enchantTable == null) {
- return false;
- }
+ if (!(t instanceof BlockEntityEnchantTable)) {
+ return false;
}
+ BlockEntityEnchantTable enchantTable = (BlockEntityEnchantTable) t;
if (enchantTable.namedTag.contains("Lock") && enchantTable.namedTag.get("Lock") instanceof StringTag) {
if (!enchantTable.namedTag.getString("Lock").equals(item.getCustomName())) {
return true;
}
}
- player.addWindow(new EnchantInventory(player.getUIInventory(), this.getLocation()), Player.ENCHANT_WINDOW_ID);
+ player.addWindow(new EnchantInventory(player.getUIInventory(), this), Player.ENCHANT_WINDOW_ID);
}
return true;
@@ -135,4 +122,9 @@ public boolean canHarvestWithHand() {
public BlockColor getColor() {
return BlockColor.RED_BLOCK_COLOR;
}
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockEndGateway.java b/src/main/java/cn/nukkit/block/BlockEndGateway.java
index 826d95eaa9d..dfe2f026a64 100644
--- a/src/main/java/cn/nukkit/block/BlockEndGateway.java
+++ b/src/main/java/cn/nukkit/block/BlockEndGateway.java
@@ -9,9 +9,6 @@
*/
public class BlockEndGateway extends BlockSolid {
- public BlockEndGateway() {
- }
-
@Override
public String getName() {
return "End Gateway";
@@ -62,4 +59,13 @@ public Item toItem() {
return new ItemBlock(Block.get(BlockID.AIR));
}
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockEndPortal.java b/src/main/java/cn/nukkit/block/BlockEndPortal.java
index 8d2cf1ac1ee..e2ae2683e1e 100644
--- a/src/main/java/cn/nukkit/block/BlockEndPortal.java
+++ b/src/main/java/cn/nukkit/block/BlockEndPortal.java
@@ -2,6 +2,7 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
+import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.utils.BlockColor;
public class BlockEndPortal extends BlockFlowable {
@@ -24,11 +25,6 @@ public int getId() {
return END_PORTAL;
}
- @Override
- public boolean canPassThrough() {
- return true;
- }
-
@Override
public boolean isBreakable(Item item) {
return false;
@@ -74,6 +70,11 @@ public boolean canBeFlowedInto() {
return false;
}
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return this;
+ }
+
@Override
public Item toItem() {
return new ItemBlock(Block.get(BlockID.AIR));
diff --git a/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java b/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java
index a8e45f5c75f..fc1a33b31d2 100644
--- a/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java
+++ b/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java
@@ -4,20 +4,16 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.Vector3;
import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.Faceable;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Created by Pub4Game on 26.12.2015.
*/
public class BlockEndPortalFrame extends BlockTransparentMeta implements Faceable {
- private static final int[] FACES = {2, 3, 0, 1};
+ private static final int[] faces = {2, 3, 0, 1};
public BlockEndPortalFrame() {
this(0);
@@ -82,91 +78,27 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
- if((this.getDamage() & 0x04) == 0 && player != null && item.getId() == Item.ENDER_EYE) {
+ if ((this.getDamage() & 0x04) == 0 && player != null && item.getId() == Item.ENDER_EYE && !player.isSneaking()) {
this.setDamage(this.getDamage() + 4);
- this.getLevel().setBlock(this, this, true, true);
+ this.getLevel().setBlock(this, this, true, false);
this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_END_PORTAL_FRAME_FILL);
- this.createPortal();
- return true;
- }
- return false;
- }
-
- public void createPortal() {
- Vector3 centerSpot = this.searchCenter(new ArrayList<>());
- if(centerSpot != null) {
- for(int x = -2; x <= 2; x++) {
- for(int z = -2; z <= 2; z++) {
- if((x == -2 || x == 2) && (z == -2 || z == 2))
- continue;
- if(x == -2 || x == 2 || z == -2 || z == 2) {
- if(!this.checkFrame(this.getLevel().getBlock(centerSpot.add(x, 0, z)), x, z)) {
- return;
+ for (int i = 0; i < 4; i++) {
+ for (int j = -1; j <= 1; j++) {
+ Block t = this.getSide(BlockFace.fromHorizontalIndex(i), 2).getSide(BlockFace.fromHorizontalIndex((i + 1) % 4), j);
+ if (isCompletedPortal(t)) {
+ for (int k = -1; k <= 1; k++) {
+ for (int l = -1; l <= 1; l++) {
+ this.getLevel().setBlock(t.add(k, 0, l), Block.get(Block.END_PORTAL), true);
+ }
}
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_END_PORTAL_SPAWN);
+ return true;
}
}
}
-
- for(int x = -1; x <= 1; x++) {
- for(int z = -1; z <= 1; z++) {
- Vector3 vector3 = centerSpot.add(x, 0, z);
- if(this.getLevel().getBlock(vector3).getId() != Block.AIR) {
- this.getLevel().useBreakOn(vector3);
- }
- this.getLevel().setBlock(vector3, Block.get(Block.END_PORTAL));
- }
- }
- }
- }
-
- private Vector3 searchCenter(List visited) {
- for(int x = -2; x <= 2; x++) {
- if(x == 0)
- continue;
- Block block = this.getLevel().getBlock(this.add(x, 0, 0));
- Block iBlock = this.getLevel().getBlock(this.add(x * 2, 0, 0));
- if(this.checkFrame(block) && !visited.contains(block)) {
- visited.add(block);
- if((x == -1 || x == 1) && this.checkFrame(iBlock))
- return ((BlockEndPortalFrame) block).searchCenter(visited);
- for(int z = -4; z <= 4; z++) {
- if(z == 0)
- continue;
- block = this.getLevel().getBlock(this.add(x, 0, z));
- if(this.checkFrame(block)) {
- return this.add(x / 2, 0, z / 2);
- }
- }
- }
- }
- for(int z = -2; z <= 2; z++) {
- if(z == 0)
- continue;
- Block block = this.getLevel().getBlock(this.add(0, 0, z));
- Block iBlock = this.getLevel().getBlock(this.add(0, 0, z * 2));
- if(this.checkFrame(block) && !visited.contains(block)) {
- visited.add(block);
- if((z == -1 || z == 1) && this.checkFrame(iBlock))
- return ((BlockEndPortalFrame) block).searchCenter(visited);
- for(int x = -4; x <= 4; x++) {
- if(x == 0)
- continue;
- block = this.getLevel().getBlock(this.add(x, 0, z));
- if(this.checkFrame(block)) {
- return this.add(x / 2, 0, z / 2);
- }
- }
- }
+ return true;
}
- return null;
- }
-
- private boolean checkFrame(Block block) {
- return block.getId() == this.getId() && (block.getDamage() & 4) == 4;
- }
-
- private boolean checkFrame(Block block, int x, int z) {
- return block.getId() == this.getId() && (block.getDamage() - 4) == (x == -2 ? 3 : x == 2 ? 1 : z == -2 ? 0 : z == 2 ? 2 : -1);
+ return false;
}
@Override
@@ -176,18 +108,31 @@ public boolean canHarvestWithHand() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- this.setDamage(FACES[player != null ? player.getDirection().getHorizontalIndex() : 0]);
- this.getLevel().setBlock(block, this, true);
+ this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+
+ private static boolean isCompletedPortal(Block center) {
+ for (int i = 0; i < 4; i++) {
+ for (int j = -1; j <= 1; j++) {
+ Block block = center.getSide(BlockFace.fromHorizontalIndex(i), 2).getSide(BlockFace.fromHorizontalIndex((i + 1) % 4), j);
+ if (block.getId() != Block.END_PORTAL_FRAME || (block.getDamage() & 0x4) == 0) {
+ return false;
+ }
+ }
+ }
return true;
}
@@ -195,4 +140,9 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
public BlockColor getColor() {
return BlockColor.GREEN_BLOCK_COLOR;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockEndRod.java b/src/main/java/cn/nukkit/block/BlockEndRod.java
index 2c4da5a0106..b4794dfa369 100644
--- a/src/main/java/cn/nukkit/block/BlockEndRod.java
+++ b/src/main/java/cn/nukkit/block/BlockEndRod.java
@@ -14,6 +14,8 @@
*/
public class BlockEndRod extends BlockTransparentMeta implements Faceable {
+ private static final int[] faces = {0, 1, 3, 2, 5, 4};
+
public BlockEndRod() {
this(0);
}
@@ -47,11 +49,6 @@ public int getLightLevel() {
return 14;
}
- @Override
- public boolean canBePushed() {
- return true;
- }
-
@Override
public int getToolType() {
return ItemTool.TYPE_PICKAXE;
@@ -79,7 +76,6 @@ public double getMaxZ() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- int[] faces = {0, 1, 3, 2, 5, 4};
this.setDamage(faces[player != null ? face.getIndex() : 0]);
this.getLevel().setBlock(block, this, true, true);
@@ -88,12 +84,21 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockEndStone.java b/src/main/java/cn/nukkit/block/BlockEndStone.java
index 1695fa12b8a..1956362e918 100644
--- a/src/main/java/cn/nukkit/block/BlockEndStone.java
+++ b/src/main/java/cn/nukkit/block/BlockEndStone.java
@@ -10,9 +10,6 @@
*/
public class BlockEndStone extends BlockSolid {
- public BlockEndStone() {
- }
-
@Override
public String getName() {
return "End Stone";
@@ -40,7 +37,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockEnderChest.java b/src/main/java/cn/nukkit/block/BlockEnderChest.java
index 0cac7913762..158deb864c0 100644
--- a/src/main/java/cn/nukkit/block/BlockEnderChest.java
+++ b/src/main/java/cn/nukkit/block/BlockEnderChest.java
@@ -6,6 +6,7 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.math.BlockFace;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.StringTag;
@@ -19,7 +20,7 @@
public class BlockEnderChest extends BlockTransparentMeta implements Faceable {
- private Set viewers = new HashSet<>();
+ private final Set viewers = new HashSet<>();
public BlockEnderChest() {
this(0);
@@ -91,10 +92,10 @@ public double getMaxZ() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- int[] faces = {2, 5, 3, 4};
- this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+
+ this.getLevel().setBlock(this, this, true, true);
- this.getLevel().setBlock(block, this, true, true);
CompoundTag nbt = new CompoundTag("")
.putString("id", BlockEntity.ENDER_CHEST)
.putInt("x", (int) this.x)
@@ -112,34 +113,24 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- BlockEntityEnderChest ender = (BlockEntityEnderChest) BlockEntity.createBlockEntity(BlockEntity.ENDER_CHEST, this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- return ender != null;
+ BlockEntity.createBlockEntity(BlockEntity.ENDER_CHEST, this.getChunk(), nbt);
+ return true;
}
@Override
public boolean onActivate(Item item, Player player) {
if (player != null) {
Block top = this.up();
- if (!top.isTransparent()) {
+ if (!top.isTransparent() && !(top instanceof BlockSlab && (top.getDamage() & 0x07) <= 0)) { // avoid issues with the slab hack
return true;
}
BlockEntity t = this.getLevel().getBlockEntity(this);
- BlockEntityEnderChest chest;
- if (t instanceof BlockEntityEnderChest) {
- chest = (BlockEntityEnderChest) t;
- } else {
- CompoundTag nbt = new CompoundTag("")
- .putString("id", BlockEntity.ENDER_CHEST)
- .putInt("x", (int) this.x)
- .putInt("y", (int) this.y)
- .putInt("z", (int) this.z);
- chest = (BlockEntityEnderChest) BlockEntity.createBlockEntity(BlockEntity.ENDER_CHEST, this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- if (chest == null) {
- return false;
- }
+ if (!(t instanceof BlockEntityEnderChest)) {
+ return false;
}
+ BlockEntityEnderChest chest = (BlockEntityEnderChest) t;
if (chest.namedTag.contains("Lock") && chest.namedTag.get("Lock") instanceof StringTag) {
if (!chest.namedTag.getString("Lock").equals(item.getCustomName())) {
return true;
@@ -155,7 +146,10 @@ public boolean onActivate(Item item, Player player) {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
return new Item[]{
Item.get(Item.OBSIDIAN, 0, 8)
};
@@ -190,11 +184,16 @@ public boolean canSilkTouch() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockFallable.java b/src/main/java/cn/nukkit/block/BlockFallable.java
index 04740226643..571249cc1d2 100644
--- a/src/main/java/cn/nukkit/block/BlockFallable.java
+++ b/src/main/java/cn/nukkit/block/BlockFallable.java
@@ -1,6 +1,5 @@
package cn.nukkit.block;
-import cn.nukkit.entity.Entity;
import cn.nukkit.entity.item.EntityFallingBlock;
import cn.nukkit.event.block.BlockFallEvent;
import cn.nukkit.level.Level;
@@ -9,9 +8,8 @@
import cn.nukkit.nbt.tag.FloatTag;
import cn.nukkit.nbt.tag.ListTag;
-
/**
- * author: rcsuperman
+ * @author rcsuperman
* Nukkit Project
*/
public abstract class BlockFallable extends BlockSolid {
@@ -28,7 +26,6 @@ public int onUpdate(int type) {
if (event.isCancelled()) {
return type;
}
-
this.level.setBlock(this, Block.get(Block.AIR), true, true);
CompoundTag nbt = new CompoundTag()
.putList(new ListTag("Pos")
@@ -46,11 +43,9 @@ public int onUpdate(int type) {
.putInt("TileID", this.getId())
.putByte("Data", this.getDamage());
- EntityFallingBlock fall = (EntityFallingBlock) Entity.createEntity("FallingSand", this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
+ EntityFallingBlock fall = new EntityFallingBlock(this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- if (fall != null) {
- fall.spawnToAll();
- }
+ fall.spawnToAll();
}
}
return type;
diff --git a/src/main/java/cn/nukkit/block/BlockFallableMeta.java b/src/main/java/cn/nukkit/block/BlockFallableMeta.java
new file mode 100644
index 00000000000..de05e801648
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFallableMeta.java
@@ -0,0 +1,54 @@
+package cn.nukkit.block;
+
+import cn.nukkit.entity.item.EntityFallingBlock;
+import cn.nukkit.event.block.BlockFallEvent;
+import cn.nukkit.level.Level;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.DoubleTag;
+import cn.nukkit.nbt.tag.FloatTag;
+import cn.nukkit.nbt.tag.ListTag;
+
+/**
+ * @author rcsuperman
+ * Nukkit Project
+ */
+public abstract class BlockFallableMeta extends BlockSolidMeta {
+
+ protected BlockFallableMeta(int meta) {
+ super(meta);
+ }
+
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block down = this.down();
+ if (down.getId() == AIR || down instanceof BlockLiquid || down instanceof BlockFire) {
+ BlockFallEvent event = new BlockFallEvent(this);
+ this.level.getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return type;
+ }
+ this.level.setBlock(this, Block.get(Block.AIR), true, true);
+ CompoundTag nbt = new CompoundTag()
+ .putList(new ListTag("Pos")
+ .add(new DoubleTag("", this.x + 0.5))
+ .add(new DoubleTag("", this.y))
+ .add(new DoubleTag("", this.z + 0.5)))
+ .putList(new ListTag("Motion")
+ .add(new DoubleTag("", 0))
+ .add(new DoubleTag("", 0))
+ .add(new DoubleTag("", 0)))
+
+ .putList(new ListTag("Rotation")
+ .add(new FloatTag("", 0))
+ .add(new FloatTag("", 0)))
+ .putInt("TileID", this.getId())
+ .putByte("Data", this.getDamage());
+
+ EntityFallingBlock fall = new EntityFallingBlock(this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
+
+ fall.spawnToAll();
+ }
+ }
+ return type;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFarmland.java b/src/main/java/cn/nukkit/block/BlockFarmland.java
index 7301b15da03..ce2c05dab8a 100644
--- a/src/main/java/cn/nukkit/block/BlockFarmland.java
+++ b/src/main/java/cn/nukkit/block/BlockFarmland.java
@@ -4,7 +4,7 @@
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
-import cn.nukkit.math.Vector3;
+import cn.nukkit.level.format.FullChunk;
import cn.nukkit.utils.BlockColor;
/**
@@ -48,21 +48,20 @@ public int getToolType() {
@Override
public double getMaxY() {
- return this.y + 1;
+ return this.y + 0.9375;
}
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_RANDOM) {
- Vector3 v = new Vector3();
+ Block up = this.up();
- if (this.level.getBlock(v.setComponents(x, this.y + 1, z)) instanceof BlockCrops) {
+ if (up instanceof BlockCrops) {
return 0;
}
- if (this.level.getBlock(v.setComponents(x, this.y + 1, z)).isSolid()) {
+ if (up.isSolid()) {
this.level.setBlock(this, Block.get(BlockID.DIRT), false, true);
-
return Level.BLOCK_UPDATE_RANDOM;
}
@@ -78,10 +77,10 @@ public int onUpdate(int type) {
continue;
}
- v.setComponents(x, y, z);
- int block = this.level.getBlockIdAt(v.getFloorX(), v.getFloorY(), v.getFloorZ());
+ FullChunk chunk = this.level.getChunk(x >> 4, z >> 4);
- if (block == WATER || block == STILL_WATER) {
+ int block = this.level.getBlockIdAt(chunk, x, y, z);
+ if (Block.hasWater(block) || block == FROSTED_ICE || this.level.isBlockWaterloggedAt(chunk, x, y, z)) {
found = true;
break;
}
@@ -90,23 +89,28 @@ public int onUpdate(int type) {
}
}
- Block block = this.level.getBlock(v.setComponents(x, y - 1, z));
- if (found || block instanceof BlockWater) {
+ Block block;
+ if (found || (block = this.down()) instanceof BlockWater || block instanceof BlockIceFrosted) {
if (this.getDamage() < 7) {
this.setDamage(7);
- this.level.setBlock(this, this, false, false);
+ this.level.setBlock(this, this, false, true);
}
return Level.BLOCK_UPDATE_RANDOM;
}
if (this.getDamage() > 0) {
this.setDamage(this.getDamage() - 1);
- this.level.setBlock(this, this, false, false);
+ this.level.setBlock(this, this, false, true);
} else {
this.level.setBlock(this, Block.get(Block.DIRT), false, true);
}
return Level.BLOCK_UPDATE_RANDOM;
+ } else if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (this.up().isSolid()) {
+ this.level.setBlock(this, Block.get(DIRT), false, true);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
}
return 0;
diff --git a/src/main/java/cn/nukkit/block/BlockFence.java b/src/main/java/cn/nukkit/block/BlockFence.java
index 3c6356511de..4a0a6e1a5ad 100644
--- a/src/main/java/cn/nukkit/block/BlockFence.java
+++ b/src/main/java/cn/nukkit/block/BlockFence.java
@@ -50,7 +50,7 @@ public int getToolType() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Oak Fence",
"Spruce Fence",
"Birch Fence",
@@ -63,7 +63,6 @@ public String getName() {
return names[this.getDamage() & 0x07];
}
- @Override
protected AxisAlignedBB recalculateBoundingBox() {
boolean north = this.canConnect(this.north());
boolean south = this.canConnect(this.south());
@@ -99,19 +98,19 @@ public boolean canConnect(Block block) {
@Override
public BlockColor getColor() {
- switch(this.getDamage() & 0x07){
+ switch (this.getDamage() & 0x07) {
default:
- case BlockFence.FENCE_OAK: //OAK
+ case FENCE_OAK: //OAK
return BlockColor.WOOD_BLOCK_COLOR;
- case BlockFence.FENCE_SPRUCE: //SPRUCE
+ case FENCE_SPRUCE: //SPRUCE
return BlockColor.SPRUCE_BLOCK_COLOR;
- case BlockFence.FENCE_BIRCH: //BIRCH
+ case FENCE_BIRCH: //BIRCH
return BlockColor.SAND_BLOCK_COLOR;
- case BlockFence.FENCE_JUNGLE: //JUNGLE
+ case FENCE_JUNGLE: //JUNGLE
return BlockColor.DIRT_BLOCK_COLOR;
- case BlockFence.FENCE_ACACIA: //ACACIA
+ case FENCE_ACACIA: //ACACIA
return BlockColor.ORANGE_BLOCK_COLOR;
- case BlockFence.FENCE_DARK_OAK: //DARK OAK
+ case FENCE_DARK_OAK: //DARK OAK
return BlockColor.BROWN_BLOCK_COLOR;
}
}
@@ -120,4 +119,9 @@ public BlockColor getColor() {
public Item toItem() {
return new ItemBlock(this, this.getDamage());
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockFenceCrimson.java b/src/main/java/cn/nukkit/block/BlockFenceCrimson.java
new file mode 100644
index 00000000000..055c65ebfdf
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFenceCrimson.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockFenceCrimson extends BlockFence {
+
+ public BlockFenceCrimson() {
+ this(0);
+ }
+
+ public BlockFenceCrimson(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Fence";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_FENCE;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGate.java b/src/main/java/cn/nukkit/block/BlockFenceGate.java
index 15c18e7b44a..a776602fd42 100644
--- a/src/main/java/cn/nukkit/block/BlockFenceGate.java
+++ b/src/main/java/cn/nukkit/block/BlockFenceGate.java
@@ -5,8 +5,8 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
+import cn.nukkit.level.Sound;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.Faceable;
@@ -104,8 +104,8 @@ public double getMaxZ() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
this.setDamage(player != null ? player.getDirection().getHorizontalIndex() : 0);
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@@ -119,7 +119,12 @@ public boolean onActivate(Item item, Player player) {
return false;
}
- this.getLevel().addLevelEvent(this.add(0.5, 0.5, 0.5), LevelEventPacket.EVENT_SOUND_DOOR);
+ this.getLevel().setBlock(this, this, true);
+ if (this.isOpen()) {
+ this.level.addSound(this, Sound.RANDOM_DOOR_OPEN);
+ } else {
+ this.level.addSound(this, Sound.RANDOM_DOOR_CLOSE);
+ }
return true;
}
@@ -174,7 +179,12 @@ public boolean toggle(Player player) {
}
this.setDamage(direction | ((~this.getDamage()) & 0x04));
- this.level.setBlock(this, this, false, false);
+ this.level.setBlock(this, this, true, false);
+ if (this.isOpen()) {
+ this.level.addSound(this, Sound.RANDOM_DOOR_OPEN);
+ } else {
+ this.level.addSound(this, Sound.RANDOM_DOOR_CLOSE);
+ }
return true;
}
@@ -185,7 +195,8 @@ public boolean isOpen() {
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_REDSTONE) {
- if ((!isOpen() && this.level.isBlockPowered(this.getLocation())) || (isOpen() && !this.level.isBlockPowered(this.getLocation()))) {
+ boolean powered = this.level.isBlockPowered(this);
+ if ((!isOpen() && powered) || (isOpen() && !powered)) {
this.toggle(null);
return type;
}
@@ -193,7 +204,7 @@ public int onUpdate(int type) {
return 0;
}
-
+
@Override
public Item toItem() {
return Item.get(Item.FENCE_GATE, 0, 1);
@@ -201,6 +212,16 @@ public Item toItem() {
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return this.isOpen();
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGateAcacia.java b/src/main/java/cn/nukkit/block/BlockFenceGateAcacia.java
index db80aa78a0f..69663971f3b 100644
--- a/src/main/java/cn/nukkit/block/BlockFenceGateAcacia.java
+++ b/src/main/java/cn/nukkit/block/BlockFenceGateAcacia.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockFenceGateAcacia extends BlockFenceGate {
+
public BlockFenceGateAcacia() {
this(0);
}
@@ -25,7 +26,7 @@ public int getId() {
public String getName() {
return "Acacia Fence Gate";
}
-
+
@Override
public Item toItem() {
return Item.get(Item.FENCE_GATE_ACACIA, 0, 1);
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGateBirch.java b/src/main/java/cn/nukkit/block/BlockFenceGateBirch.java
index 9b0723112bf..97654be29f5 100644
--- a/src/main/java/cn/nukkit/block/BlockFenceGateBirch.java
+++ b/src/main/java/cn/nukkit/block/BlockFenceGateBirch.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockFenceGateBirch extends BlockFenceGate {
+
public BlockFenceGateBirch() {
this(0);
}
@@ -25,7 +26,7 @@ public int getId() {
public String getName() {
return "Birch Fence Gate";
}
-
+
@Override
public Item toItem() {
return Item.get(Item.FENCE_GATE_BIRCH, 0, 1);
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGateCrimson.java b/src/main/java/cn/nukkit/block/BlockFenceGateCrimson.java
new file mode 100644
index 00000000000..e0c76702ebf
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFenceGateCrimson.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockFenceGateCrimson extends BlockFenceGate {
+
+ public BlockFenceGateCrimson() {
+ this(0);
+ }
+
+ public BlockFenceGateCrimson(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Fence Gate";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_FENCE_GATE;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGateDarkOak.java b/src/main/java/cn/nukkit/block/BlockFenceGateDarkOak.java
index 9f03cf39859..04ca651b283 100644
--- a/src/main/java/cn/nukkit/block/BlockFenceGateDarkOak.java
+++ b/src/main/java/cn/nukkit/block/BlockFenceGateDarkOak.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockFenceGateDarkOak extends BlockFenceGate {
+
public BlockFenceGateDarkOak() {
this(0);
}
@@ -25,7 +26,7 @@ public int getId() {
public String getName() {
return "Dark Oak Fence Gate";
}
-
+
@Override
public Item toItem() {
return Item.get(Item.FENCE_GATE_DARK_OAK, 0, 1);
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGateJungle.java b/src/main/java/cn/nukkit/block/BlockFenceGateJungle.java
index fe8c23fa047..eb5e176489d 100644
--- a/src/main/java/cn/nukkit/block/BlockFenceGateJungle.java
+++ b/src/main/java/cn/nukkit/block/BlockFenceGateJungle.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockFenceGateJungle extends BlockFenceGate {
+
public BlockFenceGateJungle() {
this(0);
}
@@ -25,7 +26,7 @@ public int getId() {
public String getName() {
return "Jungle Fence Gate";
}
-
+
@Override
public Item toItem() {
return Item.get(Item.FENCE_GATE_JUNGLE, 0, 1);
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGateSpruce.java b/src/main/java/cn/nukkit/block/BlockFenceGateSpruce.java
index d9809efd834..8b38f74dfd1 100644
--- a/src/main/java/cn/nukkit/block/BlockFenceGateSpruce.java
+++ b/src/main/java/cn/nukkit/block/BlockFenceGateSpruce.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockFenceGateSpruce extends BlockFenceGate {
+
public BlockFenceGateSpruce() {
this(0);
}
@@ -25,7 +26,7 @@ public int getId() {
public String getName() {
return "Spruce Fence Gate";
}
-
+
@Override
public Item toItem() {
return Item.get(Item.FENCE_GATE_SPRUCE,0, 1);
diff --git a/src/main/java/cn/nukkit/block/BlockFenceGateWarped.java b/src/main/java/cn/nukkit/block/BlockFenceGateWarped.java
new file mode 100644
index 00000000000..e916d49396d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFenceGateWarped.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockFenceGateWarped extends BlockFenceGate {
+
+ public BlockFenceGateWarped() {
+ this(0);
+ }
+
+ public BlockFenceGateWarped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Fence Gate";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_FENCE_GATE;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFenceNetherBrick.java b/src/main/java/cn/nukkit/block/BlockFenceNetherBrick.java
index f8ca8847192..baa16ba4c74 100644
--- a/src/main/java/cn/nukkit/block/BlockFenceNetherBrick.java
+++ b/src/main/java/cn/nukkit/block/BlockFenceNetherBrick.java
@@ -33,11 +33,6 @@ public int getId() {
return NETHER_BRICK_FENCE;
}
- @Override
- public double getHardness() {
- return 2;
- }
-
@Override
public double getResistance() {
return 10;
@@ -45,7 +40,7 @@ public double getResistance() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockFenceWarped.java b/src/main/java/cn/nukkit/block/BlockFenceWarped.java
new file mode 100644
index 00000000000..5e9a3841594
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFenceWarped.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockFenceWarped extends BlockFence {
+
+ public BlockFenceWarped() {
+ this(0);
+ }
+
+ public BlockFenceWarped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Fence";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_FENCE;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFire.java b/src/main/java/cn/nukkit/block/BlockFire.java
index 4d1eeb3458f..6d9b7bd3505 100644
--- a/src/main/java/cn/nukkit/block/BlockFire.java
+++ b/src/main/java/cn/nukkit/block/BlockFire.java
@@ -1,7 +1,9 @@
package cn.nukkit.block;
import cn.nukkit.Server;
+import cn.nukkit.entity.BaseEntity;
import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.EntityLiving;
import cn.nukkit.entity.item.EntityPotion;
import cn.nukkit.entity.projectile.EntityArrow;
import cn.nukkit.event.block.BlockBurnEvent;
@@ -16,16 +18,13 @@
import cn.nukkit.level.Level;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.Vector3;
import cn.nukkit.potion.Effect;
import cn.nukkit.potion.Potion;
import cn.nukkit.utils.BlockColor;
-
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockFire extends BlockFlowable {
@@ -81,17 +80,24 @@ public void onEntityCollide(Entity entity) {
}
}
- if (!entity.hasEffect(Effect.FIRE_RESISTANCE)) {
- entity.attack(new EntityDamageByBlockEvent(this, entity, DamageCause.FIRE, 1));
- }
+ if (!entity.fireProof || !entity.isOnFire() || !(entity instanceof BaseEntity)) { // Improve performance
- EntityCombustByBlockEvent ev = new EntityCombustByBlockEvent(this, entity, 8);
- if (entity instanceof EntityArrow) {
- ev.setCancelled();
- }
- Server.getInstance().getPluginManager().callEvent(ev);
- if (!ev.isCancelled() && entity.isAlive() && entity.noDamageTicks == 0) {
- entity.setOnFire(ev.getDuration());
+ if (!(entity instanceof EntityLiving) || (!entity.hasEffect(Effect.FIRE_RESISTANCE) && this.level.getGameRules().getBoolean(GameRule.FIRE_DAMAGE))) {
+ entity.attack(new EntityDamageByBlockEvent(this, entity, DamageCause.FIRE, 1));
+ }
+
+ if (!entity.fireProof || !entity.isOnFire()) {
+ EntityCombustByBlockEvent ev = new EntityCombustByBlockEvent(this, entity, 8);
+ if (entity instanceof EntityArrow) {
+ ev.setCancelled();
+ }
+
+ Server.getInstance().getPluginManager().callEvent(ev);
+
+ if (!ev.isCancelled() && entity.isAlive() && entity.noDamageTicks == 0) {
+ entity.setOnFire(ev.getDuration());
+ }
+ }
}
}
@@ -104,28 +110,15 @@ public Item[] getDrops(Item item) {
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_RANDOM) {
if (!this.isBlockTopFacingSurfaceSolid(this.down()) && !this.canNeighborBurn()) {
- BlockFadeEvent event = new BlockFadeEvent(this, get(AIR));
- level.getServer().getPluginManager().callEvent(event);
- if (!event.isCancelled()) {
- level.setBlock(this, event.getNewState(), true);
- }
+ this.getLevel().setBlock(this, Block.get(BlockID.AIR), true);
+ } else if (this.level.gameRules.getBoolean(GameRule.DO_FIRE_TICK) && !level.isUpdateScheduled(this, this)) {
+ level.scheduleUpdate(this, tickRate());
}
return Level.BLOCK_UPDATE_NORMAL;
} else if (type == Level.BLOCK_UPDATE_SCHEDULED && this.level.gameRules.getBoolean(GameRule.DO_FIRE_TICK)) {
- boolean forever = this.down().getId() == Block.NETHERRACK || this.down().getId() == Block.MAGMA;
-
- ThreadLocalRandom random = ThreadLocalRandom.current();
-
- //TODO: END
-
- if (!this.isBlockTopFacingSurfaceSolid(this.down()) && !this.canNeighborBurn()) {
- BlockFadeEvent event = new BlockFadeEvent(this, get(AIR));
- level.getServer().getPluginManager().callEvent(event);
- if (!event.isCancelled()) {
- level.setBlock(this, event.getNewState(), true);
- }
- }
+ Block down = this.down();
+ boolean forever = this.getId() == SOUL_FIRE || down.getId() == NETHERRACK || down.getId() == MAGMA || (down.getId() == BEDROCK && level.getDimension() == Level.DIMENSION_THE_END);
if (!forever && this.getLevel().isRaining() &&
(this.getLevel().canBlockSeeSky(this) ||
@@ -134,80 +127,75 @@ public int onUpdate(int type) {
this.getLevel().canBlockSeeSky(this.south()) ||
this.getLevel().canBlockSeeSky(this.north()))
) {
- BlockFadeEvent event = new BlockFadeEvent(this, get(AIR));
- level.getServer().getPluginManager().callEvent(event);
- if (!event.isCancelled()) {
- level.setBlock(this, event.getNewState(), true);
+
+ this.getLevel().setBlock(this, Block.get(BlockID.AIR), true);
+ }
+
+ if (!this.isBlockTopFacingSurfaceSolid(down) && !this.canNeighborBurn()) {
+ this.getLevel().setBlock(this, Block.get(BlockID.AIR), true);
+ return 0;
+ }
+
+ int meta = this.getDamage();
+
+ if (meta < 15) {
+ int newMeta = meta + Utils.random.nextInt(3);
+ if (newMeta > 15) newMeta = 15;
+ this.setDamage(newMeta);
+ this.getLevel().setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, true, false); // No need to send this to client
+ }
+
+ this.getLevel().scheduleUpdate(this, this.tickRate() + Utils.random.nextInt(10));
+
+ if (!forever && !this.canNeighborBurn()) {
+ if (!this.isBlockTopFacingSurfaceSolid(this.down()) || meta > 3) {
+ this.getLevel().setBlock(this, Block.get(BlockID.AIR), true);
}
+ } else if (!forever && !(this.down().getBurnAbility() > 0) && meta == 15 && Utils.random.nextInt(4) == 0) {
+ this.getLevel().setBlock(this, Block.get(BlockID.AIR), true);
} else {
- int meta = this.getDamage();
+ int o = 0;
- if (meta < 15) {
- int newMeta = meta + random.nextInt(3);
- this.setDamage(Math.min(newMeta, 15));
- this.getLevel().setBlock(this, this, true);
- }
+ //TODO: decrease the o if the rainfall values are high
- this.getLevel().scheduleUpdate(this, this.tickRate() + random.nextInt(10));
+ this.tryToCatchBlockOnFire(this.east(), 300 + o, meta);
+ this.tryToCatchBlockOnFire(this.west(), 300 + o, meta);
+ this.tryToCatchBlockOnFire(this.down(), 250 + o, meta);
+ this.tryToCatchBlockOnFire(this.up(), 250 + o, meta);
+ this.tryToCatchBlockOnFire(this.south(), 300 + o, meta);
+ this.tryToCatchBlockOnFire(this.north(), 300 + o, meta);
- if (!forever && !this.canNeighborBurn()) {
- if (!this.isBlockTopFacingSurfaceSolid(this.down()) || meta > 3) {
- BlockFadeEvent event = new BlockFadeEvent(this, get(AIR));
- level.getServer().getPluginManager().callEvent(event);
- if (!event.isCancelled()) {
- level.setBlock(this, event.getNewState(), true);
- }
- }
- } else if (!forever && !(this.down().getBurnAbility() > 0) && meta == 15 && random.nextInt(4) == 0) {
- BlockFadeEvent event = new BlockFadeEvent(this, get(AIR));
- level.getServer().getPluginManager().callEvent(event);
- if (!event.isCancelled()) {
- level.setBlock(this, event.getNewState(), true);
- }
- } else {
- int o = 0;
-
- //TODO: decrease the o if the rainfall values are high
-
- this.tryToCatchBlockOnFire(this.east(), 300 + o, meta);
- this.tryToCatchBlockOnFire(this.west(), 300 + o, meta);
- this.tryToCatchBlockOnFire(this.down(), 250 + o, meta);
- this.tryToCatchBlockOnFire(this.up(), 250 + o, meta);
- this.tryToCatchBlockOnFire(this.south(), 300 + o, meta);
- this.tryToCatchBlockOnFire(this.north(), 300 + o, meta);
-
- for (int x = (int) (this.x - 1); x <= (int) (this.x + 1); ++x) {
- for (int z = (int) (this.z - 1); z <= (int) (this.z + 1); ++z) {
- for (int y = (int) (this.y - 1); y <= (int) (this.y + 4); ++y) {
- if (x != (int) this.x || y != (int) this.y || z != (int) this.z) {
- int k = 100;
-
- if (y > this.y + 1) {
- k += (y - (this.y + 1)) * 100;
- }
+ for (int x = (int) (this.x - 1); x <= (int) (this.x + 1); ++x) {
+ for (int z = (int) (this.z - 1); z <= (int) (this.z + 1); ++z) {
+ for (int y = (int) (this.y - 1); y <= (int) (this.y + 4); ++y) {
+ if (x != (int) this.x || y != (int) this.y || z != (int) this.z) {
+ int k = 100;
+
+ if (y > this.y + 1) {
+ k += (y - (this.y + 1)) * 100;
+ }
- Block block = this.getLevel().getBlock(new Vector3(x, y, z));
- int chance = this.getChanceOfNeighborsEncouragingFire(block);
+ Block block = this.getLevel().getBlock(x, y, z);
+ int chance = getChanceOfNeighborsEncouragingFire(block);
- if (chance > 0) {
- int t = (chance + 40 + this.getLevel().getServer().getDifficulty() * 7) / (meta + 30);
+ if (chance > 0) {
+ int t = (chance + 40 + this.getLevel().getServer().getDifficulty() * 7) / (meta + 30);
- //TODO: decrease the t if the rainfall values are high
+ //TODO: decrease the t if the rainfall values are high
- if (t > 0 && random.nextInt(k) <= t) {
- int damage = meta + random.nextInt(5) / 4;
+ if (t > 0 && Utils.random.nextInt(k) <= t) {
+ int damage = meta + (Utils.random.nextInt(5) >> 2);
- if (damage > 15) {
- damage = 15;
- }
+ if (damage > 15) {
+ damage = 15;
+ }
- BlockIgniteEvent e = new BlockIgniteEvent(block, this, null, BlockIgniteEvent.BlockIgniteCause.SPREAD);
- this.level.getServer().getPluginManager().callEvent(e);
+ BlockIgniteEvent e = new BlockIgniteEvent(block, this, null, BlockIgniteEvent.BlockIgniteCause.SPREAD);
+ this.level.getServer().getPluginManager().callEvent(e);
- if (!e.isCancelled()) {
- this.getLevel().setBlock(block, Block.get(BlockID.FIRE, damage), true);
- this.getLevel().scheduleUpdate(block, this.tickRate());
- }
+ if (!e.isCancelled()) {
+ this.getLevel().setBlock(block, Block.get(FIRE, damage), true);
+ this.getLevel().scheduleUpdate(block, this.tickRate());
}
}
}
@@ -222,14 +210,10 @@ public int onUpdate(int type) {
}
private void tryToCatchBlockOnFire(Block block, int bound, int damage) {
- int burnAbility = block.getBurnAbility();
-
- Random random = ThreadLocalRandom.current();
-
- if (random.nextInt(bound) < burnAbility) {
+ if (Utils.random.nextInt(bound) < block.getBurnAbility()) {
- if (random.nextInt(damage + 10) < 5) {
- int meta = damage + random.nextInt(5) / 4;
+ if (Utils.random.nextInt(damage + 10) < 5) {
+ int meta = damage + (Utils.random.nextInt(5) >> 2);
if (meta > 15) {
meta = 15;
@@ -239,7 +223,7 @@ private void tryToCatchBlockOnFire(Block block, int bound, int damage) {
this.level.getServer().getPluginManager().callEvent(e);
if (!e.isCancelled()) {
- this.getLevel().setBlock(block, Block.get(BlockID.FIRE, meta), true);
+ this.getLevel().setBlock(block, Block.get(FIRE, meta), true);
this.getLevel().scheduleUpdate(block, this.tickRate());
}
} else {
@@ -257,7 +241,7 @@ private void tryToCatchBlockOnFire(Block block, int bound, int damage) {
}
}
- private int getChanceOfNeighborsEncouragingFire(Block block) {
+ private static int getChanceOfNeighborsEncouragingFire(Block block) {
if (block.getId() != AIR) {
return 0;
} else {
@@ -284,23 +268,31 @@ public boolean canNeighborBurn() {
public boolean isBlockTopFacingSurfaceSolid(Block block) {
if (block != null) {
- if (block.isSolid()) {
+ if (block instanceof BlockStairs && (block.getDamage() & 4) == 4) {
return true;
- } else {
- if (block instanceof BlockStairs &&
- (block.getDamage() & 4) == 4) {
-
- return true;
- } else if (block instanceof BlockSlab &&
- (block.getDamage() & 8) == 8) {
-
- return true;
- } else if (block instanceof BlockSnowLayer &&
- (block.getDamage() & 7) == 7) {
-
- return true;
- }
- }
+ } else if (block instanceof BlockSlab && (this.getDamage() & 0x08) > 0) {
+ return true;
+ } else if (block instanceof BlockSnowLayer && (block.getDamage() & 7) == 7) {
+ return true;
+ } else if (block instanceof BlockGlass) {
+ return false;
+ } else if (block instanceof BlockHopper || block instanceof BlockBeacon) {
+ return false;
+ } else if (block instanceof BlockShulkerBox || block instanceof BlockChest || block instanceof BlockEnderChest) {
+ return false;
+ } else if (block instanceof BlockAnvil || block instanceof BlockEnchantingTable || block instanceof BlockBrewingStand) {
+ return false;
+ } else if (block instanceof BlockCampfire) {
+ return false;
+ } else if (block instanceof BlockCactus) {
+ return false;
+ } else if (block instanceof BlockDaylightDetector) {
+ return false;
+ } else if (block instanceof BlockIce) {
+ return false;
+ } else if (block instanceof BlockCake) {
+ return false;
+ } else return block.isSolid();
}
return false;
@@ -313,7 +305,7 @@ public int tickRate() {
@Override
public BlockColor getColor() {
- return BlockColor.LAVA_BLOCK_COLOR;
+ return BlockColor.AIR_BLOCK_COLOR;
}
@Override
@@ -325,4 +317,9 @@ protected AxisAlignedBB recalculateCollisionBoundingBox() {
public Item toItem() {
return new ItemBlock(Block.get(BlockID.AIR));
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockFletchingTable.java b/src/main/java/cn/nukkit/block/BlockFletchingTable.java
new file mode 100644
index 00000000000..cf57ce7b062
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFletchingTable.java
@@ -0,0 +1,42 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockFletchingTable extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Fletching Table";
+ }
+
+ @Override
+ public int getId() {
+ return FLETCHING_TABLE;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public double getResistance() {
+ return 12.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2.5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 5;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFlowable.java b/src/main/java/cn/nukkit/block/BlockFlowable.java
index a0f2d7dd5b8..0dbc59e6d8f 100644
--- a/src/main/java/cn/nukkit/block/BlockFlowable.java
+++ b/src/main/java/cn/nukkit/block/BlockFlowable.java
@@ -3,7 +3,7 @@
import cn.nukkit.math.AxisAlignedBB;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockFlowable extends BlockTransparentMeta {
@@ -41,4 +41,9 @@ public boolean isSolid() {
protected AxisAlignedBB recalculateBoundingBox() {
return null;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockFlower.java b/src/main/java/cn/nukkit/block/BlockFlower.java
index 9f5e60536bc..a21d7c8b502 100644
--- a/src/main/java/cn/nukkit/block/BlockFlower.java
+++ b/src/main/java/cn/nukkit/block/BlockFlower.java
@@ -2,19 +2,20 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.level.Level;
import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.Vector3;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/11/23 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockFlower extends BlockFlowable {
+
public static final int TYPE_POPPY = 0;
public static final int TYPE_BLUE_ORCHID = 1;
public static final int TYPE_ALLIUM = 2;
@@ -27,6 +28,25 @@ public class BlockFlower extends BlockFlowable {
public static final int TYPE_CORNFLOWER = 9;
public static final int TYPE_LILY_OF_THE_VALLEY = 10;
+ private static final String[] names = {
+ "Poppy",
+ "Blue Orchid",
+ "Allium",
+ "Azure Bluet",
+ "Red Tulip",
+ "Orange Tulip",
+ "White Tulip",
+ "Pink Tulip",
+ "Oxeye Daisy",
+ "Cornflower",
+ "Lily of the Valley",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown"
+ };
+
public BlockFlower() {
this(0);
}
@@ -42,24 +62,6 @@ public int getId() {
@Override
public String getName() {
- String[] names = new String[]{
- "Poppy",
- "Blue Orchid",
- "Allium",
- "Azure Bluet",
- "Red Tulip",
- "Orange Tulip",
- "White Tulip",
- "Pink Tulip",
- "Oxeye Daisy",
- "Cornflower",
- "Lily of the Valley",
- "Unknown",
- "Unknown",
- "Unknown",
- "Unknown",
- "Unknown"
- };
return names[this.getDamage() & 0x0f];
}
@@ -69,7 +71,6 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
int id = down.getId();
if (id == Block.GRASS || id == Block.DIRT || id == Block.FARMLAND || id == Block.PODZOL || id == MYCELIUM) {
this.getLevel().setBlock(block, this, true);
-
return true;
}
return false;
@@ -100,8 +101,8 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.getId() == Item.DYE && item.getDamage() == 0x0f) { //Bone meal
- if (player != null && (player.gamemode & 0x01) == 0) {
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ if (player != null && !player.isCreative()) {
item.count--;
}
@@ -109,15 +110,15 @@ public boolean onActivate(Item item, Player player) {
for (int i = 0; i < 8; i++) {
Vector3 vec = this.add(
- ThreadLocalRandom.current().nextInt(-3, 4),
- ThreadLocalRandom.current().nextInt(-1, 2),
- ThreadLocalRandom.current().nextInt(-3, 4));
+ Utils.random.nextInt(-3, 4),
+ Utils.random.nextInt(-1, 2),
+ Utils.random.nextInt(-3, 4));
- if (level.getBlock(vec).getId() == AIR && level.getBlock(vec.down()).getId() == GRASS && vec.getY() >= 0 && vec.getY() < 256) {
- if (ThreadLocalRandom.current().nextInt(10) == 0) {
+ if (vec.getY() >= level.getMinBlockY() && vec.getY() <= level.getMaxBlockY() && level.getBlock(vec).getId() == AIR && level.getBlock(vec.down()).getId() == GRASS) {
+ if ((this.getDamage() == POPPY || this.getDamage() == DANDELION) && Utils.random.nextInt(10) == 0) {
this.level.setBlock(vec, this.getUncommonFlower(), true);
} else {
- this.level.setBlock(vec, get(this.getId()), true);
+ this.level.setBlock(vec, get(this.getId(), this.getDamage()), true);
}
}
}
@@ -131,4 +132,9 @@ public boolean onActivate(Item item, Player player) {
protected Block getUncommonFlower() {
return get(DANDELION);
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockFlowerPot.java b/src/main/java/cn/nukkit/block/BlockFlowerPot.java
index 1661b090f60..47949e09625 100644
--- a/src/main/java/cn/nukkit/block/BlockFlowerPot.java
+++ b/src/main/java/cn/nukkit/block/BlockFlowerPot.java
@@ -4,7 +4,8 @@
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityFlowerPot;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemFlowerPot;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
import cn.nukkit.nbt.tag.CompoundTag;
@@ -23,8 +24,8 @@ public BlockFlowerPot(int meta) {
super(meta);
}
- protected static boolean canPlaceIntoFlowerPot(int id) {
- switch (id) {
+ private static boolean canPlaceIntoFlowerPot(Item item) {
+ switch (item.getId()) {
case SAPLING:
case DEAD_BUSH:
case DANDELION:
@@ -33,6 +34,33 @@ protected static boolean canPlaceIntoFlowerPot(int id) {
case BROWN_MUSHROOM:
case CACTUS:
return true;
+ case TALL_GRASS:
+ if (item.getDamage() == 2 || item.getDamage() == 3) {
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ private static boolean canPlaceIntoFlowerPot(Block block) {
+ if (block == null) {
+ return false;
+ }
+ switch (block.getId()) {
+ case SAPLING:
+ case DEAD_BUSH:
+ case DANDELION:
+ case ROSE:
+ case RED_MUSHROOM:
+ case BROWN_MUSHROOM:
+ case CACTUS:
+ case BAMBOO:
+ case CRIMSON_FUNGUS:
+ case WARPED_FUNGUS:
+ case CRIMSON_ROOTS:
+ case WARPED_ROOTS:
+ return true;
default:
return false;
}
@@ -48,19 +76,24 @@ public int getId() {
return FLOWER_POT_BLOCK;
}
- @Override
- public double getHardness() {
- return 0;
+ private boolean isSupportValid(Block block) {
+ return block.isSolid() || block instanceof BlockFence || block instanceof BlockWall || block instanceof BlockHopper;
}
@Override
- public double getResistance() {
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!isSupportValid(down())) {
+ level.useBreakOn(this);
+ return type;
+ }
+ }
return 0;
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (face != BlockFace.UP) return false;
+ if (!isSupportValid(down())) return false;
CompoundTag nbt = new CompoundTag()
.putString("id", BlockEntity.FLOWER_POT)
.putInt("x", (int) this.x)
@@ -73,10 +106,9 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
nbt.put(aTag.getName(), aTag);
}
}
- BlockEntityFlowerPot flowerPot = (BlockEntityFlowerPot) BlockEntity.createBlockEntity(BlockEntity.FLOWER_POT, getLevel().getChunk((int) block.x >> 4, (int) block.z >> 4), nbt);
- if (flowerPot == null) return false;
+ BlockEntity.createBlockEntity(BlockEntity.FLOWER_POT, this.getChunk(), nbt);
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@@ -85,26 +117,27 @@ public boolean canBeActivated() {
return true;
}
- @Override
- public boolean onActivate(Item item) {
- return this.onActivate(item, null);
- }
-
@Override
public boolean onActivate(Item item, Player player) {
BlockEntity blockEntity = getLevel().getBlockEntity(this);
if (!(blockEntity instanceof BlockEntityFlowerPot)) return false;
-
- if (blockEntity.namedTag.getShort("item") != AIR || blockEntity.namedTag.getInt("mData") != AIR) {
- if (!canPlaceIntoFlowerPot(item.getId())) {
+ if (blockEntity.namedTag.getShort("item") != AIR) {
+ if (!canPlaceIntoFlowerPot(item) && !canPlaceIntoFlowerPot(item.getBlockUnsafe())) {
int id = blockEntity.namedTag.getShort("item");
- if (id == AIR) id = blockEntity.namedTag.getInt("mData");
- for (Item drop : player.getInventory().addItem(Item.get(id, blockEntity.namedTag.getInt("data")))) {
- player.dropItem(drop);
+ if (id > 255) {
+ for (Item drop : player.getInventory().addItem(new ItemBlock(Block.get(id)))) {
+ player.dropItem(drop);
+ }
+ } else {
+ for (Item drop : player.getInventory().addItem(Item.get(id, blockEntity.namedTag.getInt("data")))) {
+ player.dropItem(drop);
+ }
}
blockEntity.namedTag.putShort("item", AIR);
blockEntity.namedTag.putInt("data", 0);
+ blockEntity.setDirty();
+
this.setDamage(0);
this.level.setBlock(this, this, true);
((BlockEntityFlowerPot) blockEntity).spawnToAll();
@@ -112,28 +145,25 @@ public boolean onActivate(Item item, Player player) {
}
return false;
}
-
int itemID;
- int itemMeta;
- if (!canPlaceIntoFlowerPot(item.getId())) {
+ if (!canPlaceIntoFlowerPot(item)) {
Block block = item.getBlockUnsafe();
- if (block == null || !canPlaceIntoFlowerPot(block.getId())) {
+ if (!canPlaceIntoFlowerPot(block)) {
return true;
}
itemID = block.getId();
- itemMeta = item.getDamage();
} else {
itemID = item.getId();
- itemMeta = item.getDamage();
}
blockEntity.namedTag.putShort("item", itemID);
- blockEntity.namedTag.putInt("data", itemMeta);
+ blockEntity.namedTag.putInt("data", item.getDamage());
+ blockEntity.setDirty();
this.setDamage(1);
this.getLevel().setBlock(this, this, true);
((BlockEntityFlowerPot) blockEntity).spawnToAll();
- if (player.isSurvival()) {
+ if (!player.isCreative()) {
item.setCount(item.getCount() - 1);
player.getInventory().setItemInHand(item.getCount() > 0 ? item : Item.get(Item.AIR));
}
@@ -153,13 +183,20 @@ public Item[] getDrops(Item item) {
}
if (dropInside) {
- return new Item[]{
- new ItemFlowerPot(),
- Item.get(insideID, insideMeta, 1)
- };
+ if (insideID > 255) {
+ return new Item[]{
+ Item.get(Item.FLOWER_POT),
+ new ItemBlock(Block.get(insideID))
+ };
+ } else {
+ return new Item[]{
+ Item.get(Item.FLOWER_POT),
+ Item.get(insideID, insideMeta, 1)
+ };
+ }
} else {
return new Item[]{
- new ItemFlowerPot()
+ Item.get(Item.FLOWER_POT)
};
}
}
@@ -201,6 +238,21 @@ public boolean canPassThrough() {
@Override
public Item toItem() {
- return new ItemFlowerPot();
+ return Item.get(Item.FLOWER_POT);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockFungus.java b/src/main/java/cn/nukkit/block/BlockFungus.java
new file mode 100644
index 00000000000..deaa054567a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFungus.java
@@ -0,0 +1,89 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.BlockFace;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public abstract class BlockFungus extends BlockFlowable {
+
+ protected BlockFungus() {
+ super(0);
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (!isValidSupport(this.down())) {
+ return false;
+ }
+ return super.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL && !isValidSupport(down())) {
+ this.level.useBreakOn(this);
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.isNull()) {
+ return false;
+ }
+
+ if (!(item.getId() == ItemID.DYE && item.getDamage() == ItemDye.BONE_MEAL)) {
+ return false;
+ }
+
+ this.level.addParticle(new BoneMealParticle(this));
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+
+ Block down = this.down();
+ if (!this.isValidSupport(down)) {
+ this.level.useBreakOn(this);
+ return true;
+ }
+
+ if (!this.canGrowOn(down) || ThreadLocalRandom.current().nextFloat() >= 0.4) {
+ return true;
+ }
+
+ this.grow(player);
+ return true;
+ }
+
+ protected abstract boolean canGrowOn(Block support);
+
+ protected boolean isValidSupport(Block support) {
+ switch (support.getId()) {
+ case GRASS:
+ case DIRT:
+ case PODZOL:
+ case FARMLAND:
+ case CRIMSON_NYLIUM:
+ case WARPED_NYLIUM:
+ case SOUL_SOIL:
+ case MYCELIUM:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ public abstract boolean grow(Player cause);
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFurnace.java b/src/main/java/cn/nukkit/block/BlockFurnace.java
index 37851c63e3c..ac7ba3988c8 100644
--- a/src/main/java/cn/nukkit/block/BlockFurnace.java
+++ b/src/main/java/cn/nukkit/block/BlockFurnace.java
@@ -1,10 +1,13 @@
package cn.nukkit.block;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Faceable;
+
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
-public class BlockFurnace extends BlockFurnaceBurning {
+public class BlockFurnace extends BlockFurnaceBurning implements Faceable {
public BlockFurnace() {
this(0);
@@ -30,7 +33,7 @@ public int getLightLevel() {
}
@Override
- public boolean canHarvestWithHand() {
- return false;
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockFurnaceBurning.java b/src/main/java/cn/nukkit/block/BlockFurnaceBurning.java
index 2ef0ca35b9a..048dfbfd712 100644
--- a/src/main/java/cn/nukkit/block/BlockFurnaceBurning.java
+++ b/src/main/java/cn/nukkit/block/BlockFurnaceBurning.java
@@ -12,15 +12,14 @@
import cn.nukkit.nbt.tag.ListTag;
import cn.nukkit.nbt.tag.StringTag;
import cn.nukkit.nbt.tag.Tag;
-import cn.nukkit.utils.Faceable;
import java.util.Map;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
-public class BlockFurnaceBurning extends BlockSolidMeta implements Faceable {
+public class BlockFurnaceBurning extends BlockSolidMeta {
public BlockFurnaceBurning() {
this(0);
@@ -67,8 +66,7 @@ public int getLightLevel() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- int[] faces = {2, 5, 3, 4};
- this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
this.getLevel().setBlock(block, this, true, true);
CompoundTag nbt = new CompoundTag()
.putList(new ListTag<>("Items"))
@@ -88,8 +86,8 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- BlockEntityFurnace furnace = (BlockEntityFurnace) BlockEntity.createBlockEntity(BlockEntity.FURNACE, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
- return furnace != null;
+ BlockEntity.createBlockEntity(BlockEntity.FURNACE, this.getChunk(), nbt);
+ return true;
}
@Override
@@ -102,22 +100,11 @@ public boolean onBreak(Item item) {
public boolean onActivate(Item item, Player player) {
if (player != null) {
BlockEntity t = this.getLevel().getBlockEntity(this);
- BlockEntityFurnace furnace;
- if (t instanceof BlockEntityFurnace) {
- furnace = (BlockEntityFurnace) t;
- } else {
- CompoundTag nbt = new CompoundTag()
- .putList(new ListTag<>("Items"))
- .putString("id", BlockEntity.FURNACE)
- .putInt("x", (int) this.x)
- .putInt("y", (int) this.y)
- .putInt("z", (int) this.z);
- furnace = (BlockEntityFurnace) BlockEntity.createBlockEntity(BlockEntity.FURNACE, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
- if (furnace == null) {
- return false;
- }
+ if (!(t instanceof BlockEntityFurnace)) {
+ return false;
}
+ BlockEntityFurnace furnace = (BlockEntityFurnace) t;
if (furnace.namedTag.contains("Lock") && furnace.namedTag.get("Lock") instanceof StringTag) {
if (!furnace.namedTag.getString("Lock").equals(item.getCustomName())) {
return true;
@@ -132,12 +119,12 @@ public boolean onActivate(Item item, Player player) {
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.FURNACE));
+ return new ItemBlock(Block.get(FURNACE));
}
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
this.toItem()
};
@@ -167,7 +154,7 @@ public boolean canHarvestWithHand() {
}
@Override
- public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockGlass.java b/src/main/java/cn/nukkit/block/BlockGlass.java
index 219e4df9dcd..c754c9c8168 100644
--- a/src/main/java/cn/nukkit/block/BlockGlass.java
+++ b/src/main/java/cn/nukkit/block/BlockGlass.java
@@ -4,14 +4,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockGlass extends BlockTransparent {
- public BlockGlass() {
- }
-
@Override
public int getId() {
return GLASS;
diff --git a/src/main/java/cn/nukkit/block/BlockGlassPane.java b/src/main/java/cn/nukkit/block/BlockGlassPane.java
index 3f04d2bee2b..e418f961fa4 100644
--- a/src/main/java/cn/nukkit/block/BlockGlassPane.java
+++ b/src/main/java/cn/nukkit/block/BlockGlassPane.java
@@ -9,9 +9,6 @@
*/
public class BlockGlassPane extends BlockThin {
- public BlockGlassPane() {
- }
-
@Override
public String getName() {
return "Glass Pane";
@@ -46,4 +43,9 @@ public BlockColor getColor() {
public boolean canSilkTouch() {
return true;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockGlassPaneStained.java b/src/main/java/cn/nukkit/block/BlockGlassPaneStained.java
index 2c37ea9367b..85247cb6b85 100644
--- a/src/main/java/cn/nukkit/block/BlockGlassPaneStained.java
+++ b/src/main/java/cn/nukkit/block/BlockGlassPaneStained.java
@@ -20,7 +20,7 @@ public BlockGlassPaneStained(int meta) {
@Override
public int getFullId() {
- return (getId() << 4) + getDamage();
+ return (STAINED_GLASS_PANE << Block.DATA_BITS) + meta;
}
@Override
@@ -30,7 +30,7 @@ public int getId() {
@Override
public String getName() {
- return getDyeColor().getName() + " stained glass pane";
+ return getDyeColor().getName() + " Stained Glass Pane";
}
@Override
@@ -39,7 +39,7 @@ public BlockColor getColor() {
}
public DyeColor getDyeColor() {
- return DyeColor.getByWoolData(getDamage());
+ return DyeColor.getByWoolData(meta);
}
@Override
@@ -51,9 +51,4 @@ public final int getDamage() {
public final void setDamage(int meta) {
this.meta = meta;
}
-
- @Override
- public boolean canSilkTouch() {
- return true;
- }
}
diff --git a/src/main/java/cn/nukkit/block/BlockGlassStained.java b/src/main/java/cn/nukkit/block/BlockGlassStained.java
index d27e300b820..7f746b4365a 100644
--- a/src/main/java/cn/nukkit/block/BlockGlassStained.java
+++ b/src/main/java/cn/nukkit/block/BlockGlassStained.java
@@ -20,7 +20,7 @@ public BlockGlassStained(int meta) {
@Override
public int getFullId() {
- return (getId() << 4) + getDamage();
+ return (this.getId() << Block.DATA_BITS) + this.getDamage();
}
@Override
@@ -35,11 +35,11 @@ public String getName() {
@Override
public BlockColor getColor() {
- return DyeColor.getByWoolData(getDamage()).getColor();
+ return DyeColor.getByWoolData(meta).getColor();
}
public DyeColor getDyeColor() {
- return DyeColor.getByWoolData(getDamage());
+ return DyeColor.getByWoolData(meta);
}
@Override
@@ -51,9 +51,4 @@ public final int getDamage() {
public final void setDamage(int meta) {
this.meta = meta;
}
-
- @Override
- public boolean canSilkTouch() {
- return true;
- }
}
diff --git a/src/main/java/cn/nukkit/block/BlockGlassTinted.java b/src/main/java/cn/nukkit/block/BlockGlassTinted.java
new file mode 100644
index 00000000000..b4bf8595d0d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockGlassTinted.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockGlassTinted extends BlockGlass {
+
+ public BlockGlassTinted() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Tinted Glass";
+ }
+
+ @Override
+ public int getId() {
+ return TINTED_GLASS;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[] { this.toItem() };
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockGlowLichen.java b/src/main/java/cn/nukkit/block/BlockGlowLichen.java
new file mode 100644
index 00000000000..4be29eefbed
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockGlowLichen.java
@@ -0,0 +1,204 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.BlockPropertiesHelper;
+import cn.nukkit.customblock.properties.BlockProperties;
+import cn.nukkit.customblock.properties.BooleanBlockProperty;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+public class BlockGlowLichen extends BlockTransparentMeta implements BlockPropertiesHelper {
+
+ // Currently multi_face_direction_bits: 0x01 - down, 0x02 - up, 0x04 - north, 0x08 - south, 0x10 - west, 0x20 - east
+ private static final BooleanBlockProperty CONNECTION_DOWN = new BooleanBlockProperty("connection_down", false);
+ private static final BooleanBlockProperty CONNECTION_UP = new BooleanBlockProperty("connection_up", false);
+ private static final BooleanBlockProperty CONNECTION_NORTH = new BooleanBlockProperty("connection_north", false);
+ private static final BooleanBlockProperty CONNECTION_SOUTH = new BooleanBlockProperty("connection_south", false);
+ private static final BooleanBlockProperty CONNECTION_WEST = new BooleanBlockProperty("connection_west", false);
+ private static final BooleanBlockProperty CONNECTION_EAST = new BooleanBlockProperty("connection_east", false);
+
+ private static final BlockProperties PROPERTIES = new BlockProperties(CONNECTION_DOWN, CONNECTION_UP, CONNECTION_NORTH, CONNECTION_SOUTH, CONNECTION_WEST, CONNECTION_EAST);
+
+ public BlockGlowLichen() {
+ this(0);
+ }
+
+ public BlockGlowLichen(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return GLOW_LICHEN;
+ }
+
+ @Override
+ public String getName() {
+ return "Glow Lichen";
+ }
+
+ @Override
+ public BlockProperties getBlockProperties() {
+ return PROPERTIES;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (!this.canPlaceOn(block.down(), target) || !target.isSolid()) {
+ return false;
+ }
+
+ if (block.getId() == GLOW_LICHEN) {
+ this.setDamage(block.getDamage());
+ } else {
+ this.setDamage(0);
+ }
+
+ this.setBlockFace(face.getOpposite(), true);
+ this.getLevel().setBlock(this, this, false, true);
+ return true;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isShears()) {
+ return new Item[] { this.toItem() };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.getLevel().useBreakOn(this, null, null, true);
+ } else if (type != Level.BLOCK_UPDATE_NORMAL) {
+ return type;
+ }
+
+ boolean update = false;
+ boolean support = false;
+
+ Set faces = this.getSupportedFaces();
+ for (BlockFace face : faces) {
+ Block block = this.getLevel().getBlock(this.getSide(face));
+ if (block.isSolid()) {
+ support = true;
+ } else {
+ update = true;
+ this.setBlockFace(face, false);
+ }
+ }
+
+ if (!support) {
+ this.getLevel().scheduleUpdate(this, 1);
+ } else if (update) {
+ this.getLevel().setBlock(this, this, false, true);
+ }
+ return type;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.2;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 7;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+
+ @Override
+ public boolean canBeReplaced() {
+ return true;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return null;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GRAY_BLOCK_COLOR;
+ }
+
+ public void setBlockFace(BlockFace face, boolean value) {
+ switch (face) {
+ case UP:
+ this.setBooleanValue(CONNECTION_UP, value);
+ break;
+ case DOWN:
+ this.setBooleanValue(CONNECTION_DOWN, value);
+ break;
+ case NORTH:
+ this.setBooleanValue(CONNECTION_NORTH, value);
+ break;
+ case SOUTH:
+ this.setBooleanValue(CONNECTION_SOUTH, value);
+ break;
+ case WEST:
+ this.setBooleanValue(CONNECTION_WEST, value);
+ break;
+ case EAST:
+ this.setBooleanValue(CONNECTION_EAST, value);
+ break;
+
+ }
+ }
+
+ public boolean hasBlockFace(BlockFace face) {
+ switch (face) {
+ case UP:
+ return this.getBooleanValue(CONNECTION_UP);
+ case DOWN:
+ return this.getBooleanValue(CONNECTION_DOWN);
+ case NORTH:
+ return this.getBooleanValue(CONNECTION_NORTH);
+ case SOUTH:
+ return this.getBooleanValue(CONNECTION_SOUTH);
+ case WEST:
+ return this.getBooleanValue(CONNECTION_WEST);
+ case EAST:
+ return this.getBooleanValue(CONNECTION_EAST);
+
+ }
+ return false;
+ }
+
+ public Set getSupportedFaces() {
+ EnumSet faces = EnumSet.noneOf(BlockFace.class);
+ for (BlockFace face : BlockFace.values()) {
+ if (this.hasBlockFace(face)) {
+ faces.add(face);
+ }
+ }
+ return faces;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockGlowstone.java b/src/main/java/cn/nukkit/block/BlockGlowstone.java
index 77e95fdd708..99eebf56fed 100644
--- a/src/main/java/cn/nukkit/block/BlockGlowstone.java
+++ b/src/main/java/cn/nukkit/block/BlockGlowstone.java
@@ -1,20 +1,16 @@
package cn.nukkit.block;
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;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/12/6 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockGlowstone extends BlockTransparent {
- public BlockGlowstone() {
- }
@Override
public String getName() {
@@ -43,16 +39,19 @@ public int getLightLevel() {
@Override
public Item[] getDrops(Item item) {
- Random random = new Random();
- int count = 2 + random.nextInt(3);
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
+ int count = 2 + Utils.random.nextInt(3);
Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING);
if (fortune != null && fortune.getLevel() >= 1) {
- count += random.nextInt(fortune.getLevel() + 1);
+ count += Utils.random.nextInt(fortune.getLevel() + 1);
}
return new Item[]{
- new ItemGlowstoneDust(0, MathHelper.clamp(count, 1, 4))
+ Item.get(Item.GLOWSTONE_DUST, 0, MathHelper.clamp(count, 1, 4))
};
}
diff --git a/src/main/java/cn/nukkit/block/BlockGold.java b/src/main/java/cn/nukkit/block/BlockGold.java
index 35b92555d88..7124c8445f2 100644
--- a/src/main/java/cn/nukkit/block/BlockGold.java
+++ b/src/main/java/cn/nukkit/block/BlockGold.java
@@ -5,15 +5,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockGold extends BlockSolid {
-
- public BlockGold() {
- }
-
@Override
public int getId() {
return GOLD_BLOCK;
@@ -21,7 +17,7 @@ public int getId() {
@Override
public String getName() {
- return "Gold Block";
+ return "Block of Gold";
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockGrass.java b/src/main/java/cn/nukkit/block/BlockGrass.java
index 3da4fe6faa7..be7e83fb3e0 100644
--- a/src/main/java/cn/nukkit/block/BlockGrass.java
+++ b/src/main/java/cn/nukkit/block/BlockGrass.java
@@ -4,14 +4,18 @@
import cn.nukkit.Server;
import cn.nukkit.event.block.BlockSpreadEvent;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.level.Level;
+import cn.nukkit.level.Sound;
import cn.nukkit.level.generator.object.ObjectTallGrass;
import cn.nukkit.level.particle.BoneMealParticle;
-import cn.nukkit.math.NukkitRandom;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockGrass extends BlockDirt {
@@ -21,7 +25,6 @@ public BlockGrass() {
}
public BlockGrass(int meta) {
- // Grass can't have meta.
super(0);
}
@@ -47,8 +50,8 @@ public String getName() {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.getId() == Item.DYE && item.getDamage() == 0x0F) {
- ObjectTallGrass.growGrass(this.getLevel(), this, new NukkitRandom());
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ ObjectTallGrass.growGrass(this.getLevel(), this);
this.level.addParticle(new BoneMealParticle(this));
if (player != null) {
if (!player.isCreative()) {
@@ -61,6 +64,9 @@ public boolean onActivate(Item item, Player player) {
if (up instanceof BlockAir || up instanceof BlockFlowable) {
item.useOn(this);
this.getLevel().setBlock(this, Block.get(FARMLAND));
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
return true;
}
} else if (item.isShovel()) {
@@ -68,6 +74,9 @@ public boolean onActivate(Item item, Player player) {
if (up instanceof BlockAir || up instanceof BlockFlowable) {
item.useOn(this);
this.getLevel().setBlock(this, Block.get(GRASS_PATH));
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
return true;
}
}
@@ -88,10 +97,9 @@ public int onUpdate(int type) {
return 0;
}
- NukkitRandom random = new NukkitRandom();
- int xx = random.nextRange((int) x - 1, (int) x + 1);
- int yy = random.nextRange((int) y - 2, (int) y + 2);
- int zz = random.nextRange((int) z - 1, (int) z + 1);
+ int xx = Utils.rand((int) x - 1, (int) x + 1);
+ int yy = Utils.rand((int) y - 2, (int) y + 2);
+ int zz = Utils.rand((int) z - 1, (int) z + 1);
Block block = this.getLevel().getBlock(xx, yy, zz);
if (block.getId() == Block.DIRT && block.getDamage() == 0) {
up = block.up();
@@ -119,11 +127,18 @@ public boolean canSilkTouch() {
@Override
public int getFullId() {
- return this.getId() << 4;
+ return this.getId() << Block.DATA_BITS;
}
@Override
public void setDamage(int meta) {
+ }
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+ return new Item[]{new ItemBlock(Block.get(BlockID.DIRT))};
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockGrassPath.java b/src/main/java/cn/nukkit/block/BlockGrassPath.java
index e8bde73f525..8409c798abb 100644
--- a/src/main/java/cn/nukkit/block/BlockGrassPath.java
+++ b/src/main/java/cn/nukkit/block/BlockGrassPath.java
@@ -2,7 +2,7 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Sound;
import cn.nukkit.utils.BlockColor;
/**
@@ -11,9 +11,6 @@
*/
public class BlockGrassPath extends BlockGrass {
- public BlockGrassPath() {
- }
-
@Override
public int getId() {
return GRASS_PATH;
@@ -24,29 +21,11 @@ public String getName() {
return "Grass Path";
}
- @Override
- public int getToolType() {
- return ItemTool.TYPE_SHOVEL;
- }
-
- @Override
- public double getMaxY() {
- return this.y + 1;
- }
-
@Override
public double getResistance() {
return 3.25;
}
- @Override
- public BlockColor getColor() { return BlockColor.DIRT_BLOCK_COLOR; }
-
- @Override
- public boolean canSilkTouch() {
- return true;
- }
-
@Override
public int onUpdate(int type) {
return 0;
@@ -55,11 +34,27 @@ public int onUpdate(int type) {
@Override
public boolean onActivate(Item item, Player player) {
if (item.isHoe()) {
- item.useOn(this);
- this.getLevel().setBlock(this, get(FARMLAND), true);
- return true;
+ Block up = this.up();
+ if (up instanceof BlockAir || up instanceof BlockFlowable) {
+ item.useOn(this);
+ this.getLevel().setBlock(this, get(FARMLAND), true);
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
+ return true;
+ }
}
return false;
}
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DIRT_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getMaxY() {
+ return this.y + 0.9375;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockGravel.java b/src/main/java/cn/nukkit/block/BlockGravel.java
index d298404c7d2..df8fab2ce86 100644
--- a/src/main/java/cn/nukkit/block/BlockGravel.java
+++ b/src/main/java/cn/nukkit/block/BlockGravel.java
@@ -1,21 +1,21 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemFlint;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.item.ItemTool;
-
-import java.util.Random;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.level.generator.object.ObjectTallGrass;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockGravel extends BlockFallable {
-
- public BlockGravel() {
- }
-
@Override
public int getId() {
return GRAVEL;
@@ -43,9 +43,9 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (new Random().nextInt(9) == 0) {
+ if (Utils.random.nextInt(9) == 0 && !item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
return new Item[]{
- new ItemFlint()
+ Item.get(Item.FLINT)
};
} else {
return new Item[]{
@@ -53,9 +53,38 @@ public Item[] getDrops(Item item) {
};
}
}
-
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GRAY_BLOCK_COLOR;
+ }
+
@Override
public boolean canSilkTouch() {
return true;
}
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player != null && item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ Block up = this.up();
+ if (up instanceof BlockWater) {
+ if (!player.isCreative()) {
+ item.count--;
+ }
+ this.level.addParticle(new BoneMealParticle(this));
+ if (up.getDamage() == 0 && up.up() instanceof BlockWater) {
+ ObjectTallGrass.growSeagrass(this.getLevel(), this);
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockGrindstone.java b/src/main/java/cn/nukkit/block/BlockGrindstone.java
new file mode 100644
index 00000000000..e96c1e03870
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockGrindstone.java
@@ -0,0 +1,135 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Faceable;
+
+public class BlockGrindstone extends BlockTransparentMeta implements Faceable {
+
+ public static final int TYPE_ATTACHMENT_STANDING = 0;
+ public static final int TYPE_ATTACHMENT_HANGING = 1;
+ public static final int TYPE_ATTACHMENT_SIDE = 2;
+ public static final int TYPE_ATTACHMENT_MULTIPLE = 3;
+
+ public BlockGrindstone() {
+ this(0);
+ }
+
+ public BlockGrindstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Grindstone";
+ }
+
+ @Override
+ public int getId() {
+ return GRINDSTONE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.IRON_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(getDamage() & 0b11);
+ }
+
+ public void setBlockFace(BlockFace face) {
+ if (face.getHorizontalIndex() == -1) {
+ return;
+ }
+ setDamage(getDamage() & (DATA_MASK ^ 0b11) | face.getHorizontalIndex());
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(GRINDSTONE));
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (block.getId() != AIR && block.canBeReplaced()) {
+ face = BlockFace.UP;
+ }
+
+ switch (face) {
+ case UP:
+ this.setAttachmentType(TYPE_ATTACHMENT_STANDING);
+ this.setBlockFace(player.getDirection().getOpposite());
+ break;
+ case DOWN:
+ this.setAttachmentType(TYPE_ATTACHMENT_HANGING);
+ this.setBlockFace(player.getDirection().getOpposite());
+ break;
+ default:
+ this.setBlockFace(face);
+ this.setAttachmentType(TYPE_ATTACHMENT_SIDE);
+ }
+
+ if (!this.checkSupport()) {
+ return false;
+ }
+
+ return super.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ public int getAttachmentType() {
+ return (getDamage() & 0b1100) >> 2 & 0b11;
+ }
+
+ public void setAttachmentType(int attachmentType) {
+ attachmentType = attachmentType & 0b11;
+ setDamage(getDamage() & (DATA_MASK ^ 0b1100) | (attachmentType << 2));
+ }
+
+ private boolean checkSupport() {
+ switch (this.getAttachmentType()) {
+ case TYPE_ATTACHMENT_STANDING:
+ if (down().getId() != AIR) {
+ return true;
+ }
+ break;
+ case TYPE_ATTACHMENT_HANGING:
+ if (up().getId() != AIR) {
+ return true;
+ }
+ break;
+ case TYPE_ATTACHMENT_SIDE:
+ BlockFace blockFace = getBlockFace();
+ if (getSide(blockFace.getOpposite()).getId() != AIR) {
+ return true;
+ }
+ break;
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockHayBale.java b/src/main/java/cn/nukkit/block/BlockHayBale.java
index 7bef3ed7b47..963926520d5 100644
--- a/src/main/java/cn/nukkit/block/BlockHayBale.java
+++ b/src/main/java/cn/nukkit/block/BlockHayBale.java
@@ -12,6 +12,16 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockHayBale extends BlockSolidMeta implements Faceable {
+
+ private static final short[] faces = {
+ 0,
+ 0,
+ 0b1000,
+ 0b1000,
+ 0b0100,
+ 0b0100,
+ };
+
public BlockHayBale() {
this(0);
}
@@ -40,11 +50,6 @@ public double getResistance() {
return 2.5;
}
- @Override
- public int getToolType() {
- return ItemTool.TYPE_HOE;
- }
-
@Override
public int getBurnChance() {
return 60;
@@ -55,16 +60,13 @@ public int getBurnAbility() {
return 20;
}
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- int[] faces = new int[]{
- 0,
- 0,
- 0b1000,
- 0b1000,
- 0b0100,
- 0b0100,
- };
this.setDamage((this.getDamage() & 0x03) | faces[face.getIndex()]);
this.getLevel().setBlock(block, this, true, true);
@@ -78,7 +80,7 @@ public BlockColor getColor() {
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockHoneyBlock.java b/src/main/java/cn/nukkit/block/BlockHoneyBlock.java
new file mode 100644
index 00000000000..2e6058b30c0
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockHoneyBlock.java
@@ -0,0 +1,56 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.math.Vector3;
+
+public class BlockHoneyBlock extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Honey Block";
+ }
+
+ @Override
+ public int getId() {
+ return HONEY_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (!entity.onGround && entity.motionY <= 0.08 && !(entity instanceof Player)) {
+ double ex = Math.abs(x + 0.5D - entity.x);
+ double ez = Math.abs(z + 0.5D - entity.z);
+ double width = 0.4375D + (double)(entity.getWidth() / 2.0F);
+ if (ex + 1.0E-3D > width || ez + 1.0E-3D > width) {
+ Vector3 motion = entity.getMotion();
+ motion.y = -0.05;
+ if (entity.motionY < -0.13) {
+ double m = -0.05 / entity.motionY;
+ motion.x *= m;
+ motion.z *= m;
+ }
+
+ if (!entity.getMotion().equals(motion)) {
+ entity.setMotion(motion);
+ }
+ entity.resetFallDistance();
+ }
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockHoneycombBlock.java b/src/main/java/cn/nukkit/block/BlockHoneycombBlock.java
new file mode 100644
index 00000000000..7c477911b66
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockHoneycombBlock.java
@@ -0,0 +1,37 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockHoneycombBlock extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Honeycomb Block";
+ }
+
+ @Override
+ public int getId() {
+ return HONEYCOMB_BLOCK;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.6;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_NONE;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockHopper.java b/src/main/java/cn/nukkit/block/BlockHopper.java
index dc58c00cf10..1b6c8eb13ca 100644
--- a/src/main/java/cn/nukkit/block/BlockHopper.java
+++ b/src/main/java/cn/nukkit/block/BlockHopper.java
@@ -5,7 +5,6 @@
import cn.nukkit.blockentity.BlockEntityHopper;
import cn.nukkit.inventory.ContainerInventory;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemHopper;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
@@ -56,13 +55,13 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
this.setDamage(facing.getIndex());
- boolean powered = this.level.isBlockPowered(this.getLocation());
+ boolean powered = this.level.isBlockPowered(this);
if (powered == this.isEnabled()) {
this.setEnabled(!powered);
}
- this.level.setBlock(this, this);
+ this.getLevel().setBlock(this, this, true, true);
CompoundTag nbt = new CompoundTag()
.putList(new ListTag<>("Items"))
@@ -71,8 +70,8 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
.putInt("y", (int) this.y)
.putInt("z", (int) this.z);
- BlockEntityHopper hopper = (BlockEntityHopper) BlockEntity.createBlockEntity(BlockEntity.HOPPER, this.level.getChunk(this.getFloorX() >> 4, this.getFloorZ() >> 4), nbt);
- return hopper != null;
+ BlockEntity.createBlockEntity(BlockEntity.HOPPER, this.getChunk(), nbt);
+ return true;
}
@Override
@@ -123,11 +122,11 @@ public void setEnabled(boolean enabled) {
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
- boolean powered = this.level.isBlockPowered(this.getLocation());
+ boolean powered = this.level.isBlockPowered(this);
if (powered == this.isEnabled()) {
this.setEnabled(!powered);
- this.level.setBlock(this, this, true, false);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
}
return type;
@@ -152,7 +151,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemHopper();
+ return Item.get(Item.HOPPER);
}
@Override
@@ -162,6 +161,16 @@ public boolean canHarvestWithHand() {
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockHugeMushroomBrown.java b/src/main/java/cn/nukkit/block/BlockHugeMushroomBrown.java
index 3ee1bb5d380..93f21cf1315 100644
--- a/src/main/java/cn/nukkit/block/BlockHugeMushroomBrown.java
+++ b/src/main/java/cn/nukkit/block/BlockHugeMushroomBrown.java
@@ -3,8 +3,9 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
-import cn.nukkit.math.NukkitRandom;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 28.01.2016.
@@ -39,20 +40,12 @@ public double getHardness() {
return 0.2;
}
- @Override
- public double getResistance() {
- return 1;
- }
-
@Override
public Item[] getDrops(Item item) {
- if (new NukkitRandom().nextRange(1, 20) == 0) {
- return new Item[]{
- new ItemBlock(Block.get(BlockID.BROWN_MUSHROOM))
- };
- } else {
- return new Item[0];
+ if (item != null && item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
}
+ return new Item[]{new ItemBlock(Block.get(BROWN_MUSHROOM), 0, Utils.rand() ? Utils.rand(0, 2) : 0)};
}
@Override
@@ -61,5 +54,7 @@ public boolean canSilkTouch() {
}
@Override
- public BlockColor getColor() { return BlockColor.DIRT_BLOCK_COLOR; }
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java b/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java
index 4b24523a5ad..08d19cbff7f 100644
--- a/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java
+++ b/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java
@@ -3,8 +3,9 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
-import cn.nukkit.math.NukkitRandom;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 28.01.2016.
@@ -39,20 +40,12 @@ public double getHardness() {
return 0.2;
}
- @Override
- public double getResistance() {
- return 1;
- }
-
@Override
public Item[] getDrops(Item item) {
- if (new NukkitRandom().nextRange(1, 20) == 0) {
- return new Item[]{
- new ItemBlock(Block.get(BlockID.RED_MUSHROOM))
- };
- } else {
- return new Item[0];
+ if (item != null && item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
}
+ return new Item[]{new ItemBlock(Block.get(RED_MUSHROOM), 0, Utils.rand() ? Utils.rand(0, 2) : 0)};
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockHyphaeCrimson.java b/src/main/java/cn/nukkit/block/BlockHyphaeCrimson.java
new file mode 100644
index 00000000000..bff5138dd04
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockHyphaeCrimson.java
@@ -0,0 +1,44 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockHyphaeCrimson extends BlockStem {
+
+ public BlockHyphaeCrimson() {
+ this(0);
+ }
+
+ public BlockHyphaeCrimson(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_HYPHAE;
+ }
+
+ @Override
+ public int getStrippedId() {
+ return STRIPPED_CRIMSON_HYPHAE;
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Hyphae";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.3; // 2
+ }
+
+ @Override
+ public double getResistance() {
+ return 2;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_HYPHAE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockHyphaeStrippedCrimson.java b/src/main/java/cn/nukkit/block/BlockHyphaeStrippedCrimson.java
new file mode 100644
index 00000000000..0bc86caba82
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockHyphaeStrippedCrimson.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockHyphaeStrippedCrimson extends BlockStemStripped {
+
+ public BlockHyphaeStrippedCrimson() {
+ this(0);
+ }
+
+ public BlockHyphaeStrippedCrimson(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_CRIMSON_HYPHAE;
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Stripped Hyphae";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.3; // 2
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CRIMSON_HYPHAE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockHyphaeStrippedWarped.java b/src/main/java/cn/nukkit/block/BlockHyphaeStrippedWarped.java
new file mode 100644
index 00000000000..83d16c54bc3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockHyphaeStrippedWarped.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockHyphaeStrippedWarped extends BlockStemStripped {
+
+ public BlockHyphaeStrippedWarped() {
+ this(0);
+ }
+
+ public BlockHyphaeStrippedWarped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_WARPED_HYPHAE;
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Stripped Hyphae";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.3; // 2
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_HYPHAE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockHyphaeWarped.java b/src/main/java/cn/nukkit/block/BlockHyphaeWarped.java
new file mode 100644
index 00000000000..e10bde7a5f6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockHyphaeWarped.java
@@ -0,0 +1,43 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockHyphaeWarped extends BlockStem {
+
+ public BlockHyphaeWarped() {
+ this(0);
+ }
+
+ public BlockHyphaeWarped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_HYPHAE;
+ }
+
+ @Override
+ public int getStrippedId() {
+ return STRIPPED_WARPED_HYPHAE;
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Hyphae";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_HYPHAE_BLOCK_COLOR;
+ }
+
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 2;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockID.java b/src/main/java/cn/nukkit/block/BlockID.java
index 2bc85eaa001..d071dce3389 100644
--- a/src/main/java/cn/nukkit/block/BlockID.java
+++ b/src/main/java/cn/nukkit/block/BlockID.java
@@ -1,6 +1,10 @@
package cn.nukkit.block;
+/**
+ * List of block IDs
+ */
public interface BlockID {
+
int AIR = 0;
int STONE = 1;
int GRASS = 2;
@@ -49,6 +53,7 @@ public interface BlockID {
int PISTON = 33;
int PISTON_HEAD = 34;
int WOOL = 35;
+ int WHITE_WOOL = 35;
int DANDELION = 37;
int POPPY = 38;
int ROSE = 38;
@@ -102,7 +107,6 @@ public interface BlockID {
int STONE_PRESSURE_PLATE = 70;
int IRON_DOOR_BLOCK = 71;
int WOODEN_PRESSURE_PLATE = 72;
-
int REDSTONE_ORE = 73;
int GLOWING_REDSTONE_ORE = 74;
int LIT_REDSTONE_ORE = 74;
@@ -171,7 +175,6 @@ public interface BlockID {
int DRAGON_EGG = 122;
int REDSTONE_LAMP = 123;
int LIT_REDSTONE_LAMP = 124;
- //Note: dropper CAN NOT BE HARVESTED WITH HAND -- canHarvestWithHand method should be overridden FALSE.
int DROPPER = 125;
int ACTIVATOR_RAIL = 126;
int COCOA = 127;
@@ -258,6 +261,9 @@ public interface BlockID {
int FENCE_GATE_ACACIA = 187;
int REPEATING_COMMAND_BLOCK = 188;
int CHAIN_COMMAND_BLOCK = 189;
+ int HARD_GLASS_PANE = 190;
+ int HARD_STAINED_GLASS_PANE = 191;
+ int CHEMICAL_HEAT = 192;
int SPRUCE_DOOR_BLOCK = 193;
int BIRCH_DOOR_BLOCK = 194;
int JUNGLE_DOOR_BLOCK = 195;
@@ -267,11 +273,11 @@ public interface BlockID {
int ITEM_FRAME_BLOCK = 199;
int CHORUS_FLOWER = 200;
int PURPUR_BLOCK = 201;
-
+ int COLORED_TORCH_RG = 202;
int PURPUR_STAIRS = 203;
+ int COLORED_TORCH_BP = 204;
int UNDYED_SHULKER_BOX = 205;
int END_BRICKS = 206;
- //Note: frosted ice CAN NOT BE HARVESTED WITH HAND -- canHarvestWithHand method should be overridden FALSE.
int FROSTED_ICE = 207;
int ICE_FROSTED = 207;
int END_ROD = 208;
@@ -283,7 +289,7 @@ public interface BlockID {
int BLOCK_NETHER_WART_BLOCK = 214;
int RED_NETHER_BRICK = 215;
int BONE_BLOCK = 216;
-
+ // 217 not yet in Minecraft
int SHULKER_BOX = 218;
int PURPLE_GLAZED_TERRACOTTA = 219;
int WHITE_GLAZED_TERRACOTTA = 220;
@@ -296,6 +302,7 @@ public interface BlockID {
int GRAY_GLAZED_TERRACOTTA = 227;
int SILVER_GLAZED_TERRACOTTA = 228;
int CYAN_GLAZED_TERRACOTTA = 229;
+ // 230 Chalkboard in Education Edition
int BLUE_GLAZED_TERRACOTTA = 231;
int BROWN_GLAZED_TERRACOTTA = 232;
int GREEN_GLAZED_TERRACOTTA = 233;
@@ -304,19 +311,351 @@ public interface BlockID {
int CONCRETE = 236;
int CONCRETE_POWDER = 237;
int CONCRETEPOWDER = 237;
-
+ int CHEMISTRY_TABLE = 238;
+ int UNDERWATER_TORCH = 239;
int CHORUS_PLANT = 240;
int STAINED_GLASS = 241;
+ int CAMERA_BLOCK = 242;
int PODZOL = 243;
int BEETROOT_BLOCK = 244;
int STONECUTTER = 245;
int GLOWING_OBSIDIAN = 246;
- int NETHER_REACTOR = 247; //Should not be removed
+ int NETHER_REACTOR = 247;
int INFO_UPDATE = 248;
int INFO_UPDATE2 = 249;
-
int PISTON_EXTENSION = 250;
-
+ int MOVING_BLOCK = 250;
int OBSERVER = 251;
int STRUCTURE_BLOCK = 252;
+ int HARD_GLASS = 253;
+ int HARD_STAINED_GLASS = 254;
+ int RESERVED6 = 255;
+ // 256 not yet in Minecraft
+ int PRISMARINE_STAIRS = 257;
+ int DARK_PRISMARINE_STAIRS = 258;
+ int PRISMARINE_BRICKS_STAIRS = 259;
+ int STRIPPED_SPRUCE_LOG = 260;
+ int STRIPPED_BIRCH_LOG = 261;
+ int STRIPPED_JUNGLE_LOG = 262;
+ int STRIPPED_ACACIA_LOG = 263;
+ int STRIPPED_DARK_OAK_LOG = 264;
+ int STRIPPED_OAK_LOG = 265;
+ int BLUE_ICE = 266;
+ //
+ int SEAGRASS = 385;
+ int CORAL = 386;
+ int CORAL_BLOCK = 387;
+ int CORAL_FAN = 388;
+ int CORAL_FAN_DEAD = 389;
+ int CORAL_FAN_HANG = 390;
+ int CORAL_FAN_HANG2 = 391;
+ int CORAL_FAN_HANG3 = 392;
+ int BLOCK_KELP = 393;
+ int DRIED_KELP_BLOCK = 394;
+ int ACACIA_BUTTON = 395;
+ int BIRCH_BUTTON = 396;
+ int DARK_OAK_BUTTON = 397;
+ int JUNGLE_BUTTON = 398;
+ int SPRUCE_BUTTON = 399;
+ int ACACIA_TRAPDOOR = 400;
+ int BIRCH_TRAPDOOR = 401;
+ int DARK_OAK_TRAPDOOR = 402;
+ int JUNGLE_TRAPDOOR = 403;
+ int SPRUCE_TRAPDOOR = 404;
+ int ACACIA_PRESSURE_PLATE = 405;
+ int BIRCH_PRESSURE_PLATE = 406;
+ int DARK_OAK_PRESSURE_PLATE = 407;
+ int JUNGLE_PRESSURE_PLATE = 408;
+ int SPRUCE_PRESSURE_PLATE = 409;
+ int CARVED_PUMPKIN = 410;
+ int SEA_PICKLE = 411;
+ int CONDUIT = 412;
+ // 413 not yet in Minecraft
+ int TURTLE_EGG = 414;
+ int BUBBLE_COLUMN = 415;
+ int BARRIER = 416;
+ int STONE_SLAB3 = 417;
+ int BAMBOO = 418;
+ int BAMBOO_SAPLING = 419;
+ int SCAFFOLDING = 420;
+ int STONE_SLAB4 = 421;
+ int DOUBLE_STONE_SLAB3 = 422;
+ int DOUBLE_STONE_SLAB4 = 423;
+ int GRANITE_STAIRS = 424;
+ int DIORITE_STAIRS = 425;
+ int ANDESITE_STAIRS = 426;
+ int POLISHED_GRANITE_STAIRS = 427;
+ int POLISHED_DIORITE_STAIRS = 428;
+ int POLISHED_ANDESITE_STAIRS = 429;
+ int MOSSY_STONE_BRICK_STAIRS = 430;
+ int SMOOTH_RED_SANDSTONE_STAIRS = 431;
+ int SMOOTH_SANDSTONE_STAIRS = 432;
+ int END_BRICK_STAIRS = 433;
+ int MOSSY_COBBLESTONE_STAIRS = 434;
+ int NORMAL_STONE_STAIRS = 435;
+ int SPRUCE_STANDING_SIGN = 436;
+ int SPRUCE_WALL_SIGN = 437;
+ int SMOOTH_STONE = 438;
+ int RED_NETHER_BRICK_STAIRS = 439;
+ int SMOOTH_QUARTZ_STAIRS = 440;
+ int BIRCH_STANDING_SIGN = 441;
+ int BIRCH_WALL_SIGN = 442;
+ int JUNGLE_STANDING_SIGN = 443;
+ int JUNGLE_WALL_SIGN = 444;
+ int ACACIA_STANDING_SIGN = 445;
+ int ACACIA_WALL_SIGN = 446;
+ int DARK_OAK_STANDING_SIGN = 447;
+ int DARK_OAK_WALL_SIGN = 448;
+ int LECTERN = 449;
+ int GRINDSTONE = 450;
+ int BLAST_FURNACE = 451;
+ int STONECUTTER_BLOCK = 452;
+ int SMOKER = 453;
+ int LIT_SMOKER = 454;
+ int CARTOGRAPHY_TABLE = 455;
+ int FLETCHING_TABLE = 456;
+ int SMITHING_TABLE = 457;
+ int BARREL = 458;
+ int LOOM = 459;
+ //
+ int BELL = 461;
+ int SWEET_BERRY_BUSH = 462;
+ int LANTERN = 463;
+ int CAMPFIRE_BLOCK = 464;
+ int LAVA_CAULDRON = 465;
+ int JIGSAW = 466;
+ int WOOD_BARK = 467;
+ int COMPOSTER = 468;
+ int LIT_BLAST_FURNACE = 469;
+ int LIGHT_BLOCK = 470;
+ int WITHER_ROSE = 471;
+ int PISTON_HEAD_STICKY = 472;
+ int BEE_NEST = 473;
+ int BEEHIVE = 474;
+ int HONEY_BLOCK = 475;
+ int HONEYCOMB_BLOCK = 476;
+ int LODESTONE = 477;
+ int CRIMSON_ROOTS = 478;
+ int WARPED_ROOTS = 479;
+ int CRIMSON_STEM = 480;
+ int WARPED_STEM = 481;
+ int WARPED_WART_BLOCK = 482;
+ int CRIMSON_FUNGUS = 483;
+ int WARPED_FUNGUS = 484;
+ int SHROOMLIGHT = 485;
+ int WEEPING_VINES = 486;
+ int CRIMSON_NYLIUM = 487;
+ int WARPED_NYLIUM = 488;
+ int BASALT = 489;
+ int POLISHED_BASALT = 490;
+ int SOUL_SOIL = 491;
+ int SOUL_FIRE = 492;
+ int NETHER_SPROUTS_BLOCK = 493;
+ int TARGET = 494;
+ int STRIPPED_CRIMSON_STEM = 495;
+ int STRIPPED_WARPED_STEM = 496;
+ int CRIMSON_PLANKS = 497;
+ int WARPED_PLANKS = 498;
+ int CRIMSON_DOOR_BLOCK = 499;
+ int WARPED_DOOR_BLOCK = 500;
+ int CRIMSON_TRAPDOOR = 501;
+ int WARPED_TRAPDOOR = 502;
+
+ int CRIMSON_STANDING_SIGN = 505;
+ int WARPED_STANDING_SIGN = 506;
+ int CRIMSON_WALL_SIGN = 507;
+ int WARPED_WALL_SIGN = 508;
+ int CRIMSON_STAIRS = 509;
+ int WARPED_STAIRS = 510;
+ int CRIMSON_FENCE = 511;
+ int WARPED_FENCE = 512;
+ int CRIMSON_FENCE_GATE = 513;
+ int WARPED_FENCE_GATE = 514;
+ int CRIMSON_BUTTON = 515;
+ int WARPED_BUTTON = 516;
+ int CRIMSON_PRESSURE_PLATE = 517;
+ int WARPED_PRESSURE_PLATE = 518;
+ int CRIMSON_SLAB = 519;
+ int WARPED_SLAB = 520;
+ int CRIMSON_DOUBLE_SLAB = 521;
+ int WARPED_DOUBLE_SLAB = 522;
+ int SOUL_TORCH = 523;
+ int SOUL_LANTERN = 524;
+ int NETHERITE_BLOCK = 525;
+ int ANCIENT_DEBRIS = 526;
+ int RESPAWN_ANCHOR = 527;
+ int BLACKSTONE = 528;
+ int POLISHED_BLACKSTONE_BRICKS = 529;
+ int POLISHED_BLACKSTONE_BRICK_STAIRS = 530;
+ int BLACKSTONE_STAIRS = 531;
+ int BLACKSTONE_WALL = 532;
+ int POLISHED_BLACKSTONE_BRICK_WALL = 533;
+ int CHISELED_POLISHED_BLACKSTONE = 534;
+ int CRACKED_POLISHED_BLACKSTONE_BRICKS = 535;
+ int GILDED_BLACKSTONE = 536;
+ int BLACKSTONE_SLAB = 537;
+ int BLACKSTONE_DOUBLE_SLAB = 538;
+ int POLISHED_BLACKSTONE_BRICK_SLAB = 539;
+ int POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB = 540;
+ int CHAIN_BLOCK = 541;
+ int TWISTING_VINES = 542;
+ int NETHER_GOLD_ORE = 543;
+ int CRYING_OBSIDIAN = 544;
+ int SOUL_CAMPFIRE_BLOCK = 545;
+ int POLISHED_BLACKSTONE = 546;
+ int POLISHED_BLACKSTONE_STAIRS = 547;
+ int POLISHED_BLACKSTONE_SLAB = 548;
+ int POLISHED_BLACKSTONE_DOUBLE_SLAB = 549;
+ int POLISHED_BLACKSTONE_PRESSURE_PLATE = 550;
+ int POLISHED_BLACKSTONE_BUTTON = 551;
+ int POLISHED_BLACKSTONE_WALL = 552;
+ int WARPED_HYPHAE = 553;
+ int CRIMSON_HYPHAE = 554;
+ int STRIPPED_CRIMSON_HYPHAE = 555;
+ int STRIPPED_WARPED_HYPHAE = 556;
+ int CHISELED_NETHER_BRICKS = 557;
+ int CRACKED_NETHER_BRICKS = 558;
+ int QUARTZ_BRICKS = 559;
+
+ int POWDER_SNOW = 561;
+ int SCULK_SENSOR = 562;
+ int POINTED_DRIPSTONE = 563;
+ // 564 (unused)
+ // 565 (unused)
+ int COPPER_ORE = 566;
+ int LIGHTNING_ROD = 567;
+ // 568 (unused)
+ // 569 (unused)
+ // 570 (unused)
+ // 571 (unused)
+ int DRIPSTONE_BLOCK = 572;
+ int ROOTED_DIRT = 573;
+ int HANGING_ROOTS = 574;
+ int MOSS_BLOCK = 575;
+ int SPORE_BLOSSOM = 576;
+ int CAVE_VINES = 577;
+ int BIG_DRIPLEAF = 578;
+ int AZALEA_LEAVES = 579;
+ int AZALEA_LEAVES_FLOWERED = 580;
+ int CALCITE = 581;
+ int AMETHYST_BLOCK = 582;
+ int BUDDING_AMETHYST = 583;
+ int AMETHYST_CLUSTER = 584;
+ int LARGE_AMETHYST_BUD = 585;
+ int MEDIUM_AMETHYST_BUD = 586;
+ int SMALL_AMETHYST_BUD = 587;
+ int TUFF = 588;
+ int TINTED_GLASS = 589;
+ int MOSS_CARPET = 590;
+ int SMALL_DRIPLEAF = 591;
+ int AZALEA = 592;
+ int FLOWERING_AZALEA = 593;
+ int GLOW_FRAME = 594;
+ int COPPER_BLOCK = 595;
+ int EXPOSED_COPPER = 596;
+ int WEATHERED_COPPER = 597;
+ int OXIDIZED_COPPER = 598;
+ int WAXED_COPPER = 599;
+ int WAXED_EXPOSED_COPPER = 600;
+ int WAXED_WEATHERED_COPPER = 601;
+ int CUT_COPPER = 602;
+ int EXPOSED_CUT_COPPER = 603;
+ int WEATHERED_CUT_COPPER = 604;
+ int OXIDIZED_CUT_COPPER = 605;
+ int WAXED_CUT_COPPER = 606;
+ int WAXED_EXPOSED_CUT_COPPER = 607;
+ int WAXED_WEATHERED_CUT_COPPER = 608;
+ int CUT_COPPER_STAIRS = 609;
+ int EXPOSED_CUT_COPPER_STAIRS = 610;
+ int WEATHERED_CUT_COPPER_STAIRS = 611;
+ int OXIDIZED_CUT_COPPER_STAIRS = 612;
+ int WAXED_CUT_COPPER_STAIRS = 613;
+ int WAXED_EXPOSED_CUT_COPPER_STAIRS = 614;
+ int WAXED_WEATHERED_CUT_COPPER_STAIRS = 615;
+ int CUT_COPPER_SLAB = 616;
+ int EXPOSED_CUT_COPPER_SLAB = 617;
+ int WEATHERED_CUT_COPPER_SLAB = 618;
+ int OXIDIZED_CUT_COPPER_SLAB = 619;
+ int WAXED_CUT_COPPER_SLAB = 620;
+ int WAXED_EXPOSED_CUT_COPPER_SLAB = 621;
+ int WAXED_WEATHERED_CUT_COPPER_SLAB = 622;
+ int DOUBLE_CUT_COPPER_SLAB = 623;
+ int EXPOSED_DOUBLE_CUT_COPPER_SLAB = 624;
+ int WEATHERED_DOUBLE_CUT_COPPER_SLAB = 625;
+ int OXIDIZED_DOUBLE_CUT_COPPER_SLAB = 626;
+ int WAXED_DOUBLE_CUT_COPPER_SLAB = 627;
+ int WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB = 628;
+ int WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB = 629;
+ int CAVE_VINES_BODY_WITH_BERRIES = 630;
+ int CAVE_VINES_HEAD_WITH_BERRIES = 631;
+ int SMOOTH_BASALT = 632;
+ int DEEPSLATE = 633;
+ int COBBLED_DEEPSLATE = 634;
+ int COBBLED_DEEPSLATE_SLAB = 635;
+ int COBBLED_DEEPSLATE_STAIRS = 636;
+ int COBBLED_DEEPSLATE_WALL = 637;
+ int POLISHED_DEEPSLATE = 638;
+ int POLISHED_DEEPSLATE_SLAB = 639;
+ int POLISHED_DEEPSLATE_STAIRS = 640;
+ int POLISHED_DEEPSLATE_WALL = 641;
+ int DEEPSLATE_TILES = 642;
+ int DEEPSLATE_TILE_SLAB = 643;
+ int DEEPSLATE_TILE_STAIRS = 644;
+ int DEEPSLATE_TILE_WALL = 645;
+ int DEEPSLATE_BRICKS = 646;
+ int DEEPSLATE_BRICK_SLAB = 647;
+ int DEEPSLATE_BRICK_STAIRS = 648;
+ int DEEPSLATE_BRICK_WALL = 649;
+ int CHISELED_DEEPSLATE = 650;
+ int COBBLED_DEEPSLATE_DOUBLE_SLAB = 651;
+ int POLISHED_DEEPSLATE_DOUBLE_SLAB = 652;
+ int DEEPSLATE_TILE_DOUBLE_SLAB = 653;
+ int DEEPSLATE_BRICK_DOUBLE_SLAB = 654;
+ int DEEPSLATE_LAPIS_ORE = 655;
+ int DEEPSLATE_IRON_ORE = 656;
+ int DEEPSLATE_GOLD_ORE = 657;
+ int DEEPSLATE_REDSTONE_ORE = 658;
+ int LIT_DEEPSLATE_REDSTONE_ORE = 659;
+ int DEEPSLATE_DIAMOND_ORE = 660;
+ int DEEPSLATE_COAL_ORE = 661;
+ int DEEPSLATE_EMERALD_ORE = 662;
+ int DEEPSLATE_COPPER_ORE = 663;
+ int CRACKED_DEEPSLATE_TILES = 664;
+ int CRACKED_DEEPSLATE_BRICKS = 665;
+ int GLOW_LICHEN = 666;
+ int CANDLE = 667;
+ int WHITE_CANDLE = 668;
+ int ORANGE_CANDLE = 669;
+ int MAGENTA_CANDLE = 670;
+ int LIGHT_BLUE_CANDLE = 671;
+ int YELLOW_CANDLE = 672;
+ int LIME_CANDLE = 673;
+ int PINK_CANDLE = 674;
+ int GRAY_CANDLE = 675;
+ int LIGHT_GRAY_CANDLE = 676;
+ int CYAN_CANDLE = 677;
+ int PURPLE_CANDLE = 678;
+ int BLUE_CANDLE = 679;
+ int BROWN_CANDLE = 680;
+ int GREEN_CANDLE = 681;
+ int RED_CANDLE = 682;
+ int BLACK_CANDLE = 683;
+ //
+ int WAXED_OXIDIZED_COPPER = 701;
+ int WAXED_OXIDIZED_CUT_COPPER = 702;
+ int WAXED_OXIDIZED_CUT_COPPER_STAIRS = 703;
+ int WAXED_OXIDIZED_CUT_COPPER_SLAB = 704;
+ int WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB = 705;
+ int RAW_IRON_BLOCK = 706;
+ int RAW_COPPER_BLOCK = 707;
+ int RAW_GOLD_BLOCK = 708;
+ int INFESTED_DEEPSLATE = 709;
+
+ int MUD = 728;
+ int MUD_BRICKS = 730;
+ int PACKED_MUD = 732;
+ int MUD_BRICK_SLAB = 733;
+ int MUD_BRICK_DOUBLE_SLAB = 734;
+ int MUD_BRICK_STAIRS = 735;
+ int MUD_BRICK_WALL = 736;
}
diff --git a/src/main/java/cn/nukkit/block/BlockIce.java b/src/main/java/cn/nukkit/block/BlockIce.java
index fe039468082..3bf6a2672d2 100644
--- a/src/main/java/cn/nukkit/block/BlockIce.java
+++ b/src/main/java/cn/nukkit/block/BlockIce.java
@@ -8,14 +8,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockIce extends BlockTransparent {
- public BlockIce() {
- }
-
@Override
public int getId() {
return ICE;
@@ -28,7 +25,7 @@ public String getName() {
@Override
public double getResistance() {
- return 2.5;
+ return 0.5;
}
@Override
@@ -48,11 +45,10 @@ public int getToolType() {
@Override
public boolean onBreak(Item item) {
- if (this.getLevel().getDimension() != Level.DIMENSION_NETHER) {
- return this.getLevel().setBlock(this, Block.get(BlockID.WATER), true);
- } else {
+ if (this.getLevel().getDimension() == Level.DIMENSION_NETHER || item.hasEnchantment(Enchantment.ID_SILK_TOUCH) || down().getId() == BlockID.AIR) {
return super.onBreak(item);
}
+ return this.getLevel().setBlock(this, Block.get(BlockID.WATER), true);
}
@Override
@@ -82,7 +78,7 @@ public Item[] getDrops(Item item) {
public BlockColor getColor() {
return BlockColor.ICE_BLOCK_COLOR;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
diff --git a/src/main/java/cn/nukkit/block/BlockIceFrosted.java b/src/main/java/cn/nukkit/block/BlockIceFrosted.java
new file mode 100644
index 00000000000..45007fd067e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockIceFrosted.java
@@ -0,0 +1,125 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
+
+public class BlockIceFrosted extends BlockTransparentMeta {
+
+ public BlockIceFrosted() {
+ this(0);
+ }
+
+ public BlockIceFrosted(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return ICE_FROSTED;
+ }
+
+ @Override
+ public String getName() {
+ return "Frosted Ice";
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.5;
+ }
+
+ @Override
+ public double getFrictionFactor() {
+ return 0.98;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ boolean success = super.place(item, block, target, face, fx, fy, fz, player);
+ if (success) {
+ level.scheduleUpdate(this, Utils.random.nextInt(20, 40));
+ }
+ return success;
+ }
+
+ @Override
+ public boolean onBreak(Item item) {
+ level.setBlock(this, get(WATER), true);
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ int time = level.getTime() % Level.TIME_FULL;
+ if (((time < 13184 || time > 22800) || this.getLevel().getBlockLightAt((int) this.x, (int) this.y, (int) this.z) >= 12) && (Utils.random.nextInt(3) == 0 || countNeighbors() < 4)) {
+ slightlyMelt(true);
+ } else {
+ level.scheduleUpdate(this, Utils.random.nextInt(20, 40));
+ }
+ } else if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (countNeighbors() < 2) {
+ level.setBlock(this, get(WATER), true);
+ }
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ int time = level.getTime() % Level.TIME_FULL;
+ if (((time < 13184 || time > 22800) || this.getLevel().getBlockLightAt((int) this.x, (int) this.y, (int) this.z) >= 12) && (Utils.random.nextInt(3) == 0 || countNeighbors() < 4)) {
+ slightlyMelt(true);
+ }
+ }
+ return super.onUpdate(type);
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(AIR);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ICE_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ protected void slightlyMelt(boolean isSource) {
+ int age = getDamage();
+ if (age < 3) {
+ setDamage(age + 1);
+ level.setBlock(this, this, true);
+ level.scheduleUpdate(level.getBlock(this), Utils.random.nextInt(20, 40));
+ } else {
+ level.setBlock(this, get(WATER), true);
+ if (isSource) {
+ for (BlockFace face : BlockFace.values()) {
+ Block block = getSide(face);
+ if (block instanceof BlockIceFrosted) {
+ ((BlockIceFrosted) block).slightlyMelt(false);
+ }
+ }
+ }
+ }
+ }
+
+ private int countNeighbors() {
+ int neighbors = 0;
+ for (BlockFace face : BlockFace.values()) {
+ if (getSide(face).getId() == ICE_FROSTED && ++neighbors >= 4) {
+ return neighbors;
+ }
+ }
+ return neighbors;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockIcePacked.java b/src/main/java/cn/nukkit/block/BlockIcePacked.java
index ae8f9ec26c0..0cb32b1ca62 100644
--- a/src/main/java/cn/nukkit/block/BlockIcePacked.java
+++ b/src/main/java/cn/nukkit/block/BlockIcePacked.java
@@ -1,17 +1,14 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockIcePacked extends BlockIce {
- public BlockIcePacked() {
- }
-
@Override
public int getId() {
return PACKED_ICE;
@@ -23,29 +20,33 @@ public String getName() {
}
@Override
- public int getToolType() {
- return ItemTool.TYPE_PICKAXE;
+ public double getHardness() {
+ return 0.5;
}
@Override
- public int onUpdate(int type) {
- return 0; //not being melted
+ public boolean onBreak(Item item) {
+ return this.getLevel().setBlock(this, Block.get(BlockID.AIR), true);
}
@Override
- public boolean canHarvestWithHand() {
- return false;
+ public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{
+ this.toItem()
+ };
+ }
+ return new Item[0];
}
-
+
@Override
- public boolean onBreak(Item item) {
- this.getLevel().setBlock(this, Block.get(BlockID.AIR), true); //no water
- return true;
+ public int onUpdate(int type) {
+ return 0;
}
@Override
- public boolean canSilkTouch() {
- return true;
+ public boolean canHarvestWithHand() {
+ return false;
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockInfestedDeepslate.java b/src/main/java/cn/nukkit/block/BlockInfestedDeepslate.java
new file mode 100644
index 00000000000..d17518da376
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockInfestedDeepslate.java
@@ -0,0 +1,44 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockInfestedDeepslate extends BlockDeepslate {
+
+ public BlockInfestedDeepslate() {
+ this(0);
+ }
+
+ public BlockInfestedDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return INFESTED_DEEPSLATE;
+ }
+
+ @Override
+ public String getName() {
+ return "Infested Deepslate";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.75;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockInfoUpdate.java b/src/main/java/cn/nukkit/block/BlockInfoUpdate.java
new file mode 100644
index 00000000000..163608548ed
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockInfoUpdate.java
@@ -0,0 +1,26 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockInfoUpdate extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return INFO_UPDATE;
+ }
+
+ @Override
+ public String getName() {
+ return "Update Game Block";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.1;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockInfoUpdate2.java b/src/main/java/cn/nukkit/block/BlockInfoUpdate2.java
new file mode 100644
index 00000000000..ba62efccc76
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockInfoUpdate2.java
@@ -0,0 +1,21 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockInfoUpdate2 extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return INFO_UPDATE2;
+ }
+
+ @Override
+ public String getName() {
+ return "Update Game Block";
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockIron.java b/src/main/java/cn/nukkit/block/BlockIron.java
index ff825a00591..e59d64f4c33 100644
--- a/src/main/java/cn/nukkit/block/BlockIron.java
+++ b/src/main/java/cn/nukkit/block/BlockIron.java
@@ -5,15 +5,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockIron extends BlockSolid {
-
- public BlockIron() {
- }
-
@Override
public int getId() {
return IRON_BLOCK;
@@ -21,7 +17,7 @@ public int getId() {
@Override
public String getName() {
- return "Iron Block";
+ return "Block of Iron";
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockIronBars.java b/src/main/java/cn/nukkit/block/BlockIronBars.java
index 9f80a1d1ca5..a91c61d8216 100644
--- a/src/main/java/cn/nukkit/block/BlockIronBars.java
+++ b/src/main/java/cn/nukkit/block/BlockIronBars.java
@@ -11,9 +11,6 @@
*/
public class BlockIronBars extends BlockThin {
- public BlockIronBars() {
- }
-
@Override
public String getName() {
return "Iron Bars";
@@ -41,12 +38,12 @@ public int getToolType() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
this.toItem()
};
@@ -64,4 +61,9 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockItemFrame.java b/src/main/java/cn/nukkit/block/BlockItemFrame.java
index 02bcabf585f..6df32dc0273 100644
--- a/src/main/java/cn/nukkit/block/BlockItemFrame.java
+++ b/src/main/java/cn/nukkit/block/BlockItemFrame.java
@@ -4,7 +4,6 @@
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityItemFrame;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemItemFrame;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.nbt.tag.CompoundTag;
@@ -18,10 +17,11 @@
* Created by Pub4Game on 03.07.2016.
*/
public class BlockItemFrame extends BlockTransparentMeta implements Faceable {
- private final static int[] FACING = new int[]{4, 5, 3, 2, 1, 0}; // TODO when 1.13 support arrives, add UP/DOWN facings
+
+ private final static int[] FACING = {4, 5, 3, 2, 1, 0};
private final static int FACING_BITMASK = 0b0111;
- private final static int MAP_BIT = 0b1000;
+ //private final static int MAP_BIT = 0b1000;
public BlockItemFrame() {
this(0);
@@ -61,15 +61,22 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
BlockEntity blockEntity = this.getLevel().getBlockEntity(this);
+ if (!(blockEntity instanceof BlockEntityItemFrame)) {
+ return false;
+ }
+
BlockEntityItemFrame itemFrame = (BlockEntityItemFrame) blockEntity;
+ if (itemFrame.getItem() == null) {
+ return true;
+ }
if (itemFrame.getItem().getId() == Item.AIR) {
- Item itemOnFrame = item.clone();
- if (player != null && player.isSurvival()) {
- itemOnFrame.setCount(itemOnFrame.getCount() - 1);
- player.getInventory().setItemInHand(itemOnFrame);
+ Item itemToFrame = item.clone();
+ if (player != null && !player.isCreative()) {
+ item.setCount(item.getCount() - 1);
+ player.getInventory().setItemInHand(item);
}
- itemOnFrame.setCount(1);
- itemFrame.setItem(itemOnFrame);
+ itemToFrame.setCount(1);
+ itemFrame.setItem(itemToFrame);
this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_ITEM_FRAME_ITEM_ADDED);
} else {
itemFrame.setItemRotation((itemFrame.getItemRotation() + 1) % 8);
@@ -80,9 +87,11 @@ public boolean onActivate(Item item, Player player) {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (face.getIndex() > 1 && target.isSolid() && (!block.isSolid() || block.canBeReplaced())) {
+ if (target.isSolid() && (!block.isSolid() || block.canBeReplaced())) {
this.setDamage(FACING[face.getIndex()]);
- this.getLevel().setBlock(block, this, true, true);
+
+ this.getLevel().setBlock(this, this, true, true);
+
CompoundTag nbt = new CompoundTag()
.putString("id", BlockEntity.ITEM_FRAME)
.putInt("x", (int) block.x)
@@ -95,10 +104,8 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
nbt.put(aTag.getName(), aTag);
}
}
- BlockEntityItemFrame frame = (BlockEntityItemFrame) BlockEntity.createBlockEntity(BlockEntity.ITEM_FRAME, this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- if (frame == null) {
- return false;
- }
+ BlockEntity.createBlockEntity(BlockEntity.ITEM_FRAME, this.getChunk(), nbt);
+ this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_ITEM_FRAME_PLACED);
return true;
}
return false;
@@ -128,7 +135,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemItemFrame();
+ return Item.get(Item.ITEM_FRAME);
}
@Override
@@ -162,6 +169,10 @@ public BlockFace getFacing() {
return BlockFace.NORTH;
case 3:
return BlockFace.SOUTH;
+ case 4:
+ return BlockFace.UP;
+ case 5:
+ return BlockFace.DOWN;
}
return null;
@@ -172,13 +183,28 @@ public double getHardness() {
return 0.25;
}
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
@Override
public BlockFace getBlockFace() {
return this.getFacing().getOpposite();
}
@Override
- public boolean isSolid() {
- return false;
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockItemFrameGlow.java b/src/main/java/cn/nukkit/block/BlockItemFrameGlow.java
new file mode 100644
index 00000000000..a7db61b1e3e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockItemFrameGlow.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockItemFrameGlow extends BlockItemFrame {
+
+ public BlockItemFrameGlow() {
+ this(0);
+ }
+
+ public BlockItemFrameGlow(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Glow Item Frame";
+ }
+
+ @Override
+ public int getId() {
+ return GLOW_FRAME;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.GLOW_ITEM_FRAME);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockJigsaw.java b/src/main/java/cn/nukkit/block/BlockJigsaw.java
new file mode 100644
index 00000000000..df68ee485bb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockJigsaw.java
@@ -0,0 +1,79 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Faceable;
+
+public class BlockJigsaw extends BlockSolidMeta implements Faceable {
+
+ public BlockJigsaw() {
+ this(0);
+ }
+
+ public BlockJigsaw(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Jigsaw";
+ }
+
+ @Override
+ public int getId() {
+ return JIGSAW;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromIndex(getDamage());
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) {
+ double y = player.y + player.getEyeHeight();
+ if (y - this.y > 2) {
+ this.setDamage(BlockFace.UP.getIndex());
+ } else if (this.y - y > 0) {
+ this.setDamage(BlockFace.DOWN.getIndex());
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+ } else {
+ this.setDamage(player.getHorizontalFacing().getOpposite().getIndex());
+ }
+ return super.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockJukebox.java b/src/main/java/cn/nukkit/block/BlockJukebox.java
index c0efcac6220..821fbbf1c6e 100644
--- a/src/main/java/cn/nukkit/block/BlockJukebox.java
+++ b/src/main/java/cn/nukkit/block/BlockJukebox.java
@@ -6,9 +6,11 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemRecord;
+import cn.nukkit.item.ItemTool;
import cn.nukkit.math.BlockFace;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;
+import cn.nukkit.network.protocol.TextPacket;
import cn.nukkit.utils.BlockColor;
/**
@@ -16,9 +18,6 @@
*/
public class BlockJukebox extends BlockSolid {
- public BlockJukebox() {
- }
-
@Override
public String getName() {
return "Jukebox";
@@ -29,6 +28,21 @@ public int getId() {
return JUKEBOX;
}
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
@Override
public boolean canBeActivated() {
return true;
@@ -36,14 +50,14 @@ public boolean canBeActivated() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public boolean onActivate(Item item, Player player) {
BlockEntity blockEntity = this.getLevel().getBlockEntity(this);
if (!(blockEntity instanceof BlockEntityJukebox)) {
- blockEntity = this.createBlockEntity();
+ return false;
}
BlockEntityJukebox jukebox = (BlockEntityJukebox) blockEntity;
@@ -52,7 +66,16 @@ public boolean onActivate(Item item, Player player) {
} else if (item instanceof ItemRecord) {
jukebox.setRecordItem(item);
jukebox.play();
- player.getInventory().decreaseCount(player.getInventory().getHeldItemIndex());
+ if (player != null) {
+ player.getInventory().decreaseCount(player.getInventory().getHeldItemIndex());
+
+ TextPacket pk = new TextPacket();
+ pk.type = TextPacket.TYPE_JUKEBOX_POPUP;
+ pk.message = "%record.nowPlaying";
+ pk.parameters = new String[]{((ItemRecord) item).getDiscName()};
+ pk.isLocalized = true;
+ player.dataPacket(pk);
+ }
}
return false;
@@ -68,6 +91,20 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
return false;
}
+ /*@Override // Replaced with BlockEntityJukebox#onBreak
+ public boolean onBreak(Item item) {
+ if (super.onBreak(item)) {
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+
+ if (blockEntity instanceof BlockEntityJukebox) {
+ ((BlockEntityJukebox) blockEntity).dropItem();
+ }
+ return true;
+ }
+
+ return false;
+ }*/
+
private BlockEntity createBlockEntity() {
CompoundTag nbt = new CompoundTag()
.putList(new ListTag<>("Items"))
@@ -76,11 +113,16 @@ private BlockEntity createBlockEntity() {
.putInt("y", getFloorY())
.putInt("z", getFloorZ());
- return BlockEntity.createBlockEntity(BlockEntity.JUKEBOX, this.level.getChunk(getFloorX() >> 4, getFloorZ() >> 4), nbt);
+ return BlockEntity.createBlockEntity(BlockEntity.JUKEBOX, this.getChunk(), nbt);
}
@Override
public BlockColor getColor() {
- return BlockColor.DIRT_BLOCK_COLOR;
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockJungleSignStanding.java b/src/main/java/cn/nukkit/block/BlockJungleSignStanding.java
new file mode 100644
index 00000000000..e9d9b5d4a5e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockJungleSignStanding.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockJungleSignStanding extends BlockSignPost {
+
+ public BlockJungleSignStanding() {
+ this(0);
+ }
+
+ public BlockJungleSignStanding(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Jungle Sign Post";
+ }
+
+ @Override
+ public int getId() {
+ return JUNGLE_STANDING_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.JUNGLE_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return JUNGLE_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return JUNGLE_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockJungleWallSign.java b/src/main/java/cn/nukkit/block/BlockJungleWallSign.java
new file mode 100644
index 00000000000..9b21a0912fb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockJungleWallSign.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockJungleWallSign extends BlockWallSign {
+
+ public BlockJungleWallSign() {
+ this(0);
+ }
+
+ public BlockJungleWallSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Jungle Wall Sign";
+ }
+
+ @Override
+ public int getId() {
+ return JUNGLE_WALL_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.JUNGLE_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return JUNGLE_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return JUNGLE_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockKelp.java b/src/main/java/cn/nukkit/block/BlockKelp.java
new file mode 100644
index 00000000000..75556260b10
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockKelp.java
@@ -0,0 +1,254 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.format.anvil.Anvil;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.MathHelper;
+import cn.nukkit.utils.Utils;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockKelp extends BlockFlowable {
+
+ public BlockKelp() {
+ this(0);
+ }
+
+ public BlockKelp(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Kelp";
+ }
+
+ @Override
+ public int getId() {
+ return BLOCK_KELP;
+ }
+
+ public double getHardness() {
+ return 0;
+ }
+
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return level == null || !(level.getProvider() instanceof Anvil);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (level != null && level.getProvider() instanceof Anvil) {
+ Block down;
+ if (!((block instanceof BlockWater && (block.getDamage() == 0 || block.getDamage() >= 8)) || block instanceof BlockBubbleColumn) || ((down = this.down()).getId() != this.getId() && down.isTransparent())) {
+ return false;
+ }
+ // 15 is the highest supported meta value
+ // Meta 0 does not grow to not overgrow previously generated kelp
+ this.setDamage(Utils.rand(1, 15));
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+
+
+ Block down = this.down();
+ Block layer1Block = block.getLevelBlock(BlockLayer.WATERLOGGED);
+ int waterDamage;
+ if ((down.getId() == BLOCK_KELP || down.isSolid()) && down.getId() != MAGMA && down.getId() != ICE && down.getId() != SOUL_SAND &&
+ (layer1Block instanceof BlockWater && ((waterDamage = (block.getDamage())) == 0 || waterDamage == 8))
+ ) {
+ if (waterDamage == 8) {
+ this.getLevel().setBlock(this, BlockLayer.WATERLOGGED, Block.get(WATER), true, false);
+ }
+
+ if (down.getId() == BLOCK_KELP && down.getDamage() != 24) {
+ down.setDamage(24);
+ this.getLevel().setBlock(down, down, true, true);
+ }
+
+ // Placing it by hand gives it a random age value between 0 and 24
+ // Meta 0 does not grow to not overgrow previously generated kelp
+ this.setDamage(Utils.rand(1, 24));
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (level != null && level.getProvider() instanceof Anvil) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block down = this.down();
+ if (down.getId() != this.getId() && down.isTransparent()) {
+ this.getLevel().useBreakOn(this);
+ }
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ // Ignore meta value 0 to not overgrow previously generated kelp
+ if (this.getDamage() > 0 && ThreadLocalRandom.current().nextInt(100) < 14) { // 14% chance to grow
+ if (this.getDamage() < 15) {
+ Block up = up();
+ if (up instanceof BlockWater && up.getDamage() == 0) {
+ Block grown = Block.get(BLOCK_KELP, this.getDamage() + 1);
+ BlockGrowEvent ev = new BlockGrowEvent(this, grown);
+ Server.getInstance().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
+ this.getLevel().setBlock(up, ev.getNewState(), true, true);
+ }
+ }
+ }
+ }
+ }
+ return type;
+ }
+
+
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block blockLayer1 = this.getLevelBlock(BlockLayer.WATERLOGGED);
+ int waterDamage = 0;
+ if (!(blockLayer1 instanceof BlockIceFrosted) &&
+ (!(blockLayer1 instanceof BlockWater) || ((waterDamage = blockLayer1.getDamage()) != 0 && waterDamage != 8))) {
+ this.getLevel().useBreakOn(this);
+ return type;
+ }
+
+ Block down = this.down();
+ if ((!down.isSolid() && down.getId() != BLOCK_KELP) || down.getId() == MAGMA || down.getId() == ICE || down.getId() == SOUL_SAND) {
+ this.getLevel().useBreakOn(this);
+ return type;
+ }
+
+ if (waterDamage == 8) {
+ this.getLevel().setBlock(this, BlockLayer.WATERLOGGED, Block.get(WATER), true, false);
+ }
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ // Ignore meta value 0 to not overgrow previously generated
+ if (this.getDamage() > 0 && ThreadLocalRandom.current().nextInt(100) < 14) { // 14% chance to grow
+ this.grow();
+ }
+ return type;
+ }
+ return super.onUpdate(type);
+ }
+
+ public boolean grow() {
+ int age = MathHelper.clamp(this.getDamage(), 0, 25);
+ if (age < 25) {
+ Block up = this.up();
+ if (up instanceof BlockWater && (up.getDamage() == 0 || up.getDamage() == 8)) {
+ Block grown = Block.get(BLOCK_KELP, age + 1);
+ BlockGrowEvent ev = new BlockGrowEvent(this, grown);
+ this.level.getServer().getPluginManager().callEvent(ev);
+
+ if (!ev.isCancelled()) {
+ this.setDamage(25);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
+
+ this.getLevel().setBlock(up, BlockLayer.WATERLOGGED, Block.get(WATER), true, false);
+ this.getLevel().setBlock(up, BlockLayer.NORMAL, ev.getNewState(), true, true);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onBreak(Item item) {
+ if (level != null && level.getProvider() instanceof Anvil) {
+ return super.onBreak(item);
+ }
+
+
+ Block down = this.down();
+ if (down.getId() == BLOCK_KELP) {
+ this.getLevel().setBlock(down, Block.get(BLOCK_KELP, ThreadLocalRandom.current().nextInt(25)), true, true);
+ }
+ this.getLevel().setBlock(this, Block.get(AIR), true, true);
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (level != null && level.getProvider() instanceof Anvil) {
+ if (item.getId() == ItemID.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ if (this.grow()) {
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ level.addParticle(new BoneMealParticle(this));
+ }
+ return true;
+ }
+ return super.onActivate(item, player);
+ }
+
+
+ if (item.getId() != Item.DYE || item.getDamage() != ItemDye.BONE_MEAL) {
+ return false;
+ }
+ int x = (int) this.x;
+ int z = (int) this.z;
+
+ for (int y = (int) this.y + 1; y < 255; y++) {
+ int blockIdAbove = this.getLevel().getBlockIdAt(x, y, z);
+ if (blockIdAbove == BLOCK_KELP) continue;
+ if (blockIdAbove != WATER && blockIdAbove != STILL_WATER) {
+ return false;
+ }
+
+ int waterData = this.getLevel().getBlockDataAt(x, y, z);
+ if (waterData == 0 || waterData == 8) {
+ BlockKelp highestKelp = (BlockKelp) this.getLevel().getBlock(x, y - 1, z);
+ if (highestKelp.grow()) {
+ this.level.addParticle(new BoneMealParticle(this));
+
+ if (player != null && (player.gamemode & 0x01) == 0) {
+ item.count--;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.KELP, 0, 1);
+ }
+
+ @Override
+ public AxisAlignedBB getBoundingBox() {
+ return null;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockLadder.java b/src/main/java/cn/nukkit/block/BlockLadder.java
index 014ac90b437..7906b886c4d 100644
--- a/src/main/java/cn/nukkit/block/BlockLadder.java
+++ b/src/main/java/cn/nukkit/block/BlockLadder.java
@@ -1,10 +1,10 @@
package cn.nukkit.block;
import cn.nukkit.Player;
+import cn.nukkit.entity.Entity;
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.utils.BlockColor;
import cn.nukkit.utils.Faceable;
@@ -15,13 +15,23 @@
*/
public class BlockLadder extends BlockTransparentMeta implements Faceable {
+ private static final int[] faces = {
+ 0, //never use
+ 1, //never use
+ 3,
+ 2,
+ 5,
+ 4
+ };
+
public BlockLadder() {
this(0);
}
public BlockLadder(int meta) {
super(meta);
- calculateOffsets();
+
+ this.calculateOffsets();
}
@Override
@@ -94,19 +104,13 @@ private void calculateOffsets() {
break;
default:
this.offMinX = 0;
- this.offMinZ = 1 ;
+ this.offMinZ = 1;
this.offMaxX = 1;
this.offMaxZ = 1;
break;
}
}
- @Override
- public void setDamage(int meta) {
- super.setDamage(meta);
- calculateOffsets();
- }
-
@Override
public double getMinX() {
return this.x + offMinX;
@@ -127,17 +131,12 @@ public double getMaxZ() {
return this.z + offMaxZ;
}
- @Override
- protected AxisAlignedBB recalculateCollisionBoundingBox() {
- return super.recalculateBoundingBox();
- }
-
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (!target.isTransparent()) {
if (face.getIndex() >= 2 && face.getIndex() <= 5) {
this.setDamage(face.getIndex());
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
}
@@ -147,14 +146,6 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
- int[] faces = {
- 0, //never use
- 1, //never use
- 3,
- 2,
- 5,
- 4
- };
if (!this.getSide(BlockFace.fromIndex(faces[this.getDamage()])).isSolid()) {
this.getLevel().useBreakOn(this);
return Level.BLOCK_UPDATE_NORMAL;
@@ -172,16 +163,31 @@ public int getToolType() {
public BlockColor getColor() {
return BlockColor.AIR_BLOCK_COLOR;
}
-
+
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- Item.get(Item.LADDER, 0, 1)
+ Item.get(Item.LADDER)
};
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ entity.resetFallDistance();
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockLantern.java b/src/main/java/cn/nukkit/block/BlockLantern.java
new file mode 100644
index 00000000000..5c79ab6028f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLantern.java
@@ -0,0 +1,168 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockLantern extends BlockFlowable {
+
+ public BlockLantern() {
+ this(0);
+ }
+
+ public BlockLantern(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Lantern";
+ }
+
+ @Override
+ public int getId() {
+ return LANTERN;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 15;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.IRON_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(LANTERN));
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return this;
+ }
+
+ @Override
+ public double getMinX() {
+ return this.x + 0.3125;
+ }
+
+ @Override
+ public double getMinZ() {
+ return this.z + 0.3125;
+ }
+
+ @Override
+ public double getMaxX() {
+ return this.x + 0.6875;
+ }
+
+ @Override
+ public double getMaxY() {
+ return this.y + 0.5;
+ }
+
+ @Override
+ public double getMaxZ() {
+ return this.z + 0.6875;
+ }
+
+ private boolean isBlockAboveValid() {
+ Block support = this.up();
+ switch (support.getId()) {
+ case IRON_BARS:
+ case HOPPER_BLOCK:
+ case CHAIN_BLOCK:
+ return true;
+ default:
+ if (support instanceof BlockFence) {
+ return true;
+ }
+ if (support instanceof BlockSlab && (support.getDamage() & 0x08) == 0x00) {
+ return true;
+ }
+ if (support instanceof BlockStairs && (support.getDamage() & 0x04) == 0x00) {
+ return true;
+ }
+ return !support.isTransparent() && support.isSolid() && !support.isPowerSource();
+ }
+ }
+
+ private boolean isBlockUnderValid() {
+ Block down = this.down();
+ if (down instanceof BlockLeaves) {
+ return false;
+ } else if (down instanceof BlockFence || down instanceof BlockWall) {
+ return true;
+ } else if (down instanceof BlockSlab) {
+ return (down.getDamage() & 0x08) == 0x08;
+ } else if (down instanceof BlockStairs) {
+ return (down.getDamage() & 0x04) == 0x04;
+ } else return down.isSolid() || down instanceof BlockChain;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ boolean isUnderValid = this.isBlockUnderValid();
+ boolean hanging = face != BlockFace.UP && this.isBlockAboveValid() && (!isUnderValid || face == BlockFace.DOWN);
+ if (!isUnderValid && !hanging) {
+ return false;
+ }
+
+ if (hanging) {
+ this.setDamage(1);
+ } else {
+ this.setDamage(0);
+ }
+
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (this.getDamage() == 0) {
+ if (!this.isBlockUnderValid()) {
+ level.useBreakOn(this, null, null, true);
+ }
+ } else if (!this.isBlockAboveValid()) {
+ level.useBreakOn(this, null, null, true);
+ }
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockLapis.java b/src/main/java/cn/nukkit/block/BlockLapis.java
index a8c0166fdac..a7d24808f66 100644
--- a/src/main/java/cn/nukkit/block/BlockLapis.java
+++ b/src/main/java/cn/nukkit/block/BlockLapis.java
@@ -5,15 +5,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockLapis extends BlockSolid {
-
- public BlockLapis() {
- }
-
@Override
public int getId() {
return LAPIS_BLOCK;
@@ -59,5 +55,4 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockLava.java b/src/main/java/cn/nukkit/block/BlockLava.java
index fc90be4c510..c495bdc9cbc 100644
--- a/src/main/java/cn/nukkit/block/BlockLava.java
+++ b/src/main/java/cn/nukkit/block/BlockLava.java
@@ -2,6 +2,7 @@
import cn.nukkit.Player;
import cn.nukkit.Server;
+import cn.nukkit.entity.BaseEntity;
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.item.EntityPrimedTNT;
import cn.nukkit.event.block.BlockIgniteEvent;
@@ -15,12 +16,10 @@
import cn.nukkit.math.Vector3;
import cn.nukkit.potion.Effect;
import cn.nukkit.utils.BlockColor;
-
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockLava extends BlockLiquid {
@@ -52,17 +51,22 @@ public String getName() {
public void onEntityCollide(Entity entity) {
entity.highestPosition -= (entity.highestPosition - entity.y) * 0.5;
- EntityCombustByBlockEvent ev = new EntityCombustByBlockEvent(this, entity, 8);
- Server.getInstance().getPluginManager().callEvent(ev);
- if (!ev.isCancelled()
- // Making sure the entity is actually alive and not invulnerable.
- && entity.isAlive()
- && entity.noDamageTicks == 0) {
- entity.setOnFire(ev.getDuration());
- }
+ if (!entity.fireProof || !entity.isOnFire() || !(entity instanceof BaseEntity)) { // Improve performance
+ if (!entity.fireProof || !entity.isOnFire()) {
+
+ EntityCombustByBlockEvent ev = new EntityCombustByBlockEvent(this, entity, 8);
+ Server.getInstance().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()
+ // Making sure the entity is actually alive and not invulnerable
+ && entity.isAlive()
+ && entity.noDamageTicks == 0) {
+ entity.setOnFire(ev.getDuration());
+ }
+ }
- if (!entity.hasEffect(Effect.FIRE_RESISTANCE)) {
- entity.attack(new EntityDamageByBlockEvent(this, entity, DamageCause.LAVA, 4));
+ if (!entity.hasEffect(Effect.FIRE_RESISTANCE)) {
+ entity.attack(new EntityDamageByBlockEvent(this, entity, DamageCause.LAVA, 4));
+ }
}
super.onEntityCollide(entity);
@@ -81,13 +85,11 @@ public int onUpdate(int type) {
int result = super.onUpdate(type);
if (type == Level.BLOCK_UPDATE_RANDOM && this.level.gameRules.getBoolean(GameRule.DO_FIRE_TICK)) {
- Random random = ThreadLocalRandom.current();
-
- int i = random.nextInt(3);
+ int i = Utils.random.nextInt(3);
if (i > 0) {
for (int k = 0; k < i; ++k) {
- Vector3 v = this.add(random.nextInt(3) - 1, 1, random.nextInt(3) - 1);
+ Vector3 v = this.add(Utils.random.nextInt(3) - 1, 1, Utils.random.nextInt(3) - 1);
Block block = this.getLevel().getBlock(v);
if (block.getId() == AIR) {
@@ -110,7 +112,7 @@ public int onUpdate(int type) {
}
} else {
for (int k = 0; k < 3; ++k) {
- Vector3 v = this.add(random.nextInt(3) - 1, 0, random.nextInt(3) - 1);
+ Vector3 v = this.add(Utils.random.nextInt(3) - 1, 0, Utils.random.nextInt(3) - 1);
Block block = this.getLevel().getBlock(v);
if (block.up().getId() == AIR && block.getBurnChance() > 0) {
@@ -147,9 +149,9 @@ public BlockColor getColor() {
@Override
public BlockLiquid getBlock(int meta) {
- return (BlockLiquid) Block.get(BlockID.LAVA, meta);
+ return (BlockLiquid) Block.get(LAVA, meta);
}
-
+
@Override
public int tickRate() {
if (this.level.getDimension() == Level.DIMENSION_NETHER) {
@@ -167,29 +169,35 @@ public int getFlowDecayPerBlock() {
}
@Override
- protected void checkForHarden(){
+ protected void checkForHarden() {
Block colliding = null;
- for(int side = 1; side < 6; ++side){ //don't check downwards side
+ for (int side = 1; side < 6; ++side) { //don't check downwards side
Block blockSide = this.getSide(BlockFace.fromIndex(side));
- if(blockSide instanceof BlockWater){
+ if (blockSide instanceof BlockWater || blockSide.getLevelBlock(BlockLayer.WATERLOGGED) instanceof BlockWater) {
colliding = blockSide;
break;
}
+ if (blockSide instanceof BlockBlueIce) {
+ if (down() instanceof BlockSoulSoil) {
+ liquidCollide(this, Block.get(BlockID.BASALT));
+ return;
+ }
+ }
}
- if(colliding != null){
- if(this.getDamage() == 0){
- this.liquidCollide(colliding, Block.get(BlockID.OBSIDIAN));
- }else if(this.getDamage() <= 4){
- this.liquidCollide(colliding, Block.get(BlockID.COBBLESTONE));
+ if (colliding != null) {
+ if (this.getDamage() == 0) {
+ this.liquidCollide(colliding, Block.get(OBSIDIAN));
+ } else if (this.getDamage() <= 4) {
+ this.liquidCollide(colliding, Block.get(COBBLESTONE));
}
}
}
@Override
- protected void flowIntoBlock(Block block, int newFlowDecay){
- if(block instanceof BlockWater){
- ((BlockLiquid) block).liquidCollide(this, Block.get(BlockID.STONE));
- }else{
+ protected void flowIntoBlock(Block block, int newFlowDecay) {
+ if (block instanceof BlockWater) {
+ ((BlockLiquid) block).liquidCollide(this, Block.get(STONE));
+ } else {
super.flowIntoBlock(block, newFlowDecay);
}
}
@@ -200,4 +208,53 @@ public void addVelocityToEntity(Entity entity, Vector3 vector) {
super.addVelocityToEntity(entity, vector);
}
}
+
+ @Override
+ protected boolean[] getOptimalFlowDirections() {
+ int[] flowCost = {
+ 1000,
+ 1000,
+ 1000,
+ 1000
+ };
+ int maxCost = 4 / this.getFlowDecayPerBlock();
+ for (int j = 0; j < 4; ++j) {
+ int x = (int) this.x;
+ int y = (int) this.y;
+ int z = (int) this.z;
+ if (j == 0) {
+ --x;
+ } else if (j == 1) {
+ ++x;
+ } else if (j == 2) {
+ --z;
+ } else {
+ ++z;
+ }
+ Block block = this.level.getBlock(x, y, z);
+ if (!this.canFlowInto(block)) {
+ this.flowCostVisited.put(Level.blockHash(x, y, z, this.level.getDimensionData()), BLOCKED);
+ } else if (this.level.getBlock(x, y - 1, z).canBeFlowedInto()) {
+ this.flowCostVisited.put(Level.blockHash(x, y, z, this.level.getDimensionData()), CAN_FLOW_DOWN);
+ flowCost[j] = maxCost = 0;
+ } else if (maxCost > 0) {
+ this.flowCostVisited.put(Level.blockHash(x, y, z, this.level.getDimensionData()), CAN_FLOW);
+ flowCost[j] = this.calculateFlowCost(x, y, z, 1, maxCost, j ^ 0x01, j ^ 0x01);
+ maxCost = Math.min(maxCost, flowCost[j]);
+ }
+ }
+ this.flowCostVisited.clear();
+ double minCost = Double.MAX_VALUE;
+ for (int i = 0; i < 4; i++) {
+ double d = flowCost[i];
+ if (d < minCost) {
+ minCost = d;
+ }
+ }
+ boolean[] isOptimalFlowDirection = new boolean[4];
+ for (int i = 0; i < 4; ++i) {
+ isOptimalFlowDirection[i] = (flowCost[i] == minCost);
+ }
+ return isOptimalFlowDirection;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockLavaStill.java b/src/main/java/cn/nukkit/block/BlockLavaStill.java
index c587211d137..6c29fb16c5a 100644
--- a/src/main/java/cn/nukkit/block/BlockLavaStill.java
+++ b/src/main/java/cn/nukkit/block/BlockLavaStill.java
@@ -1,9 +1,7 @@
package cn.nukkit.block;
-import cn.nukkit.level.Level;
-
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockLavaStill extends BlockLava {
@@ -28,14 +26,6 @@ public String getName() {
@Override
public BlockLiquid getBlock(int meta) {
- return (BlockLiquid) Block.get(BlockID.STILL_LAVA, meta);
- }
-
- @Override
- public int onUpdate(int type) {
- if (type != Level.BLOCK_UPDATE_SCHEDULED) {
- return super.onUpdate(type);
- }
- return 0;
+ return (BlockLiquid) Block.get(STILL_LAVA, meta);
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockLayer.java b/src/main/java/cn/nukkit/block/BlockLayer.java
new file mode 100644
index 00000000000..8f17f52fb85
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLayer.java
@@ -0,0 +1,6 @@
+package cn.nukkit.block;
+
+public enum BlockLayer {
+ NORMAL,
+ WATERLOGGED
+}
diff --git a/src/main/java/cn/nukkit/block/BlockLeaves.java b/src/main/java/cn/nukkit/block/BlockLeaves.java
index 2185fc3f95a..654ea1e5d50 100644
--- a/src/main/java/cn/nukkit/block/BlockLeaves.java
+++ b/src/main/java/cn/nukkit/block/BlockLeaves.java
@@ -6,20 +6,21 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.Hash;
+import cn.nukkit.utils.Utils;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
-import java.util.concurrent.ThreadLocalRandom;
-
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockLeaves extends BlockTransparentMeta {
+
public static final int OAK = 0;
public static final int SPRUCE = 1;
public static final int BIRCH = 2;
@@ -50,7 +51,7 @@ public int getToolType() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Oak Leaves",
"Spruce Leaves",
"Birch Leaves",
@@ -71,8 +72,8 @@ public int getBurnAbility() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- this.setPersistent(true);
- this.getLevel().setBlock(this, this, true);
+ setPersistent(true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@@ -88,17 +89,20 @@ public Item[] getDrops(Item item) {
toItem()
};
} else {
- if (this.canDropApple() && ThreadLocalRandom.current().nextInt(200) == 0) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+ if (this.canDropApple() && Utils.random.nextInt(200) == 0) {
return new Item[]{
Item.get(Item.APPLE)
};
}
- if (ThreadLocalRandom.current().nextInt(20) == 0) {
- if (ThreadLocalRandom.current().nextBoolean()) {
+ if (Utils.random.nextInt(20) == 0) {
+ if (Utils.rand()) {
return new Item[]{
- Item.get(Item.STICK, 0, ThreadLocalRandom.current().nextInt(1, 2))
+ Item.get(Item.STICK, 0, Utils.random.nextInt(1, 2))
};
- } else if ((this.getDamage() & 0x03) != JUNGLE || ThreadLocalRandom.current().nextInt(20) == 0) {
+ } else if ((this.getDamage() & 0x03) != JUNGLE || Utils.random.nextInt(20) == 0) {
return new Item[]{
this.getSapling()
};
@@ -112,16 +116,15 @@ public Item[] getDrops(Item item) {
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_RANDOM && !isPersistent() && !isCheckDecay()) {
setCheckDecay(true);
- getLevel().setBlock(this, this, false, false);
+ getLevel().setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
} else if (type == Level.BLOCK_UPDATE_RANDOM && isCheckDecay() && !isPersistent()) {
- setDamage(getDamage() & 0x03);
- int check = 0;
+ this.setOnDecayDamage();
LeavesDecayEvent ev = new LeavesDecayEvent(this);
Server.getInstance().getPluginManager().callEvent(ev);
- if (ev.isCancelled() || findLog(this, new LongArraySet(), 0, check)) {
- getLevel().setBlock(this, this, false, false);
+ if (ev.isCancelled() || findLog(this, new LongArraySet(), 0, 0)) {
+ getLevel().setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, false, false); // No need to send this to client
} else {
getLevel().useBreakOn(this);
return Level.BLOCK_UPDATE_NORMAL;
@@ -130,6 +133,10 @@ public int onUpdate(int type) {
return 0;
}
+ protected void setOnDecayDamage() {
+ setDamage(getDamage() & 0x03);
+ }
+
private Boolean findLog(Block pos, LongSet visited, Integer distance, Integer check) {
return findLog(pos, visited, distance, check, null);
}
@@ -139,7 +146,7 @@ private Boolean findLog(Block pos, LongSet visited, Integer distance, Integer ch
long index = Hash.hashBlock((int) pos.x, (int) pos.y, (int) pos.z);
if (visited.contains(index)) return false;
if (pos.getId() == WOOD || pos.getId() == WOOD2) return true;
- if ((pos.getId() == LEAVES || pos.getId() == LEAVES2) && distance <= 4) {
+ if ((pos.getId() == LEAVES || pos.getId() == LEAVES2) && distance < 6) {
visited.add(index);
int down = pos.down().getId();
if (down == WOOD || down == WOOD2) {
@@ -198,7 +205,7 @@ public void setCheckDecay(boolean checkDecay) {
if (checkDecay) {
this.setDamage(this.getDamage() | 0x08);
} else {
- this.setDamage(this.getDamage() & ~0x08);
+ this.setDamage(this.getDamage() & -9);
}
}
@@ -210,7 +217,7 @@ public void setPersistent(boolean persistent) {
if (persistent) {
this.setDamage(this.getDamage() | 0x04);
} else {
- this.setDamage(this.getDamage() & ~0x04);
+ this.setDamage(this.getDamage() & -5);
}
}
@@ -231,4 +238,15 @@ protected boolean canDropApple() {
protected Item getSapling() {
return Item.get(BlockID.SAPLING, this.getDamage() & 0x03);
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
}
diff --git a/src/main/java/cn/nukkit/block/BlockLeaves2.java b/src/main/java/cn/nukkit/block/BlockLeaves2.java
index 4d95cea3578..bd936a427cc 100644
--- a/src/main/java/cn/nukkit/block/BlockLeaves2.java
+++ b/src/main/java/cn/nukkit/block/BlockLeaves2.java
@@ -4,12 +4,18 @@
/**
* Created on 2015/12/1 by xtypr.
- * Package cn.nukkit.block in project Nukkit .
+ * Package cn.nukkit.block in project Nukkit.
*/
public class BlockLeaves2 extends BlockLeaves {
+
public static final int ACACIA = 0;
public static final int DARK_OAK = 1;
+ private static final String[] names = {
+ "Acacia Leaves",
+ "Dark Oak Leaves"
+ };
+
public BlockLeaves2() {
this(0);
}
@@ -19,10 +25,6 @@ public BlockLeaves2(int meta) {
}
public String getName() {
- String[] names = new String[]{
- "Acacia Leaves",
- "Dark Oak Leaves"
- };
return names[this.getDamage() & 0x01];
}
@@ -33,7 +35,7 @@ public int getId() {
@Override
protected boolean canDropApple() {
- return (this.getDamage() & 0x01) != 0;
+ return (this.getDamage() & 0x01) == DARK_OAK;
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockLectern.java b/src/main/java/cn/nukkit/block/BlockLectern.java
new file mode 100644
index 00000000000..af43966cb75
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLectern.java
@@ -0,0 +1,171 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityLectern;
+import cn.nukkit.inventory.InventoryType;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.network.protocol.ContainerOpenPacket;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Faceable;
+
+public class BlockLectern extends BlockSolidMeta implements Faceable {
+
+ public BlockLectern() {
+ this(0);
+ }
+
+ public BlockLectern(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Lectern";
+ }
+
+ @Override
+ public int getId() {
+ return LECTERN;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ public double getHardness() {
+ return 2.5;
+ }
+
+ public double getResistance() {
+ return 2.5;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 30;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ int horizontalIndex = (player != null ? player.getDirection().getOpposite() : BlockFace.SOUTH).getHorizontalIndex();
+ if (horizontalIndex >= 0) {
+ this.setDamage(getDamage() & (15 ^ 0b11) | (horizontalIndex & 0b11));
+ }
+ CompoundTag nbt = new CompoundTag()
+ .putString("id", BlockEntity.LECTERN)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+ BlockEntityLectern lectern = (BlockEntityLectern) BlockEntity.createBlockEntity(BlockEntity.LECTERN, this.getChunk(), nbt);
+ if (lectern == null) {
+ return false;
+ }
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(getDamage() & 0b11);
+ }
+
+ public boolean dropBook() {
+ BlockEntity blockEntity = this.getLevel().getBlockEntity(this);
+ if (blockEntity instanceof BlockEntityLectern) {
+ BlockEntityLectern lectern = (BlockEntityLectern) blockEntity;
+ Item book = lectern.getBook();
+ if (book.getId() != BlockID.AIR) {
+ lectern.setBook(Item.get(BlockID.AIR));
+ lectern.spawnToAll();
+ this.level.dropItem(lectern.add(0.5f, 1, 0.5f), book);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isPowerSource() {
+ return true;
+ }
+
+ public boolean isActivated() {
+ return (this.getDamage() & 0x04) == 0x04;
+ }
+
+ public void setActivated(boolean activated) {
+ if (activated) {
+ setDamage(getDamage() | 0x04);
+ } else {
+ setDamage(getDamage() ^ 0x04);
+ }
+ }
+
+ @Override
+ public int getWeakPower(BlockFace face) {
+ return isActivated() ? 15 : 0;
+ }
+
+ @Override
+ public int getStrongPower(BlockFace side) {
+ return 0;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player != null) {
+ BlockEntity t = this.getLevel().getBlockEntity(this);
+ if (!(t instanceof BlockEntityLectern)) {
+ return false;
+ }
+
+ BlockEntityLectern lectern = (BlockEntityLectern) t;;
+ Item currentBook = lectern.getBook();
+ if (currentBook.getId() == BlockID.AIR) {
+ if (item.getId() == ItemID.WRITTEN_BOOK || item.getId() == ItemID.BOOK_AND_QUILL) {
+ Item newBook = item.clone();
+ if (player.isSurvival()) {
+ newBook.setCount(newBook.getCount() - 1);
+ player.getInventory().setItemInHand(newBook);
+ }
+ newBook.setCount(1);
+ lectern.setBook(newBook);
+ lectern.spawnToAll();
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_ITEM_BOOK_PUT);
+ }
+ } else {
+ ContainerOpenPacket pk = new ContainerOpenPacket();
+ pk.windowId = -1;
+ pk.type = InventoryType.LECTERN.getNetworkType();
+ pk.x = (int) x;
+ pk.y = (int) y;
+ pk.z = (int) z;
+ pk.entityId = player.getId();
+ player.dataPacket(pk);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockLever.java b/src/main/java/cn/nukkit/block/BlockLever.java
index 98903e236de..c43cfcdacf6 100644
--- a/src/main/java/cn/nukkit/block/BlockLever.java
+++ b/src/main/java/cn/nukkit/block/BlockLever.java
@@ -6,7 +6,7 @@
import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.network.protocol.LevelEventPacket;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.Faceable;
@@ -50,7 +50,7 @@ public double getResistance() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
@@ -68,12 +68,16 @@ public boolean onActivate(Item item, Player player) {
this.setDamage(this.getDamage() ^ 0x08);
this.getLevel().setBlock(this, this, false, true);
- this.getLevel().addLevelEvent(this.add(0.5, 0.5, 0.5), LevelEventPacket.EVENT_SOUND_BUTTON_CLICK, this.isPowerOn() ? 600 : 500);
+ if (this.isPowerOn()) {
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_ON);
+ } else {
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_OFF);
+ }
LeverOrientation orientation = LeverOrientation.byMetadata(this.isPowerOn() ? this.getDamage() ^ 0x08 : this.getDamage());
BlockFace face = orientation.getFacing();
- //this.level.updateAroundRedstone(this, null);
- this.level.updateAroundRedstone(this.getLocation().getSide(face.getOpposite()), isPowerOn() ? face : null);
+ level.updateAroundRedstone(this, null);
+ this.level.updateAroundRedstone(this.getSideVec(face.getOpposite()), isPowerOn() ? face : null);
return true;
}
@@ -104,8 +108,8 @@ public boolean onBreak(Item item) {
this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, true);
if (isPowerOn()) {
- BlockFace face = LeverOrientation.byMetadata(this.isPowerOn() ? this.getDamage() ^ 0x08 : this.getDamage()).getFacing();
- this.level.updateAround(this.getLocation().getSide(face.getOpposite()));
+ BlockFace face = LeverOrientation.byMetadata(this.getDamage() ^ 0x08).getFacing();
+ this.level.updateAround(this.getSideVec(face.getOpposite()));
}
return true;
}
@@ -116,7 +120,11 @@ public int getWeakPower(BlockFace side) {
}
public int getStrongPower(BlockFace side) {
- return !isPowerOn() ? 0 : LeverOrientation.byMetadata(this.isPowerOn() ? this.getDamage() ^ 0x08 : this.getDamage()).getFacing() == side ? 15 : 0;
+ if (!isPowerOn()) {
+ return 0;
+ } else {
+ return LeverOrientation.byMetadata(this.getDamage() ^ 0x08).getFacing() == side ? 15 : 0;
+ }
}
@Override
@@ -214,18 +222,33 @@ public String getName() {
static {
for (LeverOrientation face : values()) {
- META_LOOKUP[face.getMetadata()] = face;
+ META_LOOKUP[face.meta] = face;
}
}
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
@Override
public BlockColor getColor() {
return BlockColor.AIR_BLOCK_COLOR;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockLightBlock.java b/src/main/java/cn/nukkit/block/BlockLightBlock.java
new file mode 100644
index 00000000000..062911bccdc
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLightBlock.java
@@ -0,0 +1,75 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.math.AxisAlignedBB;
+
+public class BlockLightBlock extends BlockTransparentMeta {
+
+ public BlockLightBlock() {
+ this(0);
+ }
+
+ public BlockLightBlock(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Light Block";
+ }
+
+ @Override
+ public int getId() {
+ return LIGHT_BLOCK;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return getDamage() & 0xF;
+ }
+
+ @Override
+ public AxisAlignedBB getBoundingBox() {
+ return null;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return true;
+ }
+
+ @Override
+ public boolean canBeReplaced() {
+ return true;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3600000.8;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.AIR);
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockLightningRod.java b/src/main/java/cn/nukkit/block/BlockLightningRod.java
new file mode 100644
index 00000000000..b7de521c04d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLightningRod.java
@@ -0,0 +1,87 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+
+public class BlockLightningRod extends BlockTransparentMeta {
+
+ private static final int[] faces = {0, 1, 2, 3, 4, 5};
+
+ public BlockLightningRod() {
+ this(0);
+ }
+
+ public BlockLightningRod(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Lightning Rod";
+ }
+
+ @Override
+ public int getId() {
+ return LIGHTNING_ROD;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getMinX() {
+ return this.x + 0.4;
+ }
+
+ @Override
+ public double getMinZ() {
+ return this.z + 0.4;
+ }
+
+ @Override
+ public double getMaxX() {
+ return this.x + 0.6;
+ }
+
+ @Override
+ public double getMaxZ() {
+ return this.z + 0.6;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(faces[player != null ? face.getIndex() : 0]);
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_STONE) {
+ return new Item[]{
+ toItem()
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockLiquid.java b/src/main/java/cn/nukkit/block/BlockLiquid.java
index 688be7cf069..e81d7beaef5 100644
--- a/src/main/java/cn/nukkit/block/BlockLiquid.java
+++ b/src/main/java/cn/nukkit/block/BlockLiquid.java
@@ -6,29 +6,25 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
-import cn.nukkit.level.particle.SmokeParticle;
+import cn.nukkit.level.format.FullChunk;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.Vector3;
-import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.network.protocol.LevelSoundEventPacket;
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockLiquid extends BlockTransparentMeta {
- private final byte CAN_FLOW_DOWN = 1;
- private final byte CAN_FLOW = 0;
- private final byte BLOCKED = -1;
+ protected static final byte CAN_FLOW_DOWN = 1;
+ protected static final byte CAN_FLOW = 0;
+ protected static final byte BLOCKED = -1;
public int adjacentSources = 0;
protected Vector3 flowVector = null;
- private Long2ByteMap flowCostVisited = new Long2ByteOpenHashMap();
+ protected Long2ByteMap flowCostVisited = new Long2ByteOpenHashMap();
protected BlockLiquid(int meta) {
super(meta);
@@ -39,10 +35,12 @@ public boolean canBeFlowedInto() {
return true;
}
+ @Override
protected AxisAlignedBB recalculateBoundingBox() {
return null;
}
+ @Override
public Item[] getDrops(Item item) {
return new Item[0];
}
@@ -77,6 +75,11 @@ public AxisAlignedBB getBoundingBox() {
return null;
}
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
@Override
public double getMaxY() {
return this.y + 1 - getFluidHeightPercent();
@@ -98,14 +101,22 @@ public float getFluidHeightPercent() {
protected int getFlowDecay(Block block) {
if (block.getId() != this.getId()) {
- return -1;
+ Block layer1 = block.getLevelBlock(BlockLayer.WATERLOGGED);
+ if (layer1.getId() != this.getId()) {
+ return -1;
+ } else {
+ return layer1.getDamage();
+ }
}
return block.getDamage();
}
protected int getEffectiveFlowDecay(Block block) {
if (block.getId() != this.getId()) {
- return -1;
+ block = block.getLevelBlock(BlockLayer.WATERLOGGED);
+ if (block.getId() != this.getId()) {
+ return -1;
+ }
}
int decay = block.getDamage();
if (decay >= 8) {
@@ -142,13 +153,14 @@ public Vector3 getFlowVector() {
default:
z++;
}
- Block sideBlock = this.level.getBlock(x, y, z);
+ FullChunk chunk = this.level.getChunk(x >> 4, z >> 4);
+ Block sideBlock = this.level.getBlock(chunk, x, y, z, true);
int blockDecay = this.getEffectiveFlowDecay(sideBlock);
if (blockDecay < 0) {
if (!sideBlock.canBeFlowedInto()) {
continue;
}
- blockDecay = this.getEffectiveFlowDecay(this.level.getBlock(x, y - 1, z));
+ blockDecay = this.getEffectiveFlowDecay(this.level.getBlock(chunk, x, y - 1, z, true));
if (blockDecay >= 0) {
int realDecay = blockDecay - (decay - 8);
vector.x += (sideBlock.x - this.x) * realDecay;
@@ -163,14 +175,15 @@ public Vector3 getFlowVector() {
}
}
if (this.getDamage() >= 8) {
- if (!this.canFlowInto(this.level.getBlock((int) this.x, (int) this.y, (int) this.z - 1)) ||
- !this.canFlowInto(this.level.getBlock((int) this.x, (int) this.y, (int) this.z + 1)) ||
- !this.canFlowInto(this.level.getBlock((int) this.x - 1, (int) this.y, (int) this.z)) ||
- !this.canFlowInto(this.level.getBlock((int) this.x + 1, (int) this.y, (int) this.z)) ||
- !this.canFlowInto(this.level.getBlock((int) this.x, (int) this.y + 1, (int) this.z - 1)) ||
- !this.canFlowInto(this.level.getBlock((int) this.x, (int) this.y + 1, (int) this.z + 1)) ||
- !this.canFlowInto(this.level.getBlock((int) this.x - 1, (int) this.y + 1, (int) this.z)) ||
- !this.canFlowInto(this.level.getBlock((int) this.x + 1, (int) this.y + 1, (int) this.z))) {
+ FullChunk guessChunk = getChunk();
+ if (!this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x, (int) this.y, (int) this.z - 1, true)) ||
+ !this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x, (int) this.y, (int) this.z + 1, true)) ||
+ !this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x - 1, (int) this.y, (int) this.z, true)) ||
+ !this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x + 1, (int) this.y, (int) this.z, true)) ||
+ !this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x, (int) this.y + 1, (int) this.z - 1, true)) ||
+ !this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x, (int) this.y + 1, (int) this.z + 1, true)) ||
+ !this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x - 1, (int) this.y + 1, (int) this.z, true)) ||
+ !this.canFlowInto(this.level.getBlock(guessChunk, (int) this.x + 1, (int) this.y + 1, (int) this.z, true))) {
vector = vector.normalize().add(0, -6, 0);
}
}
@@ -195,32 +208,47 @@ public int getFlowDecayPerBlock() {
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
this.checkForHarden();
+ if (this.usesWaterLogging() && this.getLayer().ordinal() > LAYER_NORMAL.ordinal()) {
+ Block layer0 = this.level.getBlock(this, LAYER_NORMAL, true);
+ if (layer0.getId() == Block.AIR) {
+ this.level.setBlock(this, LAYER_WATERLOGGED, Block.get(Block.AIR), false, false);
+ this.level.setBlock(this, LAYER_NORMAL, this, false, false);
+ } else if (layer0.getWaterloggingType() == WaterloggingType.NO_WATERLOGGING || ((layer0.getWaterloggingType() == WaterloggingType.WHEN_PLACED_IN_WATER) && (this.getDamage() > 0))) {
+ this.level.setBlock(this, LAYER_WATERLOGGED, Block.get(Block.AIR), true, true);
+ }
+ }
this.level.scheduleUpdate(this, this.tickRate());
return 0;
} else if (type == Level.BLOCK_UPDATE_SCHEDULED) {
int decay = this.getFlowDecay(this);
int multiplier = this.getFlowDecayPerBlock();
+ FullChunk guessChunk = getChunk();
if (decay > 0) {
int smallestFlowDecay = -100;
this.adjacentSources = 0;
- smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock((int) this.x, (int) this.y, (int) this.z - 1), smallestFlowDecay);
- smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock((int) this.x, (int) this.y, (int) this.z + 1), smallestFlowDecay);
- smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock((int) this.x - 1, (int) this.y, (int) this.z), smallestFlowDecay);
- smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock((int) this.x + 1, (int) this.y, (int) this.z), smallestFlowDecay);
+ smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock(guessChunk, (int) this.x, (int) this.y, (int) this.z - 1, true), smallestFlowDecay);
+ smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock(guessChunk, (int) this.x, (int) this.y, (int) this.z + 1, true), smallestFlowDecay);
+ smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock(guessChunk, (int) this.x - 1, (int) this.y, (int) this.z, true), smallestFlowDecay);
+ smallestFlowDecay = this.getSmallestFlowDecay(this.level.getBlock(guessChunk, (int) this.x + 1, (int) this.y, (int) this.z, true), smallestFlowDecay);
int newDecay = smallestFlowDecay + multiplier;
if (newDecay >= 8 || smallestFlowDecay < 0) {
newDecay = -1;
}
- int topFlowDecay = this.getFlowDecay(this.level.getBlock((int) this.x, (int) this.y + 1, (int) this.z));
+ int topFlowDecay = this.getFlowDecay(this.level.getBlock(guessChunk, (int) this.x, (int) this.y + 1, (int) this.z, true));
if (topFlowDecay >= 0) {
newDecay = topFlowDecay | 0x08;
}
if (this.adjacentSources >= 2 && this instanceof BlockWater) {
- Block bottomBlock = this.level.getBlock((int) this.x, (int) this.y - 1, (int) this.z);
+ Block bottomBlock = this.level.getBlock(guessChunk, (int) this.x, (int) this.y - 1, (int) this.z, true);
if (bottomBlock.isSolid()) {
newDecay = 0;
} else if (bottomBlock instanceof BlockWater && bottomBlock.getDamage() == 0) {
newDecay = 0;
+ } else {
+ bottomBlock = bottomBlock.getLevelBlock(BlockLayer.WATERLOGGED);
+ if (bottomBlock instanceof BlockWater && bottomBlock.getDamage() == 0) {
+ newDecay = 0;
+ }
}
}
if (newDecay != decay) {
@@ -235,7 +263,7 @@ public int onUpdate(int type) {
BlockFromToEvent event = new BlockFromToEvent(this, to);
level.getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
- this.level.setBlock(this, event.getTo(), true, true);
+ this.level.setBlock(this, this.getLayer(), event.getTo(), true, true);
if (!decayed) {
this.level.scheduleUpdate(this, this.tickRate());
}
@@ -243,9 +271,9 @@ public int onUpdate(int type) {
}
}
if (decay >= 0) {
- Block bottomBlock = this.level.getBlock((int) this.x, (int) this.y - 1, (int) this.z);
+ Block bottomBlock = this.level.getBlock(guessChunk, (int) this.x, (int) this.y - 1, (int) this.z, true);
this.flowIntoBlock(bottomBlock, decay | 0x08);
- if (decay == 0 || !bottomBlock.canBeFlowedInto()) {
+ if (decay == 0 || !(this.usesWaterLogging()? bottomBlock.canWaterloggingFlowInto(): bottomBlock.canBeFlowedInto())) {
int adjacentDecay;
if (decay >= 8) {
adjacentDecay = 1;
@@ -255,16 +283,16 @@ public int onUpdate(int type) {
if (adjacentDecay < 8) {
boolean[] flags = this.getOptimalFlowDirections();
if (flags[0]) {
- this.flowIntoBlock(this.level.getBlock((int) this.x - 1, (int) this.y, (int) this.z), adjacentDecay);
+ this.flowIntoBlock(this.level.getBlock(guessChunk, (int) this.x - 1, (int) this.y, (int) this.z, true), adjacentDecay);
}
if (flags[1]) {
- this.flowIntoBlock(this.level.getBlock((int) this.x + 1, (int) this.y, (int) this.z), adjacentDecay);
+ this.flowIntoBlock(this.level.getBlock(guessChunk, (int) this.x + 1, (int) this.y, (int) this.z, true), adjacentDecay);
}
if (flags[2]) {
- this.flowIntoBlock(this.level.getBlock((int) this.x, (int) this.y, (int) this.z - 1), adjacentDecay);
+ this.flowIntoBlock(this.level.getBlock(guessChunk, (int) this.x, (int) this.y, (int) this.z - 1, true), adjacentDecay);
}
if (flags[3]) {
- this.flowIntoBlock(this.level.getBlock((int) this.x, (int) this.y, (int) this.z + 1), adjacentDecay);
+ this.flowIntoBlock(this.level.getBlock(guessChunk, (int) this.x, (int) this.y, (int) this.z + 1, true), adjacentDecay);
}
}
}
@@ -275,27 +303,37 @@ public int onUpdate(int type) {
}
protected void flowIntoBlock(Block block, int newFlowDecay) {
- if (this.canFlowInto(block) && !(block instanceof BlockLiquid)) {
+ if (!(block instanceof BlockLiquid) && this.canFlowInto(block)) {
+ if (this.usesWaterLogging()) {
+ Block waterlogged = block.getLevelBlock(LAYER_WATERLOGGED);
+ if (waterlogged instanceof BlockLiquid) {
+ return;
+ }
+
+ if (block.getWaterloggingType() == WaterloggingType.FLOW_INTO_BLOCK) {
+ block = waterlogged;
+ }
+ }
+
LiquidFlowEvent event = new LiquidFlowEvent(block, this, newFlowDecay);
level.getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
- if (block.getId() > 0) {
+ if (block.getLayer() == BlockLayer.NORMAL && block.getId() != 0) {
this.level.useBreakOn(block, block.getId() == COBWEB ? Item.get(Item.WOODEN_SWORD) : null);
}
- this.level.setBlock(block, getBlock(newFlowDecay), true, true);
+ this.level.setBlock(block, block.getLayer(), getBlock(newFlowDecay), true, true);
this.level.scheduleUpdate(block, this.tickRate());
}
}
}
- private int calculateFlowCost(int blockX, int blockY, int blockZ, int accumulatedCost, int maxCost, int originOpposite, int lastOpposite) {
+ protected int calculateFlowCost(int blockX, int blockY, int blockZ, int accumulatedCost, int maxCost, int originOpposite, int lastOpposite) {
int cost = 1000;
for (int j = 0; j < 4; ++j) {
if (j == originOpposite || j == lastOpposite) {
continue;
}
int x = blockX;
- int y = blockY;
int z = blockZ;
if (j == 0) {
--x;
@@ -306,18 +344,26 @@ private int calculateFlowCost(int blockX, int blockY, int blockZ, int accumulate
} else if (j == 3) {
++z;
}
- long hash = Level.blockHash(x, y, z);
- if (!this.flowCostVisited.containsKey(hash)) {
- Block blockSide = this.level.getBlock(x, y, z);
+ long hash = Level.blockHash(x, blockY, z, this.level.getDimensionData());
+ byte status;
+ if (this.flowCostVisited.containsKey(hash)) {
+ status = this.flowCostVisited.get(hash);
+ } else {
+ FullChunk chunk = this.level.getChunk(x >> 4, z >> 4);
+ Block blockSide = this.level.getBlock(chunk, x, blockY, z, true);
if (!this.canFlowInto(blockSide)) {
this.flowCostVisited.put(hash, BLOCKED);
- } else if (this.level.getBlock(x, y - 1, z).canBeFlowedInto()) {
+ status = BLOCKED;
+ } else if (usesWaterLogging()?
+ this.level.getBlock(x, blockY - 1, z).canWaterloggingFlowInto() :
+ this.level.getBlock(chunk, x, blockY - 1, z, true).canBeFlowedInto()) {
this.flowCostVisited.put(hash, CAN_FLOW_DOWN);
+ status = CAN_FLOW_DOWN;
} else {
this.flowCostVisited.put(hash, CAN_FLOW);
+ status = CAN_FLOW;
}
}
- byte status = this.flowCostVisited.get(hash);
if (status == BLOCKED) {
continue;
} else if (status == CAN_FLOW_DOWN) {
@@ -326,7 +372,7 @@ private int calculateFlowCost(int blockX, int blockY, int blockZ, int accumulate
if (accumulatedCost >= maxCost) {
continue;
}
- int realCost = this.calculateFlowCost(x, y, z, accumulatedCost + 1, maxCost, originOpposite, j ^ 0x01);
+ int realCost = this.calculateFlowCost(x, blockY, z, accumulatedCost + 1, maxCost, originOpposite, j ^ 0x01);
if (realCost < cost) {
cost = realCost;
}
@@ -344,8 +390,8 @@ public double getResistance() {
return 500;
}
- private boolean[] getOptimalFlowDirections() {
- int[] flowCost = new int[]{
+ protected boolean[] getOptimalFlowDirections() {
+ int[] flowCost = {
1000,
1000,
1000,
@@ -365,14 +411,17 @@ private boolean[] getOptimalFlowDirections() {
} else {
++z;
}
- Block block = this.level.getBlock(x, y, z);
+ FullChunk chunk = this.level.getChunk(x >> 4, z >> 4);
+ Block block = this.level.getBlock(chunk, x, y, z, true);
if (!this.canFlowInto(block)) {
- this.flowCostVisited.put(Level.blockHash(x, y, z), BLOCKED);
- } else if (this.level.getBlock(x, y - 1, z).canBeFlowedInto()) {
- this.flowCostVisited.put(Level.blockHash(x, y, z), CAN_FLOW_DOWN);
+ this.flowCostVisited.put(Level.blockHash(x, y, z, this.level.getDimensionData()), BLOCKED);
+ } else if (usesWaterLogging()?
+ this.level.getBlock(x, y - 1, z).canWaterloggingFlowInto():
+ this.level.getBlock(chunk, x, y - 1, z, true).canBeFlowedInto()) {
+ this.flowCostVisited.put(Level.blockHash(x, y, z, this.level.getDimensionData()), CAN_FLOW_DOWN);
flowCost[j] = maxCost = 0;
} else if (maxCost > 0) {
- this.flowCostVisited.put(Level.blockHash(x, y, z), CAN_FLOW);
+ this.flowCostVisited.put(Level.blockHash(x, y, z, this.level.getDimensionData()), CAN_FLOW);
flowCost[j] = this.calculateFlowCost(x, y, z, 1, maxCost, j ^ 0x01, j ^ 0x01);
maxCost = Math.min(maxCost, flowCost[j]);
}
@@ -407,15 +456,6 @@ private int getSmallestFlowDecay(Block block, int decay) {
protected void checkForHarden() {
}
- protected void triggerLavaMixEffects(Vector3 pos) {
- Random random = ThreadLocalRandom.current();
- this.getLevel().addLevelEvent(pos.add(0.5, 0.5, 0.5), LevelEventPacket.EVENT_SOUND_FIZZ, (int) ((random.nextFloat() - random.nextFloat()) * 800) + 2600);
-
- for (int i = 0; i < 8; ++i) {
- this.getLevel().addParticle(new SmokeParticle(pos.add(Math.random(), 1.2, Math.random())));
- }
- }
-
public abstract BlockLiquid getBlock(int meta);
@Override
@@ -435,11 +475,18 @@ protected boolean liquidCollide(Block cause, Block result) {
return false;
}
this.level.setBlock(this, event.getTo(), true, true);
+ this.level.setBlock(this, Block.LAYER_WATERLOGGED, Block.get(Block.AIR), true, true);
this.getLevel().addLevelSoundEvent(this.add(0.5, 0.5, 0.5), LevelSoundEventPacket.SOUND_FIZZ);
return true;
}
protected boolean canFlowInto(Block block) {
+ if (this.usesWaterLogging()) {
+ if (block.canWaterloggingFlowInto()) {
+ Block blockLayer1 = block.getLevelBlock(BlockLayer.WATERLOGGED);
+ return !(block instanceof BlockLiquid && block.getDamage() == 0) && !(blockLayer1 instanceof BlockLiquid && blockLayer1.getDamage() == 0);
+ }
+ }
return block.canBeFlowedInto() && !(block instanceof BlockLiquid && block.getDamage() == 0);
}
@@ -447,4 +494,8 @@ protected boolean canFlowInto(Block block) {
public Item toItem() {
return new ItemBlock(Block.get(BlockID.AIR));
}
-}
+
+ public boolean usesWaterLogging() {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockLodestone.java b/src/main/java/cn/nukkit/block/BlockLodestone.java
new file mode 100644
index 00000000000..cd54861b331
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLodestone.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockLodestone extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Lodestone";
+ }
+
+ @Override
+ public int getId() {
+ return LODESTONE;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3.5;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockLoom.java b/src/main/java/cn/nukkit/block/BlockLoom.java
new file mode 100644
index 00000000000..6e186e14b9f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLoom.java
@@ -0,0 +1,76 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.inventory.LoomInventory;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockLoom extends BlockSolidMeta {
+
+ public BlockLoom() {
+ this(0);
+ }
+
+ public BlockLoom(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Loom";
+ }
+
+ @Override
+ public int getId() {
+ return LOOM;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public double getResistance() {
+ return 12.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2.5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player != null) {
+ player.addWindow(new LoomInventory(player.getUIInventory(), this), Player.LOOM_WINDOW_ID);
+ }
+ return true;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ private static final short[] faces = {2, 3, 0, 1};
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMagma.java b/src/main/java/cn/nukkit/block/BlockMagma.java
index 9ba436318ea..286b61f07a7 100644
--- a/src/main/java/cn/nukkit/block/BlockMagma.java
+++ b/src/main/java/cn/nukkit/block/BlockMagma.java
@@ -1,21 +1,22 @@
package cn.nukkit.block;
import cn.nukkit.Player;
+import cn.nukkit.Server;
import cn.nukkit.entity.Entity;
+import cn.nukkit.event.block.BlockFormEvent;
import cn.nukkit.event.entity.EntityDamageByBlockEvent;
import cn.nukkit.event.entity.EntityDamageEvent;
+import cn.nukkit.inventory.PlayerInventory;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.level.GameRule;
+import cn.nukkit.level.Level;
import cn.nukkit.potion.Effect;
import cn.nukkit.utils.BlockColor;
public class BlockMagma extends BlockSolid {
- public BlockMagma(){
-
- }
-
@Override
public int getId() {
return MAGMA;
@@ -38,7 +39,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 30;
+ return 0.5;
}
@Override
@@ -48,7 +49,7 @@ public int getLightLevel() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -59,14 +60,18 @@ public Item[] getDrops(Item item) {
@Override
public void onEntityCollide(Entity entity) {
- if (!entity.hasEffect(Effect.FIRE_RESISTANCE)) {
+ if (entity.y >= this.y + 1 && !entity.hasEffect(Effect.FIRE_RESISTANCE)) {
if (entity instanceof Player) {
Player p = (Player) entity;
- if (!p.isCreative() && !p.isSpectator() && !p.isSneaking() && p.level.gameRules.getBoolean(GameRule.FIRE_DAMAGE)) {
- entity.attack(new EntityDamageByBlockEvent(this, entity, EntityDamageEvent.DamageCause.LAVA, 1));
+ PlayerInventory inv = p.getInventory();
+ if (inv == null || inv.getBootsFast().hasEnchantment(Enchantment.ID_FROST_WALKER) || !entity.level.gameRules.getBoolean(GameRule.FIRE_DAMAGE)) {
+ return;
+ }
+ if (!p.isCreative() && !p.isSpectator() && !p.isSneaking()) {
+ entity.attack(new EntityDamageByBlockEvent(this, entity, EntityDamageEvent.DamageCause.MAGMA, 1));
}
} else {
- entity.attack(new EntityDamageByBlockEvent(this, entity, EntityDamageEvent.DamageCause.LAVA, 1));
+ entity.attack(new EntityDamageByBlockEvent(this, entity, EntityDamageEvent.DamageCause.MAGMA, 1));
}
}
}
@@ -80,5 +85,26 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block up = this.up();
+ if (up instanceof BlockWater && (up.getDamage() == 0 || up.getDamage() == 8)) {
+ BlockFormEvent event = new BlockFormEvent(up, Block.get(BUBBLE_COLUMN, BlockBubbleColumn.DIRECTION_DOWN));
+ Server.getInstance().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(up, event.getNewState(), false, true);
+ }
+ }
+ }
+
+ return 0;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockMelon.java b/src/main/java/cn/nukkit/block/BlockMelon.java
index 6bad17f49ae..3e99161d1e1 100644
--- a/src/main/java/cn/nukkit/block/BlockMelon.java
+++ b/src/main/java/cn/nukkit/block/BlockMelon.java
@@ -1,12 +1,10 @@
package cn.nukkit.block;
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;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/12/11 by Pub4Game.
@@ -15,9 +13,6 @@
public class BlockMelon extends BlockSolid {
- public BlockMelon() {
- }
-
@Override
public int getId() {
return MELON_BLOCK;
@@ -38,16 +33,19 @@ public double getResistance() {
@Override
public Item[] getDrops(Item item) {
- Random random = new Random();
- int count = 3 + random.nextInt(5);
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
+ int count = 3 + Utils.random.nextInt(5);
Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING);
if (fortune != null && fortune.getLevel() >= 1) {
- count += random.nextInt(fortune.getLevel() + 1);
+ count += Utils.random.nextInt(fortune.getLevel() + 1);
}
return new Item[]{
- new ItemMelon(0, Math.min(9, count))
+ Item.get(Item.MELON, 0, Math.min(9, count))
};
}
@@ -60,9 +58,14 @@ public int getToolType() {
public BlockColor getColor() {
return BlockColor.LIME_BLOCK_COLOR;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockMeta.java b/src/main/java/cn/nukkit/block/BlockMeta.java
index 6ffb2087e38..7fc627bcc51 100644
--- a/src/main/java/cn/nukkit/block/BlockMeta.java
+++ b/src/main/java/cn/nukkit/block/BlockMeta.java
@@ -1,6 +1,7 @@
package cn.nukkit.block;
public abstract class BlockMeta extends Block {
+
private int meta;
protected BlockMeta(int meta) {
@@ -9,7 +10,7 @@ protected BlockMeta(int meta) {
@Override
public int getFullId() {
- return (getId() << 4) + getDamage();
+ return (getId() << DATA_BITS) + getDamage();
}
@Override
@@ -21,5 +22,4 @@ public final int getDamage() {
public void setDamage(int meta) {
this.meta = meta;
}
-
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMobSpawner.java b/src/main/java/cn/nukkit/block/BlockMobSpawner.java
index 766b662146b..dca8ea8089e 100644
--- a/src/main/java/cn/nukkit/block/BlockMobSpawner.java
+++ b/src/main/java/cn/nukkit/block/BlockMobSpawner.java
@@ -1,16 +1,17 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 27.12.2015.
*/
public class BlockMobSpawner extends BlockSolid {
- public BlockMobSpawner() {
- }
-
@Override
public String getName() {
return "Monster Spawner";
@@ -37,8 +38,13 @@ public double getResistance() {
}
@Override
- public Item[] getDrops(Item item) {
- return new Item[0];
+ 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)) {
+ BlockEntity.createBlockEntity(BlockEntity.MOB_SPAWNER, this.getChunk(), BlockEntity.getDefaultCompound(this, BlockEntity.MOB_SPAWNER));
+
+ return true;
+ }
+ return false;
}
@Override
@@ -51,4 +57,28 @@ public boolean canHarvestWithHand() {
return false;
}
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 3;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public int getDropExp() {
+ return Utils.rand(15, 43);
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockMonsterEgg.java b/src/main/java/cn/nukkit/block/BlockMonsterEgg.java
index 129c35d09c2..2b3e4238e7b 100644
--- a/src/main/java/cn/nukkit/block/BlockMonsterEgg.java
+++ b/src/main/java/cn/nukkit/block/BlockMonsterEgg.java
@@ -3,6 +3,7 @@
import cn.nukkit.item.Item;
public class BlockMonsterEgg extends BlockSolidMeta {
+
public static final int STONE = 0;
public static final int COBBLESTONE = 1;
public static final int STONE_BRICK = 2;
@@ -10,7 +11,7 @@ public class BlockMonsterEgg extends BlockSolidMeta {
public static final int CRACKED_BRICK = 4;
public static final int CHISELED_BRICK = 5;
- private static final String[] NAMES = new String[]{
+ private static final String[] NAMES = {
"Stone",
"Cobblestone",
"Stone Brick",
@@ -39,7 +40,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 3.75;
+ return 0.75;
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockMoss.java b/src/main/java/cn/nukkit/block/BlockMoss.java
new file mode 100644
index 00000000000..12611a00028
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMoss.java
@@ -0,0 +1,112 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.utils.BlockColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockMoss extends BlockDirt {
+
+ public BlockMoss() {
+ }
+
+ public BlockMoss(int meta) {
+ super(0);
+ }
+
+ @Override
+ public int getId() {
+ return MOSS_BLOCK;
+ }
+
+ @Override
+ public String getName() {
+ return "Moss Block";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 2.5;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() != Item.DYE || item.getDamage() != ItemDye.BONE_MEAL || this.up().getId() != AIR) {
+ return false;
+ }
+
+ int random = ThreadLocalRandom.current().nextInt(13);
+ Block block;
+ Block block2 = null;
+ if (random < 5) {
+ block = Block.get(TALL_GRASS);
+ } else if (random < 8) {
+ block = Block.get(MOSS_CARPET);
+ } else if (random < 9) {
+ if (this.up(2).getId() != AIR) {
+ return false;
+ }
+
+ block = Block.get(DOUBLE_PLANT, BlockDoublePlant.TALL_GRASS);
+ block2 = Block.get(DOUBLE_PLANT, BlockDoublePlant.TALL_GRASS ^ BlockDoublePlant.TOP_HALF_BITMASK);
+ } else if (random < 11) {
+ block = Block.get(AZALEA);
+ } else {
+ block = Block.get(FLOWERING_AZALEA);
+ }
+
+ this.getLevel().setBlock(this.up(), block, false, true);
+ if (block2 != null) {
+ this.getLevel().setBlock(this.up(2), block2, false, true);
+ }
+
+ this.level.addParticle(new BoneMealParticle(this));
+
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GREEN_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public int getFullId() {
+ return this.getId() << Block.DATA_BITS;
+ }
+
+ @Override
+ public void setDamage(int meta) {
+ // Noop
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (this.canHarvestWithHand() || this.canHarvest(item)) {
+ return new Item[]{this.toItem()};
+ }
+ return new Item[0];
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMossCarpet.java b/src/main/java/cn/nukkit/block/BlockMossCarpet.java
new file mode 100644
index 00000000000..a482fc38db8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMossCarpet.java
@@ -0,0 +1,90 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.DyeColor;
+
+public class BlockMossCarpet extends BlockTransparent {
+
+ public BlockMossCarpet() {
+ }
+
+ @Override
+ public int getId() {
+ return MOSS_CARPET;
+ }
+
+ @Override
+ public String getName() {
+ return "Moss Carpet";
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return true;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.5;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return true;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return false;
+ }
+
+ @Override
+ public double getMaxY() {
+ return this.y + 0.0625;
+ }
+
+ @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.getId() != Item.AIR) {
+ this.getLevel().setBlock(block, this, true, true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!this.down().isSolid()) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return DyeColor.GREEN.getColor();
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMossStone.java b/src/main/java/cn/nukkit/block/BlockMossStone.java
index 9c4a6e9cf72..cf1a65533d3 100644
--- a/src/main/java/cn/nukkit/block/BlockMossStone.java
+++ b/src/main/java/cn/nukkit/block/BlockMossStone.java
@@ -9,9 +9,6 @@
*/
public class BlockMossStone extends BlockSolid {
- public BlockMossStone() {
- }
-
@Override
public String getName() {
return "Mossy Cobblestone";
@@ -39,7 +36,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockMud.java b/src/main/java/cn/nukkit/block/BlockMud.java
new file mode 100644
index 00000000000..8ce0ce5b773
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMud.java
@@ -0,0 +1,39 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockMud extends BlockSolid {
+
+ public BlockMud() {
+ }
+
+ @Override
+ public String getName() {
+ return "Mud";
+ }
+
+ @Override
+ public int getId() {
+ return MUD;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_SHOVEL;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMudBrick.java b/src/main/java/cn/nukkit/block/BlockMudBrick.java
new file mode 100644
index 00000000000..4ea3fb4322f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMudBrick.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockMudBrick extends BlockSolid {
+
+ public BlockMudBrick() {
+ }
+
+ @Override
+ public String getName() {
+ return "Mud Brick";
+ }
+
+ @Override
+ public int getId() {
+ return MUD_BRICKS;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMudBrickSlab.java b/src/main/java/cn/nukkit/block/BlockMudBrickSlab.java
new file mode 100644
index 00000000000..450db319183
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMudBrickSlab.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockMudBrickSlab extends BlockSlab {
+
+ public BlockMudBrickSlab() {
+ this(0);
+ }
+
+ public BlockMudBrickSlab(int meta) {
+ super(meta, MUD_BRICK_DOUBLE_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return MUD_BRICK_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Mud Brick Slab";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMudBrickStairs.java b/src/main/java/cn/nukkit/block/BlockMudBrickStairs.java
new file mode 100644
index 00000000000..99e5bee899a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMudBrickStairs.java
@@ -0,0 +1,32 @@
+package cn.nukkit.block;
+
+public class BlockMudBrickStairs extends BlockStairs {
+
+ public BlockMudBrickStairs() {
+ this(0);
+ }
+
+ public BlockMudBrickStairs(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Mud Brick Stair";
+ }
+
+ @Override
+ public int getId() {
+ return MUD_BRICK_STAIRS;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMudBrickWall.java b/src/main/java/cn/nukkit/block/BlockMudBrickWall.java
new file mode 100644
index 00000000000..1924a95f277
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMudBrickWall.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockMudBrickWall extends BlockWall {
+
+ public BlockMudBrickWall() {
+ this(0);
+ }
+
+ public BlockMudBrickWall(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Mud Brick Wall";
+ }
+
+ @Override
+ public int getId() {
+ return MUD_BRICK_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMushroom.java b/src/main/java/cn/nukkit/block/BlockMushroom.java
index 350229a9eef..f1f7e173c5b 100644
--- a/src/main/java/cn/nukkit/block/BlockMushroom.java
+++ b/src/main/java/cn/nukkit/block/BlockMushroom.java
@@ -1,10 +1,8 @@
package cn.nukkit.block;
import cn.nukkit.Player;
-import cn.nukkit.event.level.StructureGrowEvent;
import cn.nukkit.item.Item;
import cn.nukkit.level.Level;
-import cn.nukkit.level.ListChunkManager;
import cn.nukkit.level.generator.object.mushroom.BigMushroom;
import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.math.BlockFace;
@@ -38,6 +36,10 @@ 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 (block instanceof BlockWater) {
+ return false;
+ }
+
if (canStay()) {
getLevel().setBlock(block, this, true, true);
return true;
@@ -53,7 +55,7 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
if (item.getId() == Item.DYE && item.getDamage() == DyeColor.WHITE.getDyeData()) {
- if (player != null && (player.gamemode & 0x01) == 0) {
+ if (player != null && !player.isCreative()) {
item.count--;
}
@@ -72,16 +74,7 @@ public boolean grow() {
BigMushroom generator = new BigMushroom(getType());
- ListChunkManager chunkManager = new ListChunkManager(this.level);
- if (generator.generate(chunkManager, new NukkitRandom(), this)) {
- StructureGrowEvent ev = new StructureGrowEvent(this, chunkManager.getBlocks());
- this.level.getServer().getPluginManager().callEvent(ev);
- if (ev.isCancelled()) {
- return false;
- }
- for(Block block : ev.getBlockList()) {
- this.level.setBlockAt(block.getFloorX(), block.getFloorY(), block.getFloorZ(), block.getId(), block.getDamage());
- }
+ if (generator.generate(this.level, new NukkitRandom(), this)) {
return true;
} else {
this.level.setBlock(this, this, true, false);
@@ -91,7 +84,7 @@ public boolean grow() {
public boolean canStay() {
Block block = this.down();
- return block.getId() == MYCELIUM || block.getId() == PODZOL || (!block.isTransparent() && this.level.getFullLight(this) < 13);
+ return block.getId() == MYCELIUM || block.getId() == PODZOL || (!block.isTransparent() && this.level.getBlockLightAt((int) this.x, (int) this.y, (int) this.z) < 13); // TODO: sky/full light
}
@Override
@@ -105,4 +98,9 @@ public boolean canSilkTouch() {
}
protected abstract int getType();
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockMushroomBrown.java b/src/main/java/cn/nukkit/block/BlockMushroomBrown.java
index 5655ea13196..8ce4f00e33a 100644
--- a/src/main/java/cn/nukkit/block/BlockMushroomBrown.java
+++ b/src/main/java/cn/nukkit/block/BlockMushroomBrown.java
@@ -32,4 +32,4 @@ public int getLightLevel() {
protected int getType() {
return 0;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockMushroomRed.java b/src/main/java/cn/nukkit/block/BlockMushroomRed.java
index ce82f01a9c2..9f27cc06ca4 100644
--- a/src/main/java/cn/nukkit/block/BlockMushroomRed.java
+++ b/src/main/java/cn/nukkit/block/BlockMushroomRed.java
@@ -27,4 +27,4 @@ public int getId() {
protected int getType() {
return 1;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockMycelium.java b/src/main/java/cn/nukkit/block/BlockMycelium.java
index 358d6677130..150d3546951 100644
--- a/src/main/java/cn/nukkit/block/BlockMycelium.java
+++ b/src/main/java/cn/nukkit/block/BlockMycelium.java
@@ -1,23 +1,21 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
import cn.nukkit.Server;
import cn.nukkit.event.block.BlockSpreadEvent;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
-import cn.nukkit.math.NukkitRandom;
-import cn.nukkit.math.Vector3;
+import cn.nukkit.level.Sound;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 03.01.2016.
*/
public class BlockMycelium extends BlockSolid {
- public BlockMycelium() {
- }
-
@Override
public String getName() {
return "Mycelium";
@@ -53,15 +51,13 @@ public Item[] getDrops(Item item) {
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_RANDOM) {
- //TODO: light levels
- NukkitRandom random = new NukkitRandom();
- x = random.nextRange((int) x - 1, (int) x + 1);
- y = random.nextRange((int) y - 1, (int) y + 1);
- z = random.nextRange((int) z - 1, (int) z + 1);
- Block block = this.getLevel().getBlock(new Vector3(x, y, z));
+ int xx = Utils.rand((int) x - 1, (int) x + 1);
+ int yy = Utils.rand((int) y - 1, (int) y + 1);
+ int zz = Utils.rand((int) z - 1, (int) z + 1);
+ Block block = this.getLevel().getBlock(xx, yy, zz);
if (block.getId() == Block.DIRT && block.getDamage() == 0) {
- if (block.up().isTransparent()) {
- BlockSpreadEvent ev = new BlockSpreadEvent(block, this, Block.get(BlockID.MYCELIUM));
+ if (block.up() instanceof BlockAir) {
+ BlockSpreadEvent ev = new BlockSpreadEvent(block, this, Block.get(MYCELIUM));
Server.getInstance().getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
this.getLevel().setBlock(block, ev.getNewState());
@@ -76,9 +72,30 @@ public int onUpdate(int type) {
public BlockColor getColor() {
return BlockColor.PURPLE_BLOCK_COLOR;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
}
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.isShovel()) {
+ Block up = this.up();
+ if (up instanceof BlockAir || up instanceof BlockFlowable) {
+ item.useOn(this);
+ this.getLevel().setBlock(this, Block.get(GRASS_PATH));
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockNetherBrick.java b/src/main/java/cn/nukkit/block/BlockNetherBrick.java
index ff28430572c..06e3bb942c0 100644
--- a/src/main/java/cn/nukkit/block/BlockNetherBrick.java
+++ b/src/main/java/cn/nukkit/block/BlockNetherBrick.java
@@ -10,9 +10,6 @@
*/
public class BlockNetherBrick extends BlockSolid {
- public BlockNetherBrick() {
- }
-
@Override
public String getName() {
return "Nether Bricks";
@@ -40,7 +37,7 @@ public double getResistance() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockNetherPortal.java b/src/main/java/cn/nukkit/block/BlockNetherPortal.java
index b48c0ee8c7b..fbdf3af76c6 100644
--- a/src/main/java/cn/nukkit/block/BlockNetherPortal.java
+++ b/src/main/java/cn/nukkit/block/BlockNetherPortal.java
@@ -1,11 +1,16 @@
package cn.nukkit.block;
+import cn.nukkit.Server;
+import cn.nukkit.event.level.NetherPortalSpawnEvent;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
import cn.nukkit.level.Position;
+import cn.nukkit.level.format.FullChunk;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.BlockFace.Axis;
+import cn.nukkit.math.Vector3;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.Faceable;
@@ -34,16 +39,6 @@ public int getId() {
return NETHER_PORTAL;
}
- @Override
- public boolean canBeFlowedInto() {
- return false;
- }
-
- @Override
- public boolean canPassThrough() {
- return true;
- }
-
@Override
public boolean isBreakable(Item item) {
return false;
@@ -64,15 +59,18 @@ public Item toItem() {
return new ItemBlock(Block.get(BlockID.AIR));
}
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
@Override
public boolean onBreak(Item item) {
boolean result = super.onBreak(item);
for (BlockFace face : BlockFace.values()) {
Block b = this.getSide(face);
- if (b != null) {
- if (b instanceof BlockNetherPortal) {
- result &= b.onBreak(item);
- }
+ if (b instanceof BlockNetherPortal) {
+ result &= b.onBreak(item);
}
}
return result;
@@ -98,17 +96,234 @@ public boolean canHarvestWithHand() {
return false;
}
+ public static boolean trySpawnPortal(Level level, Vector3 pos) {
+ return trySpawnPortal(level, pos, false);
+ }
+
@Override
protected AxisAlignedBB recalculateBoundingBox() {
return this;
}
- public static void spawnPortal(Position pos) {
+ public static boolean trySpawnPortal(Level level, Vector3 pos, boolean force) {
+ PortalBuilder builder = new PortalBuilder(level, pos, Axis.X, force);
+
+ if (builder.isValid() && builder.portalBlockCount == 0) {
+ builder.placePortalBlocks();
+ return true;
+ } else {
+ builder = new PortalBuilder(level, pos, Axis.Z, force);
+
+ if (builder.isValid() && builder.portalBlockCount == 0) {
+ builder.placePortalBlocks();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public static class PortalBuilder {
+
+ private final Level level;
+ private final Axis axis;
+ private final BlockFace rightDir;
+ private final BlockFace leftDir;
+ private int portalBlockCount;
+ private Vector3 bottomLeft;
+ private int height;
+ private int width;
+
+ private final boolean force;
+
+ public PortalBuilder(Level level, Vector3 pos, Axis axis, boolean force) {
+ this.level = level;
+ this.axis = axis;
+ this.force = force;
+
+ if (axis == Axis.X) {
+ this.leftDir = BlockFace.EAST;
+ this.rightDir = BlockFace.WEST;
+ } else {
+ this.leftDir = BlockFace.NORTH;
+ this.rightDir = BlockFace.SOUTH;
+ }
+
+
+ for (Vector3 blockpos = pos; pos.getY() > blockpos.getY() - 21 && pos.getY() > level.getMinBlockY() && this.isEmptyBlock(getBlockId(pos.getSideVec(BlockFace.DOWN))); pos = pos.getSideVec(BlockFace.DOWN)) {
+ }
+
+ int i = this.getDistanceUntilEdge(pos, this.leftDir) - 1;
+
+ if (i >= 0) {
+ this.bottomLeft = pos.getSideVec(this.leftDir, i);
+ this.width = this.getDistanceUntilEdge(this.bottomLeft, this.rightDir);
+
+ if (this.width < 2 || this.width > 21) {
+ this.bottomLeft = null;
+ this.width = 0;
+ }
+ }
+
+ if (this.bottomLeft != null) {
+ this.height = this.calculatePortalHeight();
+ }
+ }
+
+ protected int getDistanceUntilEdge(Vector3 pos, BlockFace dir) {
+ int i;
+
+ for (i = 0; i < 22; ++i) {
+ Vector3 v = pos.getSideVec(dir, i);
+
+ if (!this.isEmptyBlock(getBlockId(v)) || getBlockId(v.getSideVec(BlockFace.DOWN)) != OBSIDIAN) {
+ break;
+ }
+ }
+
+ return getBlockId(pos.getSideVec(dir, i)) == OBSIDIAN ? i : 0;
+ }
+
+ public int getHeight() {
+ return this.height;
+ }
+
+ public int getWidth() {
+ return this.width;
+ }
+
+ protected int calculatePortalHeight() {
+
+ loop:
+ for (this.height = 0; this.height < 21; ++this.height) {
+ for (int i = 0; i < this.width; ++i) {
+ Vector3 blockpos = this.bottomLeft.getSideVec(this.rightDir, i).up(this.height);
+ int block = getBlockId(blockpos);
+
+ if (!this.isEmptyBlock(block)) {
+ break loop;
+ }
+
+ if (block == NETHER_PORTAL) {
+ ++this.portalBlockCount;
+ }
+
+ if (i == 0) {
+ block = getBlockId(blockpos.getSideVec(this.leftDir));
+
+ if (block != OBSIDIAN) {
+ break loop;
+ }
+ } else if (i == this.width - 1) {
+ block = getBlockId(blockpos.getSideVec(this.rightDir));
+
+ if (block != OBSIDIAN) {
+ break loop;
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < this.width; ++i) {
+ if (getBlockId(this.bottomLeft.getSideVec(this.rightDir, i).up(this.height)) != OBSIDIAN) {
+ this.height = 0;
+ break;
+ }
+ }
+
+ if (this.height <= 21 && this.height >= 3) {
+ return this.height;
+ } else {
+ this.bottomLeft = null;
+ this.width = 0;
+ this.height = 0;
+ return 0;
+ }
+ }
+
+ private int getBlockId(Vector3 pos) {
+ return this.level.getBlockIdAt(pos.getFloorX(), pos.getFloorY(), pos.getFloorZ());
+ }
+
+ protected boolean isEmptyBlock(int id) {
+ return force || id == AIR || id == FIRE || id == NETHER_PORTAL;
+ }
+
+ public boolean isValid() {
+ return this.bottomLeft != null && this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21;
+ }
+
+ public void placePortalBlocks() {
+ for (int i = 0; i < this.width; ++i) {
+ Vector3 blockpos = this.bottomLeft.getSideVec(this.rightDir, i);
+
+ for (int j = 0; j < this.height; ++j) {
+ this.level.setBlock(blockpos.up(j), Block.get(NETHER_PORTAL, this.axis == Axis.X ? 1 : this.axis == Axis.Z ? 2 : 0));
+ }
+ }
+ }
+ }
+
+ public static Position getSafePortal(Position portal) {
+ Level level = portal.getLevel();
+ FullChunk chunk = portal.getChunk();
+ Vector3 down = portal.getSideVec(BlockFace.DOWN);
+
+ while (level.getBlockIdAt(chunk, down.getFloorX(), down.getFloorY(), down.getFloorZ()) == NETHER_PORTAL) {
+ down = down.getSideVec(BlockFace.DOWN);
+ }
+
+ return Position.fromObject(down.up(), portal.getLevel());
+ }
+
+ public static Position findNearestPortal(Position pos) {
+ Level level = pos.getLevel();
+ Position found = null;
+ int maxY = level.getMaxBlockY();
+
+ for (int xx = -16; xx <= 16; xx++) {
+ for (int zz = -16; zz <= 16; zz++) {
+ for (int y = 0; y < maxY; y++) {
+ int x = pos.getFloorX() + xx, z = pos.getFloorZ() + zz;
+ if (level.getBlockIdAt(x, y, z) == NETHER_PORTAL) {
+ found = new Position(x, y, z, level);
+ break;
+ }
+ }
+ }
+ }
+
+ if (found == null) {
+ return null;
+ }
+ Vector3 up = found.up();
+ int x = up.getFloorX(), y = up.getFloorY(), z = up.getFloorZ();
+ int id = level.getBlockIdAt(x, y, z);
+ if (id != AIR && id != OBSIDIAN && id != NETHER_PORTAL) {
+ for (int xx = -1; xx < 4; xx++) {
+ for (int yy = 1; yy < 4; yy++) {
+ for (int zz = -1; zz < 3; zz++) {
+ level.setBlockAt(x + xx, y + yy, z + zz, AIR);
+ }
+ }
+ }
+ }
+ return found;
+ }
+
+ public static void spawnPortal(Position pos) {
+ NetherPortalSpawnEvent ev = new NetherPortalSpawnEvent(pos);
+ Server.getInstance().getPluginManager().callEvent(ev);
+
+ if (ev.isCancelled()) {
+ return;
+ }
+
Level lvl = pos.level;
int x = pos.getFloorX(), y = pos.getFloorY(), z = pos.getFloorZ();
for (int xx = -1; xx < 4; xx++) {
- for (int yy = 1; yy < 4; yy++) {
+ for (int yy = 1; yy < 4; yy++) {
for (int zz = -1; zz < 3; zz++) {
lvl.setBlockAt(x + xx, y + yy, z + zz, AIR);
}
@@ -156,6 +371,6 @@ public static void spawnPortal(Position pos) {
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockNetherReactor.java b/src/main/java/cn/nukkit/block/BlockNetherReactor.java
new file mode 100644
index 00000000000..30bb6702f01
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockNetherReactor.java
@@ -0,0 +1,44 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+
+public class BlockNetherReactor extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return NETHER_REACTOR;
+ }
+
+ @Override
+ public String getName() {
+ return "Nether Reactor Core";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 15;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{Item.get(Item.DIAMOND, 0, 3), Item.get(Item.IRON_INGOT, 0, 6)};
+ } else return new Item[0];
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockNetherSprouts.java b/src/main/java/cn/nukkit/block/BlockNetherSprouts.java
new file mode 100644
index 00000000000..b695f1cd9db
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockNetherSprouts.java
@@ -0,0 +1,54 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockNetherSprouts extends BlockRoots {
+
+ public BlockNetherSprouts() {
+ this(0);
+ }
+
+ public BlockNetherSprouts(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Nether Sprouts";
+ }
+
+ @Override
+ public int getId() {
+ return NETHER_SPROUTS_BLOCK;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_SHEARS;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.NETHER_SPROUTS);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CYAN_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isShears()) {
+ return new Item[]{ toItem() };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public boolean canBeReplaced() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockNetherWart.java b/src/main/java/cn/nukkit/block/BlockNetherWart.java
index f286e4f96b1..2cf4fa511f6 100644
--- a/src/main/java/cn/nukkit/block/BlockNetherWart.java
+++ b/src/main/java/cn/nukkit/block/BlockNetherWart.java
@@ -4,12 +4,10 @@
import cn.nukkit.Server;
import cn.nukkit.event.block.BlockGrowEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemNetherWart;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
-
-import java.util.Random;
+import cn.nukkit.utils.Utils;
/**
* Created by Leonidius20 on 22.03.17.
@@ -42,7 +40,7 @@ public int onUpdate(int type) {
return Level.BLOCK_UPDATE_NORMAL;
}
} else if (type == Level.BLOCK_UPDATE_RANDOM) {
- if (new Random().nextInt(10) == 1) {
+ if (Utils.random.nextInt(10) == 1) {
if (this.getDamage() < 0x03) {
BlockNetherWart block = (BlockNetherWart) this.clone();
block.setDamage(block.getDamage() + 1);
@@ -65,7 +63,7 @@ public int onUpdate(int type) {
@Override
public BlockColor getColor() {
- return BlockColor.RED_BLOCK_COLOR;
+ return BlockColor.FOLIAGE_BLOCK_COLOR;
}
@Override
@@ -82,19 +80,22 @@ public int getId() {
public Item[] getDrops(Item item) {
if (this.getDamage() == 0x03) {
return new Item[]{
- new ItemNetherWart(0, 2 + (int) (Math.random() * ((4 - 2) + 1)))
+ Item.get(Item.NETHER_WART, 0, 2 + (int) (Math.random() * (3)))
};
} else {
return new Item[]{
- new ItemNetherWart()
+ Item.get(Item.NETHER_WART)
};
}
}
@Override
public Item toItem() {
- return new ItemNetherWart();
+ return Item.get(Item.NETHER_WART);
}
-}
-
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockNetherWartBlock.java b/src/main/java/cn/nukkit/block/BlockNetherWartBlock.java
index 0edaf1cb54b..d20ed238ea0 100644
--- a/src/main/java/cn/nukkit/block/BlockNetherWartBlock.java
+++ b/src/main/java/cn/nukkit/block/BlockNetherWartBlock.java
@@ -1,13 +1,11 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
public class BlockNetherWartBlock extends BlockSolid {
- public BlockNetherWartBlock() {
- }
-
@Override
public String getName() {
return "Nether Wart Block";
@@ -20,7 +18,7 @@ public int getId() {
@Override
public double getResistance() {
- return 5;
+ return 1;
}
@Override
@@ -39,4 +37,9 @@ public Item[] getDrops(Item item) {
public BlockColor getColor() {
return BlockColor.RED_BLOCK_COLOR;
}
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockNetheriteBlock.java b/src/main/java/cn/nukkit/block/BlockNetheriteBlock.java
new file mode 100644
index 00000000000..7c68f12c7c8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockNetheriteBlock.java
@@ -0,0 +1,45 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockNetheriteBlock extends BlockSolid {
+
+ public BlockNetheriteBlock() {
+ }
+
+ @Override
+ public int getId() {
+ return NETHERITE_BLOCK;
+ }
+
+ @Override
+ public String getName() {
+ return "Netherite Block";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 35;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6000;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockNetherrack.java b/src/main/java/cn/nukkit/block/BlockNetherrack.java
index 8417aaa474e..2043e7dd363 100644
--- a/src/main/java/cn/nukkit/block/BlockNetherrack.java
+++ b/src/main/java/cn/nukkit/block/BlockNetherrack.java
@@ -10,9 +10,6 @@
*/
public class BlockNetherrack extends BlockSolid {
- public BlockNetherrack() {
- }
-
@Override
public int getId() {
return NETHERRACK;
@@ -40,7 +37,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -58,5 +55,4 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockNoteblock.java b/src/main/java/cn/nukkit/block/BlockNoteblock.java
index 66c304992c6..e0649baeda9 100644
--- a/src/main/java/cn/nukkit/block/BlockNoteblock.java
+++ b/src/main/java/cn/nukkit/block/BlockNoteblock.java
@@ -18,10 +18,6 @@
*/
public class BlockNoteblock extends BlockSolid {
- public BlockNoteblock() {
-
- }
-
@Override
public String getName() {
return "Note Block";
@@ -55,7 +51,8 @@ public boolean canBeActivated() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
this.getLevel().setBlock(block, this, true);
- return this.createBlockEntity() != null;
+ BlockEntity.createBlockEntity(BlockEntity.MUSIC, this.getChunk(), BlockEntity.getDefaultCompound(this, BlockEntity.MUSIC));
+ return true;
}
public int getStrength() {
@@ -203,6 +200,7 @@ public Instrument getInstrument() {
case CONCRETE:
case STONECUTTER:
case OBSERVER:
+ case RESPAWN_ANCHOR:
return Instrument.BASS_DRUM;
default:
return Instrument.PIANO;
@@ -210,7 +208,7 @@ public Instrument getInstrument() {
}
public void emitSound() {
- if (this.up().getId() != AIR) return;
+ if (!this.isBlockAboveAir()) return;
Instrument instrument = this.getInstrument();
@@ -222,11 +220,12 @@ public void emitSound() {
pk.z = this.getFloorZ();
pk.case1 = instrument.ordinal();
pk.case2 = this.getStrength();
- this.getLevel().addChunkPacket(this.getFloorX() >> 4, this.getFloorZ() >> 4, pk);
+ this.getLevel().addChunkPacket(this.getChunkX(), this.getChunkZ(), pk);
}
@Override
public boolean onActivate(Item item, Player player) {
+ if (player.isSneaking()) return false;
this.increaseStrength();
this.emitSound();
return true;
@@ -258,11 +257,6 @@ private BlockEntityMusic getBlockEntity() {
return null;
}
- private BlockEntityMusic createBlockEntity() {
- return (BlockEntityMusic) BlockEntity.createBlockEntity(BlockEntity.MUSIC, this.getLevel().getChunk(this.getFloorX() >> 4, this.getFloorZ() >> 4),
- BlockEntity.getDefaultCompound(this, BlockEntity.MUSIC));
- }
-
public enum Instrument {
PIANO(Sound.NOTE_HARP),
BASS_DRUM(Sound.NOTE_BD),
@@ -296,4 +290,9 @@ public Sound getSound() {
public BlockColor getColor() {
return BlockColor.WOOD_BLOCK_COLOR;
}
-}
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockNylium.java b/src/main/java/cn/nukkit/block/BlockNylium.java
new file mode 100644
index 00000000000..f58cdd2bd38
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockNylium.java
@@ -0,0 +1,59 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
+
+public abstract class BlockNylium extends BlockSolid {
+
+ public BlockNylium() {
+ // Does nothing
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_RANDOM && !up().isTransparent()) {
+ level.setBlock(this, Block.get(NETHERRACK), false);
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.4;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.4;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ return new Item[]{ Item.get(NETHERRACK) };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockObserver.java b/src/main/java/cn/nukkit/block/BlockObserver.java
index f2d1855b4bc..3841dbf966e 100644
--- a/src/main/java/cn/nukkit/block/BlockObserver.java
+++ b/src/main/java/cn/nukkit/block/BlockObserver.java
@@ -1,16 +1,22 @@
package cn.nukkit.block;
import cn.nukkit.Player;
+import cn.nukkit.event.redstone.RedstoneUpdateEvent;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.Vector3;
import cn.nukkit.utils.Faceable;
-/**
- * Created by Leonidius20 on 18.08.18.
- */
public class BlockObserver extends BlockSolidMeta implements Faceable {
+ /**
+ * Where the block update happens. Used to check whether this observer should detect it.
+ */
+ private Vector3 updatePos;
+
public BlockObserver() {
this(0);
}
@@ -19,21 +25,53 @@ public BlockObserver(int meta) {
super(meta);
}
+ @Override
+ public int getId() {
+ return OBSERVER;
+ }
+
@Override
public String getName() {
return "Observer";
}
@Override
- public int getId() {
- return OBSERVER;
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 17.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{
+ Item.get(Item.OBSERVER, 0, 1)
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (player != null) {
- if (Math.abs(player.getFloorX() - this.x) <= 1 && Math.abs(player.getFloorZ() - this.z) <= 1) {
+ if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) {
double y = player.y + player.getEyeHeight();
+
if (y - this.y > 2) {
this.setDamage(BlockFace.DOWN.getIndex());
} else if (this.y - y > 0) {
@@ -44,36 +82,73 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
} else {
this.setDamage(player.getHorizontalFacing().getIndex());
}
- } else {
- this.setDamage(0);
}
- this.getLevel().setBlock(block, this, true, true);
- return true;
+
+ return this.getLevel().setBlock(this, this, true, true);
}
@Override
- public boolean canHarvestWithHand() {
- return false;
+ public BlockFace getBlockFace() {
+ return BlockFace.fromIndex(this.getDamage() & 0x07);
}
@Override
- public int getToolType() {
- return ItemTool.TYPE_PICKAXE;
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL && this.getSideVec(this.getBlockFace()).equals(this.updatePos) && !this.isPowered()) {
+ RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this);
+ this.level.getServer().getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return 0;
+ }
+ this.setPowered(true);
+ this.level.setBlock(this, this, false, false);
+ this.level.updateAroundRedstone(this, this.getBlockFace());
+ level.scheduleUpdate(this, 4);
+ return Level.BLOCK_UPDATE_NORMAL;
+ } else if (type == Level.BLOCK_UPDATE_SCHEDULED && this.isPowered()) {
+ RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this);
+ this.level.getServer().getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return 0;
+ }
+ this.setPowered(false);
+ this.level.setBlock(this, this, false, false);
+ this.level.updateAroundRedstone(this, this.getBlockFace());
+ }
+ return type;
}
@Override
- public double getHardness() {
- return 3.5;
+ public Item toItem() {
+ return new ItemBlock(Block.get(Block.OBSERVER));
}
@Override
- public double getResistance() {
- return 17.5;
+ public boolean isPowerSource() {
+ return true;
}
@Override
- public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ public int getStrongPower(BlockFace side) {
+ return this.isPowered() && side == this.getBlockFace() ? 15 : 0;
+ }
+
+ @Override
+ public int getWeakPower(BlockFace face) {
+ return this.getStrongPower(face);
+ }
+
+ public boolean isPowered() {
+ return (this.getDamage() & 0x8) == 0x8;
}
+ public void setPowered(boolean powered) {
+ this.setDamage((this.getDamage() & 0x7) | (powered ? 0x8 : 0x0));
+ }
+
+ @Override
+ public Block setUpdatePos(Vector3 pos) {
+ this.updatePos = pos;
+ return this;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockObsidian.java b/src/main/java/cn/nukkit/block/BlockObsidian.java
index 7ac74925ae3..cca7e15cff0 100644
--- a/src/main/java/cn/nukkit/block/BlockObsidian.java
+++ b/src/main/java/cn/nukkit/block/BlockObsidian.java
@@ -10,9 +10,6 @@
*/
public class BlockObsidian extends BlockSolid {
- public BlockObsidian() {
- }
-
@Override
public String getName() {
return "Obsidian";
@@ -30,7 +27,7 @@ public int getToolType() {
@Override
public double getHardness() {
- return 35; //50 in PC
+ return 35;
}
@Override
@@ -52,7 +49,7 @@ public Item[] getDrops(Item item) {
@Override
public boolean onBreak(Item item) {
//destroy the nether portal
- Block[] nearby = new Block[]{
+ Block[] nearby = {
this.up(), this.down(),
this.north(), south(),
this.west(), this.east(),
diff --git a/src/main/java/cn/nukkit/block/BlockObsidianCrying.java b/src/main/java/cn/nukkit/block/BlockObsidianCrying.java
new file mode 100644
index 00000000000..84fd9177d13
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockObsidianCrying.java
@@ -0,0 +1,63 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockObsidianCrying extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return CRYING_OBSIDIAN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public String getName() {
+ return "Crying Obsidian";
+ }
+
+ @Override
+ public double getHardness() {
+ return 35;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1200;
+ }
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_DIAMOND) {
+ return new Item[]{
+ toItem()
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 10;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.OBSIDIAN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java b/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java
index 8b57354b0e5..2797560fd03 100644
--- a/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java
+++ b/src/main/java/cn/nukkit/block/BlockObsidianGlowing.java
@@ -3,6 +3,7 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
/**
* Created on 2015/11/22 by xtypr.
@@ -10,47 +11,49 @@
*/
public class BlockObsidianGlowing extends BlockSolid {
- public BlockObsidianGlowing() {
- }
-
@Override
public int getId() {
return GLOWING_OBSIDIAN;
}
@Override
- public int getToolType() {
- return ItemTool.TYPE_PICKAXE;
+ public String getName() {
+ return "Glowing Obsidian";
}
@Override
- public String getName() {
- return "Glowing Obsidian";
+ public int getLightLevel() {
+ return 12;
}
@Override
- public double getHardness() {
- return 50;
+ public Item toItem() {
+ return new ItemBlock(Block.get(GLOWING_OBSIDIAN));
}
@Override
- public double getResistance() {
- return 6000;
+ public boolean onBreak(Item item) {
+ return this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, true);
}
@Override
- public int getLightLevel() {
- return 12;
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
}
@Override
- public Item toItem() {
- return new ItemBlock(Block.get(BlockID.OBSIDIAN));
+ public double getHardness() {
+ return 12; //?
+ }
+
+ @Override
+ public double getResistance() {
+ return 6000;
}
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() > ItemTool.DIAMOND_PICKAXE) {
+ if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_DIAMOND) {
return new Item[]{
toItem()
};
@@ -59,6 +62,11 @@ public Item[] getDrops(Item item) {
}
}
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.OBSIDIAN_BLOCK_COLOR;
+ }
+
@Override
public boolean canBePushed() {
return false;
diff --git a/src/main/java/cn/nukkit/block/BlockOre.java b/src/main/java/cn/nukkit/block/BlockOre.java
new file mode 100644
index 00000000000..ea25e4fc42e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOre.java
@@ -0,0 +1,82 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.math.NukkitMath;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public abstract class BlockOre extends BlockSolid {
+
+ public BlockOre() {
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (!this.canHarvest(item) || item.getTier() < this.getToolTier()) {
+ return new Item[0];
+ }
+
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
+ int rawMaterial = this.getRawMaterial();
+ if (rawMaterial == BlockID.AIR) {
+ return super.getDrops(item);
+ }
+
+ float multiplier = this.getDropMultiplier();
+ int amount = (int) multiplier;
+ if (amount > 1) {
+ amount = 1 + ThreadLocalRandom.current().nextInt(amount);
+ }
+ int fortuneLevel = NukkitMath.clamp(item.getEnchantmentLevel(Enchantment.ID_FORTUNE_DIGGING), 0, 3);
+ if (fortuneLevel > 0) {
+ int increase = ThreadLocalRandom.current().nextInt((int)(multiplier * fortuneLevel) + 1);
+ amount += increase;
+ }
+ return new Item[]{ Item.get(rawMaterial, this.getRawMaterialMeta(), amount) };
+ }
+
+ protected abstract int getRawMaterial();
+
+ protected int getRawMaterialMeta() {
+ return 0;
+ }
+
+ protected float getDropMultiplier() {
+ return 1;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_STONE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreCoal.java b/src/main/java/cn/nukkit/block/BlockOreCoal.java
index e7ea88dd02d..1f7216f1707 100644
--- a/src/main/java/cn/nukkit/block/BlockOreCoal.java
+++ b/src/main/java/cn/nukkit/block/BlockOreCoal.java
@@ -1,23 +1,16 @@
package cn.nukkit.block;
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 cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockOreCoal extends BlockSolid {
- public BlockOreCoal() {
- }
-
@Override
public int getId() {
return COAL_ORE;
@@ -30,7 +23,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 15;
+ return 3;
}
@Override
@@ -45,11 +38,15 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
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;
+ int i = Utils.random.nextInt(fortune.getLevel() + 2) - 1;
if (i < 0) {
i = 0;
@@ -59,7 +56,7 @@ public Item[] getDrops(Item item) {
}
return new Item[]{
- new ItemCoal(0, count)
+ Item.get(Item.COAL, 0, count)
};
} else {
return new Item[0];
@@ -68,21 +65,16 @@ public Item[] getDrops(Item item) {
@Override
public int getDropExp() {
- return new NukkitRandom().nextRange(0, 2);
+ return Utils.rand(0, 2);
}
@Override
public boolean canHarvestWithHand() {
return false;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
}
-
- @Override
- public BlockColor getColor() {
- return BlockColor.BLACK_BLOCK_COLOR;
- }
}
diff --git a/src/main/java/cn/nukkit/block/BlockOreCoalDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreCoalDeepslate.java
new file mode 100644
index 00000000000..0d71042547a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreCoalDeepslate.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemID;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
+
+public class BlockOreCoalDeepslate extends BlockOre {
+
+ public BlockOreCoalDeepslate() {
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.COAL;
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_COAL_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Coal Ore";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+
+ @Override
+ public int getDropExp() {
+ return Utils.rand(0, 2);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreCopper.java b/src/main/java/cn/nukkit/block/BlockOreCopper.java
new file mode 100644
index 00000000000..c538a855257
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreCopper.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemID;
+import cn.nukkit.utils.Utils;
+
+public class BlockOreCopper extends BlockOre {
+
+ public BlockOreCopper() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Copper Ore";
+ }
+
+ @Override
+ public int getId() {
+ return COPPER_ORE;
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.RAW_COPPER;
+ }
+
+ @Override
+ protected float getDropMultiplier() {
+ return 3;
+ }
+
+ @Override
+ public int getDropExp() {
+ return Utils.rand(0, 2);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreCopperDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreCopperDeepslate.java
new file mode 100644
index 00000000000..2da6a62e90a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreCopperDeepslate.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockOreCopperDeepslate extends BlockOreCopper {
+
+ public BlockOreCopperDeepslate() {
+ // Does nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Copper Ore";
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_COPPER_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreDiamond.java b/src/main/java/cn/nukkit/block/BlockOreDiamond.java
index d284ad98a6a..6d0d75d97cf 100644
--- a/src/main/java/cn/nukkit/block/BlockOreDiamond.java
+++ b/src/main/java/cn/nukkit/block/BlockOreDiamond.java
@@ -1,23 +1,16 @@
package cn.nukkit.block;
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;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockOreDiamond extends BlockSolid {
-
- public BlockOreDiamond() {
- }
-
@Override
public int getId() {
return DIAMOND_ORE;
@@ -30,7 +23,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 15;
+ return 3;
}
@Override
@@ -46,10 +39,14 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
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;
+ int i = Utils.random.nextInt(fortune.getLevel() + 2) - 1;
if (i < 0) {
i = 0;
@@ -59,7 +56,7 @@ public Item[] getDrops(Item item) {
}
return new Item[]{
- new ItemDiamond(0, count)
+ Item.get(Item.DIAMOND, 0, count)
};
} else {
return new Item[0];
@@ -68,14 +65,14 @@ public Item[] getDrops(Item item) {
@Override
public int getDropExp() {
- return new NukkitRandom().nextRange(3, 7);
+ return Utils.rand(3, 7);
}
@Override
public boolean canHarvestWithHand() {
return false;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
diff --git a/src/main/java/cn/nukkit/block/BlockOreDiamondDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreDiamondDeepslate.java
new file mode 100644
index 00000000000..c14504c25ab
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreDiamondDeepslate.java
@@ -0,0 +1,47 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
+
+public class BlockOreDiamondDeepslate extends BlockOre {
+
+ public BlockOreDiamondDeepslate() {
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.DIAMOND;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_IRON;
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_DIAMOND_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Diamond Ore";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+
+ @Override
+ public int getDropExp() {
+ return Utils.rand(3, 7);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreEmerald.java b/src/main/java/cn/nukkit/block/BlockOreEmerald.java
index 65c8d0e34f1..c9f1d3d3426 100644
--- a/src/main/java/cn/nukkit/block/BlockOreEmerald.java
+++ b/src/main/java/cn/nukkit/block/BlockOreEmerald.java
@@ -1,12 +1,9 @@
package cn.nukkit.block;
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;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/12/1 by xtypr.
@@ -14,9 +11,6 @@
*/
public class BlockOreEmerald extends BlockSolid {
- public BlockOreEmerald() {
- }
-
@Override
public String getName() {
return "Emerald Ore";
@@ -39,16 +33,20 @@ public double getHardness() {
@Override
public double getResistance() {
- return 15;
+ return 3;
}
@Override
public Item[] getDrops(Item item) {
if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
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;
+ int i = Utils.random.nextInt(fortune.getLevel() + 2) - 1;
if (i < 0) {
i = 0;
@@ -58,7 +56,7 @@ public Item[] getDrops(Item item) {
}
return new Item[]{
- new ItemEmerald(0, count)
+ Item.get(Item.EMERALD, 0, count)
};
} else {
return new Item[0];
@@ -67,14 +65,14 @@ public Item[] getDrops(Item item) {
@Override
public int getDropExp() {
- return new NukkitRandom().nextRange(3, 7);
+ return Utils.rand(3, 7);
}
@Override
public boolean canHarvestWithHand() {
return false;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
diff --git a/src/main/java/cn/nukkit/block/BlockOreEmeraldDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreEmeraldDeepslate.java
new file mode 100644
index 00000000000..aa374976d5f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreEmeraldDeepslate.java
@@ -0,0 +1,47 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
+
+public class BlockOreEmeraldDeepslate extends BlockOre {
+
+ public BlockOreEmeraldDeepslate() {
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.EMERALD;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_IRON;
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_EMERALD_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Emerald Ore";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+
+ @Override
+ public int getDropExp() {
+ return Utils.rand(3, 7);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreGold.java b/src/main/java/cn/nukkit/block/BlockOreGold.java
index fb80d03b712..e74bda7bc3c 100644
--- a/src/main/java/cn/nukkit/block/BlockOreGold.java
+++ b/src/main/java/cn/nukkit/block/BlockOreGold.java
@@ -1,16 +1,12 @@
package cn.nukkit.block;
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.ItemID;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
-public class BlockOreGold extends BlockSolid {
-
- public BlockOreGold() {
- }
+public class BlockOreGold extends BlockOre {
@Override
public int getId() {
@@ -18,38 +14,12 @@ public int getId() {
}
@Override
- public double getHardness() {
- return 3;
- }
-
- @Override
- public double getResistance() {
- return 15;
- }
-
- @Override
- public int getToolType() {
- return ItemTool.TYPE_PICKAXE;
+ protected int getRawMaterial() {
+ return ItemID.RAW_GOLD;
}
@Override
public String getName() {
return "Gold Ore";
}
-
- @Override
- public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) {
- return new Item[]{
- Item.get(GOLD_ORE)
- };
- } else {
- return new Item[0];
- }
- }
-
- @Override
- public boolean canHarvestWithHand() {
- return false;
- }
}
diff --git a/src/main/java/cn/nukkit/block/BlockOreGoldDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreGoldDeepslate.java
new file mode 100644
index 00000000000..ca85c573b5c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreGoldDeepslate.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockOreGoldDeepslate extends BlockOre {
+
+ public BlockOreGoldDeepslate() {
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.RAW_GOLD;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_IRON;
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_GOLD_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Gold Ore";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreGoldNether.java b/src/main/java/cn/nukkit/block/BlockOreGoldNether.java
new file mode 100644
index 00000000000..d6df72b9ed8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreGoldNether.java
@@ -0,0 +1,90 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.utils.BlockColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockOreGoldNether extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return NETHER_GOLD_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public String getName() {
+ return "Nether Gold Ore";
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (!item.isPickaxe()) {
+ return new Item[0];
+ }
+
+ Enchantment enchantment = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING);
+ int fortune = 0;
+ if (enchantment != null) {
+ fortune = enchantment.getLevel();
+ }
+
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ int count = random.nextInt(2, 7);
+ switch (fortune) {
+ case 0:
+ // Does nothing
+ break;
+ case 1:
+ if (random.nextInt(0, 2) == 0) {
+ count *= 2;
+ }
+ break;
+ case 2:
+ if (random.nextInt(0, 1) == 0) {
+ count *= random.nextInt(2, 3);
+ }
+ break;
+ default:
+ case 3:
+ if (random.nextInt(0, 4) < 3) {
+ count *= random.nextInt(2, 4);
+ }
+ break;
+ }
+
+ return new Item[]{ Item.get(Item.GOLD_NUGGET, 0, count) };
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreIron.java b/src/main/java/cn/nukkit/block/BlockOreIron.java
index 27fcc2c5284..93e783aec64 100644
--- a/src/main/java/cn/nukkit/block/BlockOreIron.java
+++ b/src/main/java/cn/nukkit/block/BlockOreIron.java
@@ -1,17 +1,12 @@
package cn.nukkit.block;
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.ItemID;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
-public class BlockOreIron extends BlockSolid {
-
-
- public BlockOreIron() {
- }
+public class BlockOreIron extends BlockOre {
@Override
public int getId() {
@@ -19,38 +14,12 @@ public int getId() {
}
@Override
- public double getHardness() {
- return 3;
- }
-
- @Override
- public double getResistance() {
- return 5;
- }
-
- @Override
- public int getToolType() {
- return ItemTool.TYPE_PICKAXE;
+ protected int getRawMaterial() {
+ return ItemID.RAW_IRON;
}
@Override
public String getName() {
return "Iron Ore";
}
-
- @Override
- public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_STONE) {
- return new Item[]{
- Item.get(IRON_ORE)
- };
- } else {
- return new Item[0];
- }
- }
-
- @Override
- public boolean canHarvestWithHand() {
- return false;
- }
}
diff --git a/src/main/java/cn/nukkit/block/BlockOreIronDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreIronDeepslate.java
new file mode 100644
index 00000000000..459b66be921
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreIronDeepslate.java
@@ -0,0 +1,35 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemID;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockOreIronDeepslate extends BlockOre {
+
+ public BlockOreIronDeepslate() {
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.RAW_IRON;
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_IRON_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Iron Ore";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreLapis.java b/src/main/java/cn/nukkit/block/BlockOreLapis.java
index 3e4207985a2..1883976ea20 100644
--- a/src/main/java/cn/nukkit/block/BlockOreLapis.java
+++ b/src/main/java/cn/nukkit/block/BlockOreLapis.java
@@ -1,24 +1,16 @@
package cn.nukkit.block;
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;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockOreLapis extends BlockSolid {
-
- public BlockOreLapis() {
- }
-
@Override
public int getId() {
return LAPIS_ORE;
@@ -31,7 +23,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 5;
+ return 3;
}
@Override
@@ -47,20 +39,24 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_STONE) {
- int count = 4 + ThreadLocalRandom.current().nextInt(5);
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
+ int count = 4 + Utils.random.nextInt(6);
Enchantment fortune = item.getEnchantment(Enchantment.ID_FORTUNE_DIGGING);
if (fortune != null && fortune.getLevel() >= 1) {
- int i = ThreadLocalRandom.current().nextInt(fortune.getLevel() + 2) - 1;
+ int i = Utils.random.nextInt(fortune.getLevel() + 2) - 1;
if (i < 0) {
i = 0;
}
- count *= (i + 1);
+ count = count + i;
}
return new Item[]{
- new ItemDye(4, new Random().nextInt(4) + 4)
+ Item.get(Item.DYE, 4, count)
};
} else {
return new Item[0];
@@ -69,14 +65,14 @@ public Item[] getDrops(Item item) {
@Override
public int getDropExp() {
- return new NukkitRandom().nextRange(2, 5);
+ return Utils.rand(2, 5);
}
@Override
public boolean canHarvestWithHand() {
return false;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
diff --git a/src/main/java/cn/nukkit/block/BlockOreLapisDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreLapisDeepslate.java
new file mode 100644
index 00000000000..8745d203e54
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreLapisDeepslate.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockOreLapisDeepslate extends BlockOre {
+
+ public BlockOreLapisDeepslate() {
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.DYE;
+ }
+
+ @Override
+ protected int getRawMaterialMeta() {
+ return ItemDye.LAPIS_LAZULI;
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_LAPIS_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Lapis Lazuli Ore";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreQuartz.java b/src/main/java/cn/nukkit/block/BlockOreQuartz.java
index 3b3556971ad..ba52256307c 100644
--- a/src/main/java/cn/nukkit/block/BlockOreQuartz.java
+++ b/src/main/java/cn/nukkit/block/BlockOreQuartz.java
@@ -1,12 +1,9 @@
package cn.nukkit.block;
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;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/12/26 by xtypr.
@@ -14,9 +11,6 @@
*/
public class BlockOreQuartz extends BlockSolid {
- public BlockOreQuartz() {
- }
-
@Override
public String getName() {
return "Quartz Ore";
@@ -34,7 +28,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 5;
+ return 3;
}
@Override
@@ -44,11 +38,15 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
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;
+ int i = Utils.random.nextInt(fortune.getLevel() + 2) - 1;
if (i < 0) {
i = 0;
@@ -58,7 +56,7 @@ public Item[] getDrops(Item item) {
}
return new Item[]{
- new ItemQuartz(0, count)
+ Item.get(Item.QUARTZ, 0, count)
};
} else {
return new Item[0];
@@ -67,7 +65,7 @@ public Item[] getDrops(Item item) {
@Override
public int getDropExp() {
- return new NukkitRandom().nextRange(1, 5);
+ return Utils.rand(1, 5);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockOreRedstone.java b/src/main/java/cn/nukkit/block/BlockOreRedstone.java
index 0ce78d76c5c..a96f0cc371c 100644
--- a/src/main/java/cn/nukkit/block/BlockOreRedstone.java
+++ b/src/main/java/cn/nukkit/block/BlockOreRedstone.java
@@ -1,23 +1,17 @@
package cn.nukkit.block;
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;
-
-import java.util.Random;
+import cn.nukkit.utils.Utils;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockOreRedstone extends BlockSolid {
- public BlockOreRedstone() {
- }
-
@Override
public int getId() {
return REDSTONE_ORE;
@@ -30,7 +24,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 15;
+ return 3;
}
@Override
@@ -46,15 +40,19 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_IRON) {
- int count = new Random().nextInt(2) + 4;
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
+
+ int count = Utils.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);
+ count += Utils.random.nextInt(fortune.getLevel() + 1);
}
return new Item[]{
- new ItemRedstone(0, count)
+ Item.get(Item.REDSTONE_DUST, 0, count)
};
} else {
return new Item[0];
@@ -63,8 +61,9 @@ public Item[] getDrops(Item item) {
@Override
public int onUpdate(int type) {
- if (type == Level.BLOCK_UPDATE_TOUCH) { //type == Level.BLOCK_UPDATE_NORMAL ||
- this.getLevel().setBlock(this, Block.get(BlockID.GLOWING_REDSTONE_ORE), false, false);
+ if (type == Level.BLOCK_UPDATE_TOUCH) {
+ this.getLevel().setBlock(this, Block.get(GLOWING_REDSTONE_ORE), false, false);
+ this.getLevel().scheduleUpdate(this, 600);
return Level.BLOCK_UPDATE_WEAK;
}
@@ -74,7 +73,7 @@ public int onUpdate(int type) {
@Override
public int getDropExp() {
- return new NukkitRandom().nextRange(1, 5);
+ return Utils.rand(1, 5);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockOreRedstoneDeepslate.java b/src/main/java/cn/nukkit/block/BlockOreRedstoneDeepslate.java
new file mode 100644
index 00000000000..d4b86824ed3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreRedstoneDeepslate.java
@@ -0,0 +1,53 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockOreRedstoneDeepslate extends BlockOre {
+
+ public BlockOreRedstoneDeepslate() {
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_TOUCH) {
+ this.getLevel().setBlock(this, Block.get(LIT_DEEPSLATE_REDSTONE_ORE), false, false);
+ this.getLevel().scheduleUpdate(this, 600);
+
+ return Level.BLOCK_UPDATE_WEAK;
+ }
+ return 0;
+ }
+
+ @Override
+ protected int getRawMaterial() {
+ return ItemID.REDSTONE_DUST;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_IRON;
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_REDSTONE_ORE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 4.5;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Redstone Ore";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreRedstoneDeepslateGlowing.java b/src/main/java/cn/nukkit/block/BlockOreRedstoneDeepslateGlowing.java
new file mode 100644
index 00000000000..2b55f735f6d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockOreRedstoneDeepslateGlowing.java
@@ -0,0 +1,47 @@
+package cn.nukkit.block;
+
+import cn.nukkit.event.block.BlockFadeEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
+
+public class BlockOreRedstoneDeepslateGlowing extends BlockOreRedstoneDeepslate {
+
+ public BlockOreRedstoneDeepslateGlowing() {
+ }
+
+ @Override
+ public int getId() {
+ return LIT_DEEPSLATE_REDSTONE_ORE;
+ }
+
+ @Override
+ public String getName() {
+ return "Glowing Deepslate Redstone Ore";
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 9;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(REDSTONE_ORE));
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED || type == Level.BLOCK_UPDATE_RANDOM) {
+ BlockFadeEvent event = new BlockFadeEvent(this, Block.get(DEEPSLATE_REDSTONE_ORE));
+ level.getServer().getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ level.setBlock(this, event.getNewState(), false, false);
+ }
+
+ return Level.BLOCK_UPDATE_WEAK;
+ }
+
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockOreRedstoneGlowing.java b/src/main/java/cn/nukkit/block/BlockOreRedstoneGlowing.java
index dd607c696d4..d737a298ba2 100644
--- a/src/main/java/cn/nukkit/block/BlockOreRedstoneGlowing.java
+++ b/src/main/java/cn/nukkit/block/BlockOreRedstoneGlowing.java
@@ -5,17 +5,12 @@
import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
-//和pm源码有点出入,这里参考了wiki
-
/**
* Created on 2015/12/6 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockOreRedstoneGlowing extends BlockOreRedstone {
- public BlockOreRedstoneGlowing() {
- }
-
@Override
public String getName() {
return "Glowing Redstone Ore";
@@ -33,7 +28,7 @@ public int getLightLevel() {
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.REDSTONE_ORE));
+ return new ItemBlock(Block.get(REDSTONE_ORE));
}
@Override
@@ -50,14 +45,4 @@ public int onUpdate(int type) {
return 0;
}
-
- @Override
- public boolean canHarvestWithHand() {
- return false;
- }
-
- @Override
- public boolean canSilkTouch() {
- return true;
- }
}
diff --git a/src/main/java/cn/nukkit/block/BlockPackedMud.java b/src/main/java/cn/nukkit/block/BlockPackedMud.java
new file mode 100644
index 00000000000..08233302453
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPackedMud.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockPackedMud extends BlockSolid {
+
+ public BlockPackedMud() {
+ }
+
+ @Override
+ public String getName() {
+ return "Packed Mud";
+ }
+
+ @Override
+ public int getId() {
+ return PACKED_MUD;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPiston.java b/src/main/java/cn/nukkit/block/BlockPiston.java
index 6bf3cff2f36..5c0c7dce6f8 100644
--- a/src/main/java/cn/nukkit/block/BlockPiston.java
+++ b/src/main/java/cn/nukkit/block/BlockPiston.java
@@ -18,6 +18,11 @@ public int getId() {
return PISTON;
}
+ @Override
+ public int getPistonHeadBlockId() {
+ return PISTON_HEAD;
+ }
+
@Override
public String getName() {
return "Piston";
diff --git a/src/main/java/cn/nukkit/block/BlockPistonBase.java b/src/main/java/cn/nukkit/block/BlockPistonBase.java
index 19c8a24b4ff..b9e2dd7048e 100644
--- a/src/main/java/cn/nukkit/block/BlockPistonBase.java
+++ b/src/main/java/cn/nukkit/block/BlockPistonBase.java
@@ -4,6 +4,8 @@
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityPistonArm;
import cn.nukkit.event.block.BlockPistonChangeEvent;
+import cn.nukkit.event.block.BlockPistonEvent;
+import cn.nukkit.event.redstone.RedstoneUpdateEvent;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
@@ -31,21 +33,22 @@ public BlockPistonBase(int meta) {
super(meta);
}
+ public abstract int getPistonHeadBlockId();
+
@Override
public double getResistance() {
- return 2.5;
+ return 1.5;
}
@Override
public double getHardness() {
- return 0.5;
+ return 1.5;
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (Math.abs(player.x - this.x) < 2 && Math.abs(player.z - this.z) < 2) {
+ if (Math.abs(player.getFloorX() - this.x) < 2 && Math.abs(player.getFloorZ() - this.z) < 2) {
double y = player.y + player.getEyeHeight();
-
if (y - this.y > 2) {
this.setDamage(BlockFace.UP.getIndex());
} else if (this.y - y > 0) {
@@ -56,7 +59,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
} else {
this.setDamage(player.getHorizontalFacing().getIndex());
}
- this.level.setBlock(block, this, true, false);
+ this.getLevel().setBlock(this, this, true, false);
CompoundTag nbt = new CompoundTag("")
.putString("id", BlockEntity.PISTON_ARM)
@@ -65,10 +68,11 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
.putInt("z", (int) this.z)
.putBoolean("Sticky", this.sticky);
- BlockEntityPistonArm be = (BlockEntityPistonArm) BlockEntity.createBlockEntity(BlockEntity.PISTON_ARM, this.level.getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
+ BlockEntityPistonArm be = (BlockEntityPistonArm) BlockEntity.createBlockEntity(BlockEntity.PISTON_ARM, this.getChunk(), nbt);
+ be.sticky = this.sticky;
+ be.spawnToAll();
- if (be == null) return false;
- //this.checkState();
+ this.checkState();
return true;
}
@@ -77,8 +81,7 @@ public boolean onBreak(Item item) {
this.level.setBlock(this, Block.get(BlockID.AIR), true, true);
Block block = this.getSide(getFacing());
-
- if (block instanceof BlockPistonHead && ((BlockPistonHead) block).getFacing() == this.getFacing()) {
+ if (block instanceof BlockPistonHead && ((BlockPistonHead) block).getBlockFace() == this.getFacing()) {
block.onBreak(item);
}
return true;
@@ -87,7 +90,7 @@ public boolean onBreak(Item item) {
public boolean isExtended() {
BlockFace face = getFacing();
Block block = getSide(face);
- return block instanceof BlockPistonHead && ((BlockPistonHead) block).getFacing() == face;
+ return block instanceof BlockPistonHead && ((BlockPistonHead) block).getBlockFace() == face;
}
@Override
@@ -95,19 +98,14 @@ public int onUpdate(int type) {
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();
- }
+ if (type == Level.BLOCK_UPDATE_REDSTONE) {
+ RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this);
+ getLevel().getServer().getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return 0;
}
}
-
+ this.checkState();
return type;
}
}
@@ -115,31 +113,36 @@ public int onUpdate(int type) {
private void checkState() {
BlockFace facing = getFacing();
boolean isPowered = this.isPowered();
+ boolean extended = isExtended();
- if (isPowered && !isExtended()) {
- if ((new BlocksCalculator(this.level, this, facing, true)).canMove()) {
- if (!this.doMove(true)) {
+ if (isPowered && !extended) {
+ BlocksCalculator calculator = new BlocksCalculator(this, facing, true);
+ if (calculator.canMove()) {
+ if (!this.doMove(true, calculator)) {
return;
}
-
+ this.updateBlockEntity(true);
this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_PISTON_OUT);
- } else {
}
- } else if (!isPowered && isExtended()) {
- //this.level.setBlock() TODO: set piston extension?
+ return;
+ }
+ if (!isPowered && extended) {
if (this.sticky) {
- Vector3 pos = this.add(facing.getXOffset() * 2, facing.getYOffset() * 2, facing.getZOffset() * 2);
+ Vector3 pos = this.add(facing.getXOffset() << 1, facing.getYOffset() << 1, facing.getZOffset() << 1);
Block block = this.level.getBlock(pos);
if (block.getId() == AIR) {
- this.level.setBlock(this.getLocation().getSide(facing), Block.get(BlockID.AIR), true, true);
+ this.level.setBlock(this.getSideVec(facing), Block.get(BlockID.AIR), true, true);
}
- if (canPush(block, facing.getOpposite(), false) && (!(block instanceof BlockFlowable) || block.getId() == PISTON || block.getId() == STICKY_PISTON)) {
- this.doMove(false);
+ if (canPush(block, facing.getOpposite(), false) && (!(block instanceof BlockFlowable || block.breakWhenPushed()) || block.getId() == PISTON || block.getId() == STICKY_PISTON)) {
+ if (this.doMove(false, null)) {
+ this.updateBlockEntity(false);
+ }
}
} else {
- this.level.setBlock(getLocation().getSide(facing), Block.get(BlockID.AIR), true, false);
+ this.updateBlockEntity(false);
+ this.level.setBlock(getSideVec(facing), Block.get(BlockID.AIR), true, true);
}
this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_PISTON_IN);
@@ -147,14 +150,21 @@ private void checkState() {
}
public BlockFace getFacing() {
- return BlockFace.fromIndex(this.getDamage()).getOpposite();
+ BlockFace face = BlockFace.fromIndex(this.getDamage()).getOpposite();
+ if (face == BlockFace.UP) return BlockFace.DOWN;
+ if (face == BlockFace.DOWN) return BlockFace.UP;
+ return face;
}
private boolean isPowered() {
BlockFace face = getFacing();
+ // Revert to opposite
+ if (face == BlockFace.UP) face = BlockFace.DOWN;
+ if (face == BlockFace.DOWN) face = BlockFace.UP;
+
for (BlockFace side : BlockFace.values()) {
- if (side != face && this.level.isSidePowered(this.getLocation().getSide(side), side)) {
+ if (side != face && this.level.isSidePowered(this.getSideVec(side), side)) {
return true;
}
}
@@ -162,10 +172,10 @@ private boolean isPowered() {
if (this.level.isSidePowered(this, BlockFace.DOWN)) {
return true;
} else {
- Vector3 pos = this.getLocation().up();
+ Vector3 pos = this.getSideVec(BlockFace.UP);
for (BlockFace side : BlockFace.values()) {
- if (side != BlockFace.DOWN && this.level.isSidePowered(pos.getSide(side), side)) {
+ if (side != BlockFace.DOWN && this.level.isSidePowered(pos.getSideVec(side), side)) {
return true;
}
}
@@ -174,23 +184,45 @@ private boolean isPowered() {
}
}
- private boolean doMove(boolean extending) {
- Vector3 pos = this.getLocation();
+ private void updateBlockEntity(boolean extending) {
+ BlockEntity blockEntity = this.level.getBlockEntity(this);
+ if (blockEntity instanceof BlockEntityPistonArm) {
+ BlockEntityPistonArm arm = (BlockEntityPistonArm) blockEntity;
+ if (arm.isExtended() != extending) {
+ this.level.getServer().getPluginManager().callEvent(new BlockPistonChangeEvent(this, extending ? 0 : 15, extending ? 15 : 0));
+ arm.setExtended(extending);
+ arm.broadcastMove();
+ if (arm.chunk != null) {
+ arm.chunk.setChanged();
+ }
+ }
+ }
+ }
+
+ private boolean doMove(boolean extending, BlocksCalculator calculator) {
BlockFace direction = getFacing();
if (!extending) {
- this.level.setBlock(pos.getSide(direction), Block.get(BlockID.AIR), true, false);
+ this.level.setBlock(this.getSideVec(direction), Block.get(BlockID.AIR), true, false);
+ }
+ if (calculator == null) {
+ calculator = new BlocksCalculator(this, direction, extending);
}
- BlocksCalculator calculator = new BlocksCalculator(this.level, this, direction, extending);
+ if (calculator.canMove()) {
+ BlockPistonEvent event = new BlockPistonEvent(this, direction, calculator.getBlocksToMove(), calculator.getBlocksToDestroy(), extending);
+ this.level.getServer().getPluginManager().callEvent(event);
- if (!calculator.canMove()) {
- return false;
- } else {
- List blocks = calculator.getBlocksToMove();
+ if (event.isCancelled()) {
+ return false;
+ }
+ List blocks = calculator.getBlocksToMove();
+ if (!extending && blocks.isEmpty()) {
+ this.level.setBlock(this.getSideVec(direction), Block.get(BlockID.AIR), false, true);
+ return true;
+ }
List newBlocks = new ArrayList<>(blocks);
-
List destroyBlocks = calculator.getBlocksToDestroy();
BlockFace side = extending ? direction : direction.getOpposite();
@@ -201,43 +233,37 @@ private boolean doMove(boolean extending) {
for (int i = blocks.size() - 1; i >= 0; --i) {
Block block = blocks.get(i);
- this.level.setBlock(block, Block.get(BlockID.AIR));
- Vector3 newPos = block.getLocation().getSide(side);
+ this.level.setBlock(block, Block.get(BlockID.AIR), true, false);
+ Vector3 newPos = block.getSideVec(side);
- //TODO: change this to block entity
- this.level.setBlock(newPos, newBlocks.get(i));
+ // TODO: Change this to block entity
+ this.level.setBlock(newPos, newBlocks.get(i), true, false);
}
- Vector3 pistonHead = pos.getSide(direction);
-
if (extending) {
- //extension block entity
-
- this.level.setBlock(pistonHead, Block.get(BlockID.PISTON_HEAD, this.getDamage()));
+ // Extension block entity
+ Vector3 pistonHead = this.getSideVec(direction);
+ this.level.setBlock(pistonHead, Block.get(this.getPistonHeadBlockId(), this.getDamage()), true, false);
}
-
return true;
+ } else {
+ return false;
}
}
public static boolean canPush(Block block, BlockFace face, boolean destroyBlocks) {
- if (block.canBePushed() && block.getY() >= 0 && (face != BlockFace.DOWN || block.getY() != 0) &&
- block.getY() <= 255 && (face != BlockFace.UP || block.getY() != 255)) {
+ if (block.canBePushed() && block.getY() >= block.getLevel().getMinBlockY() && (face != BlockFace.DOWN || block.getY() != block.getLevel().getMinBlockY()) && block.getY() <= block.getLevel().getMaxBlockY() && (face != BlockFace.UP || block.getY() != block.getLevel().getMaxBlockY())) {
if (!(block instanceof BlockPistonBase)) {
-
- if (block instanceof BlockFlowable) {
+ if ((block instanceof BlockFlowable && !(block instanceof BlockEndPortal || block instanceof BlockNetherPortal)) || block.breakWhenPushed()) {
return destroyBlocks;
}
} else return !((BlockPistonBase) block).isExtended();
return true;
}
return false;
-
}
public static class BlocksCalculator {
-
- private final Level level;
private final Vector3 pistonPos;
private final Block blockToMove;
private final BlockFace moveDirection;
@@ -245,8 +271,9 @@ public static class BlocksCalculator {
private final List toMove = new ArrayList<>();
private final List toDestroy = new ArrayList<>();
- public BlocksCalculator(Level level, Block pos, BlockFace facing, boolean extending) {
- this.level = level;
+ protected Boolean canMove;
+
+ public BlocksCalculator(Block pos, BlockFace facing, boolean extending) {
this.pistonPos = pos.getLocation();
if (extending) {
@@ -259,13 +286,25 @@ public BlocksCalculator(Level level, Block pos, BlockFace facing, boolean extend
}
public boolean canMove() {
+ return this.canMove == null ? this.canMove = this.eval() : this.canMove;
+ }
+
+ private boolean eval() {
this.toMove.clear();
this.toDestroy.clear();
- Block block = this.blockToMove;
- if (!canPush(block, this.moveDirection, false)) {
- if (block instanceof BlockFlowable) {
- this.toDestroy.add(this.blockToMove);
+ if (!canPush(this.blockToMove, this.moveDirection, false)) {
+ if ((this.blockToMove instanceof BlockFlowable && !(this.blockToMove instanceof BlockEndPortal || this.blockToMove instanceof BlockNetherPortal)) || this.blockToMove.breakWhenPushed()) {
+ boolean exists = false;
+ for (Block b : this.toDestroy) {
+ if (b.x == this.blockToMove.x && b.y == this.blockToMove.y && b.z == this.blockToMove.z) {
+ exists = true;
+ break;
+ }
+ }
+ if (!exists) {
+ this.toDestroy.add(this.blockToMove);
+ }
return true;
} else {
return false;
@@ -273,18 +312,20 @@ public boolean canMove() {
} else if (!this.addBlockLine(this.blockToMove)) {
return false;
} else {
- for (Block b : this.toMove) {
- if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) {
- return false;
+ /*if (false) { //todo?
+ for (Block b : this.toMove) {
+ if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) {
+ return false;
+ }
}
- }
+ }*/
return true;
}
}
private boolean addBlockLine(Block origin) {
- Block block = origin.clone();
+ Block block = origin/*.clone()*/;
if (block.getId() == AIR) {
return true;
@@ -300,7 +341,7 @@ private boolean addBlockLine(Block origin) {
if (count + this.toMove.size() > 12) {
return false;
} else {
- while (block.getId() == SLIME_BLOCK) {
+ while (false && block.getId() == SLIME_BLOCK) {
block = origin.getSide(this.moveDirection.getOpposite(), count);
if (block.getId() == AIR || !canPush(block, this.moveDirection, false) || block.equals(this.pistonPos)) {
@@ -317,7 +358,11 @@ private boolean addBlockLine(Block origin) {
int blockCount = 0;
for (int step = count - 1; step >= 0; --step) {
- this.toMove.add(block.getSide(this.moveDirection.getOpposite(), step));
+ Block aBlock = block.getSide(this.moveDirection.getOpposite(), step);
+ if (aBlock.breakWhenPushed()) {
+ return true; // shouldn't be possible?
+ }
+ this.toMove.add(aBlock);
++blockCount;
}
@@ -333,7 +378,7 @@ private boolean addBlockLine(Block origin) {
for (int l = 0; l <= index + blockCount; ++l) {
Block b = this.toMove.get(l);
- if (b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) {
+ if (false && b.getId() == SLIME_BLOCK && !this.addBranchingBlocks(b)) {
return false;
}
}
@@ -349,8 +394,17 @@ private boolean addBlockLine(Block origin) {
return false;
}
- if (nextBlock instanceof BlockFlowable) {
- this.toDestroy.add(nextBlock);
+ if ((nextBlock instanceof BlockFlowable && !(nextBlock instanceof BlockEndPortal || nextBlock instanceof BlockNetherPortal)) || nextBlock.breakWhenPushed()) {
+ boolean exists = false;
+ for (Block b : this.toDestroy) {
+ if (b.x == nextBlock.x && b.y == nextBlock.y && b.z == nextBlock.z) {
+ exists = true;
+ break;
+ }
+ }
+ if (!exists) {
+ this.toDestroy.add(nextBlock);
+ }
return true;
}
@@ -397,7 +451,7 @@ public List getBlocksToDestroy() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockPistonExtension.java b/src/main/java/cn/nukkit/block/BlockPistonExtension.java
new file mode 100644
index 00000000000..92df04df905
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPistonExtension.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockPistonExtension extends BlockTransparent {
+
+ @Override
+ public int getId() {
+ return PISTON_EXTENSION;
+ }
+
+ @Override
+ public String getName() {
+ return "Piston Extension";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.1;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPistonHead.java b/src/main/java/cn/nukkit/block/BlockPistonHead.java
index fb99b423307..f58b42c3bff 100644
--- a/src/main/java/cn/nukkit/block/BlockPistonHead.java
+++ b/src/main/java/cn/nukkit/block/BlockPistonHead.java
@@ -3,11 +3,12 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Faceable;
/**
* @author CreeperFace
*/
-public class BlockPistonHead extends BlockTransparentMeta {
+public class BlockPistonHead extends BlockTransparentMeta implements Faceable {
public BlockPistonHead() {
this(0);
@@ -29,12 +30,12 @@ public String getName() {
@Override
public double getResistance() {
- return 2.5;
+ return 1.5;
}
@Override
public double getHardness() {
- return 0.5;
+ return 1.5;
}
@Override
@@ -45,16 +46,19 @@ public Item[] getDrops(Item item) {
@Override
public boolean onBreak(Item item) {
this.level.setBlock(this, Block.get(BlockID.AIR), true, true);
- Block piston = getSide(getFacing().getOpposite());
-
- if (piston instanceof BlockPistonBase && ((BlockPistonBase) piston).getFacing() == this.getFacing()) {
+ Block piston = getSide(getBlockFace().getOpposite());
+ if (piston instanceof BlockPistonBase && ((BlockPistonBase) piston).getFacing() == this.getBlockFace()) {
piston.onBreak(item);
}
return true;
}
- public BlockFace getFacing() {
- return BlockFace.fromIndex(this.getDamage()).getOpposite();
+ @Override
+ public BlockFace getBlockFace() {
+ BlockFace face = BlockFace.fromIndex(this.getDamage()).getOpposite();
+ if (face == BlockFace.UP) return BlockFace.DOWN;
+ if (face == BlockFace.DOWN) return BlockFace.UP;
+ return face;
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockPistonHeadSticky.java b/src/main/java/cn/nukkit/block/BlockPistonHeadSticky.java
new file mode 100644
index 00000000000..ad895ed5090
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPistonHeadSticky.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockPistonHeadSticky extends BlockPistonHead {
+
+ public BlockPistonHeadSticky() {
+ this(0);
+ }
+
+ public BlockPistonHeadSticky(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Sticky Piston Head";
+ }
+
+ @Override
+ public int getId() {
+ return PISTON_HEAD_STICKY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPistonSticky.java b/src/main/java/cn/nukkit/block/BlockPistonSticky.java
index 4962b5c6db3..de68535b58c 100644
--- a/src/main/java/cn/nukkit/block/BlockPistonSticky.java
+++ b/src/main/java/cn/nukkit/block/BlockPistonSticky.java
@@ -19,6 +19,11 @@ public int getId() {
return STICKY_PISTON;
}
+ @Override
+ public int getPistonHeadBlockId() {
+ return PISTON_HEAD_STICKY;
+ }
+
@Override
public String getName() {
return "Sticky Piston";
diff --git a/src/main/java/cn/nukkit/block/BlockPlanks.java b/src/main/java/cn/nukkit/block/BlockPlanks.java
index 409ae264815..840721f41a3 100644
--- a/src/main/java/cn/nukkit/block/BlockPlanks.java
+++ b/src/main/java/cn/nukkit/block/BlockPlanks.java
@@ -4,10 +4,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockPlanks extends BlockSolidMeta {
+
public static final int OAK = 0;
public static final int SPRUCE = 1;
public static final int BIRCH = 2;
@@ -15,7 +16,6 @@ public class BlockPlanks extends BlockSolidMeta {
public static final int ACACIA = 4;
public static final int DARK_OAK = 5;
-
public BlockPlanks() {
this(0);
}
@@ -51,7 +51,7 @@ public int getBurnAbility() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Oak Wood Planks",
"Spruce Wood Planks",
"Birch Wood Planks",
@@ -60,7 +60,7 @@ public String getName() {
"Dark Oak Wood Planks",
};
- return this.getDamage() < 0 ? "Unknown" : names[this.getDamage() % 6];
+ return names[this.getDamage() & 0x07];
}
@Override
@@ -70,7 +70,7 @@ public int getToolType() {
@Override
public BlockColor getColor() {
- switch(getDamage() & 0x07){
+ switch (getDamage() & 0x07) {
default:
case OAK:
return BlockColor.WOOD_BLOCK_COLOR;
diff --git a/src/main/java/cn/nukkit/block/BlockPodzol.java b/src/main/java/cn/nukkit/block/BlockPodzol.java
index b2cff7609f7..512caff6f5e 100644
--- a/src/main/java/cn/nukkit/block/BlockPodzol.java
+++ b/src/main/java/cn/nukkit/block/BlockPodzol.java
@@ -2,7 +2,7 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.utils.BlockColor;
+import cn.nukkit.level.Sound;
/**
* Created on 2015/11/22 by xtypr.
@@ -15,7 +15,6 @@ public BlockPodzol() {
}
public BlockPodzol(int meta) {
- // Podzol can't have meta.
super(0);
}
@@ -34,28 +33,28 @@ public boolean canSilkTouch() {
return true;
}
- @Override
- public boolean canBeActivated() {
- return false;
- }
-
@Override
public boolean onActivate(Item item, Player player) {
+ if (item.isShovel()) {
+ Block up = this.up();
+ if (up instanceof BlockAir || up instanceof BlockFlowable) {
+ item.useOn(this);
+ this.getLevel().setBlock(this, Block.get(GRASS_PATH));
+ if (player != null) {
+ player.getLevel().addSound(player, Sound.STEP_GRASS);
+ }
+ return true;
+ }
+ }
return false;
}
@Override
public int getFullId() {
- return this.getId() << 4;
+ return getId() << DATA_BITS;
}
@Override
public void setDamage(int meta) {
-
- }
-
- @Override
- public BlockColor getColor() {
- return BlockColor.SPRUCE_BLOCK_COLOR;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockPointedDripstone.java b/src/main/java/cn/nukkit/block/BlockPointedDripstone.java
new file mode 100644
index 00000000000..7bdf53e1b77
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPointedDripstone.java
@@ -0,0 +1,288 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.BlockPropertiesHelper;
+import cn.nukkit.block.properties.DripstoneThickness;
+import cn.nukkit.customblock.properties.BlockProperties;
+import cn.nukkit.customblock.properties.BooleanBlockProperty;
+import cn.nukkit.customblock.properties.EnumBlockProperty;
+import cn.nukkit.entity.item.EntityFallingBlock;
+import cn.nukkit.event.block.BlockFallEvent;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Location;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.particle.DestroyBlockParticle;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.DoubleTag;
+import cn.nukkit.nbt.tag.FloatTag;
+import cn.nukkit.nbt.tag.ListTag;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Faceable;
+import it.unimi.dsi.fastutil.ints.IntObjectPair;
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Consumer;
+
+public class BlockPointedDripstone extends BlockSolidMeta implements BlockPropertiesHelper, Faceable {
+
+ private static final float GROWTH_PROBABILITY = 0.011377778F;
+ private static final int MAX_HEIGHT = 7;
+
+ private static final EnumBlockProperty THICKNESS = new EnumBlockProperty<>("dripstone_thickness", false, DripstoneThickness.class);
+ private static final BooleanBlockProperty HANGING = new BooleanBlockProperty("hanging", false);
+ private static final BlockProperties PROPERTIES = new BlockProperties(HANGING, THICKNESS);
+
+ public BlockPointedDripstone() {
+ this(0);
+ }
+
+ public BlockPointedDripstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Pointed Dripstone";
+ }
+
+ @Override
+ public int getId() {
+ return POINTED_DRIPSTONE;
+ }
+
+ @Override
+ public BlockProperties getBlockProperties() {
+ return PROPERTIES;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (!this.canPlaceOn(block.down(), target)) {
+ return false;
+ }
+
+ Block up = this.up();
+ Block down = this.down();
+
+ boolean hanging = false;
+ if (face == BlockFace.UP || face == BlockFace.DOWN) {
+ if ((face == BlockFace.UP && !down.isSolid()) || (face == BlockFace.DOWN && !up.isSolid())) {
+ return false;
+ }
+ hanging = face == BlockFace.DOWN;
+ } else if (up.isSolid()) {
+ hanging = true;
+ } else if (!down.isSolid()) {
+ return false;
+ }
+
+
+ BlockPointedDripstone tip = null;
+ if (up instanceof BlockPointedDripstone && hanging) {
+ tip = (BlockPointedDripstone) up;
+ } else if (down instanceof BlockPointedDripstone) {
+ tip = (BlockPointedDripstone) down;
+ }
+
+ if (tip != null) {
+ IntObjectPair pair = this.getDripstoneHeightFromTip(tip, hanging);
+ int height = pair.keyInt();
+ if (height == 0 || height == MAX_HEIGHT) {
+ return false;
+ }
+ Location location = pair.right().getLocation();
+ this.growPointedDripstone(location, hanging, height);
+ } else {
+ this.setHanging(hanging);
+ this.setThickness(DripstoneThickness.TIP);
+ this.getLevel().setBlock(this, this, true, true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onBreak(Item item, Player player) {
+ boolean hanging = this.isHanging();
+
+ Block newTip = hanging ? this.up() : this.down();
+ if (newTip instanceof BlockPointedDripstone) {
+ ((BlockPointedDripstone) newTip).setThickness(DripstoneThickness.TIP);
+ this.getLevel().setBlock(newTip, newTip);
+ }
+
+ DripstoneThickness thickness = this.getThickness();
+ if (thickness == DripstoneThickness.TIP || thickness == DripstoneThickness.MERGE) {
+ return super.onBreak(item, player);
+ }
+
+ Block block = this;
+ while (block instanceof BlockPointedDripstone) {
+ BlockPointedDripstone dripstone = (BlockPointedDripstone) block;
+ if (this != dripstone) {
+ this.getLevel().addParticle(new DestroyBlockParticle(block.add(0.5), block));
+ if (hanging) {
+ this.spawnFallingBlock(dripstone);
+ } else {
+ this.getLevel().dropItem(block.add(0.5, 0.5, 0.5), block.toItem());
+ }
+ }
+ this.getLevel().setBlock(block, Block.get(BlockID.AIR), false, true);
+ block = hanging ? block.down() : block.up();
+ }
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type != Level.BLOCK_UPDATE_RANDOM) {
+ return 0;
+ }
+
+ if (ThreadLocalRandom.current().nextFloat() >= GROWTH_PROBABILITY || !this.isHanging() || this.up().getId() == POINTED_DRIPSTONE) {
+ return 0;
+ }
+
+ int height;
+ if (this.canGrow() && (height = this.getDripstoneHeightFromBase(this, true)) < MAX_HEIGHT) {
+ BlockGrowEvent event = new BlockGrowEvent(this, Block.get(BlockID.POINTED_DRIPSTONE));
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return 0;
+ }
+ this.growPointedDripstone(this.getLocation(), true, height);
+ }
+
+ // TODO: grow from ground too
+ return 0;
+ }
+
+ private void growPointedDripstone(Position position, boolean hanging, int height) {
+ this.buildBaseToTipColumn(height + 1, false, thickness -> {
+ BlockPointedDripstone dripstone = (BlockPointedDripstone) Block.get(POINTED_DRIPSTONE);
+ dripstone.setHanging(hanging);
+ dripstone.setThickness(thickness);
+ this.getLevel().setBlock(position, dripstone);
+ position.setY(hanging ? position.getY() - 1 : position.getY() + 1);
+ });
+ }
+
+ private IntObjectPair getDripstoneHeightFromTip(Block block, boolean hanging) {
+ int height = 0;
+ BlockPointedDripstone dripstone = null;
+ while (block instanceof BlockPointedDripstone) {
+ height++;
+ dripstone = (BlockPointedDripstone) block;
+ block = hanging ? block.up() : block.down();
+ }
+ return IntObjectPair.of(height, dripstone);
+ }
+
+ private int getDripstoneHeightFromBase(Block block, boolean hanging) {
+ int height = 0;
+ while (block instanceof BlockPointedDripstone) {
+ height++;
+ block = hanging ? block.down() : block.up();
+ }
+ return height;
+ }
+
+ private void buildBaseToTipColumn(int height, boolean merge, Consumer callback) {
+ if (height >= 3) {
+ callback.accept(DripstoneThickness.BASE);
+ for(int i = 0; i < height - 3; ++i) {
+ callback.accept(DripstoneThickness.MIDDLE);
+ }
+ }
+
+ if (height >= 2) {
+ callback.accept(DripstoneThickness.FRUSTUM);
+ }
+
+ if (height >= 1) {
+ callback.accept(merge ? DripstoneThickness.MERGE : DripstoneThickness.TIP);
+ }
+ }
+
+ private boolean canGrow() {
+ Block up2;
+ // TODO: grow from ground too
+ return this.down().getId() == AIR && this.up().getId() == DRIPSTONE_BLOCK && ((up2 = this.up(2)).getId() == WATER || up2.getId() == STILL_WATER);
+ }
+
+ private void spawnFallingBlock(BlockPointedDripstone block) {
+ BlockFallEvent event = new BlockFallEvent(block);
+ this.level.getServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return;
+ }
+
+ CompoundTag nbt = new CompoundTag()
+ .putList(new ListTag("Pos")
+ .add(new DoubleTag("", this.x + 0.5))
+ .add(new DoubleTag("", this.y))
+ .add(new DoubleTag("", this.z + 0.5)))
+ .putList(new ListTag("Motion")
+ .add(new DoubleTag("", 0))
+ .add(new DoubleTag("", 0))
+ .add(new DoubleTag("", 0)))
+
+ .putList(new ListTag("Rotation")
+ .add(new FloatTag("", 0))
+ .add(new FloatTag("", 0)))
+ .putInt("TileID", this.getId())
+ .putByte("Data", this.getDamage());
+
+ EntityFallingBlock fall = new EntityFallingBlock(this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
+ fall.spawnToAll();
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId()), 0, 1);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BROWN_TERRACOTA_BLOCK_COLOR;
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return this.getBooleanValue(HANGING) ? BlockFace.DOWN : BlockFace.UP;
+ }
+
+ public boolean isHanging() {
+ return this.getBooleanValue(HANGING);
+ }
+
+ public void setHanging(boolean hanging) {
+ this.setBooleanValue(HANGING, hanging);
+ }
+
+ public DripstoneThickness getThickness() {
+ return this.getPropertyValue(THICKNESS);
+ }
+
+ public void setThickness(DripstoneThickness value) {
+ this.setPropertyValue(THICKNESS, value);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPolishedBasalt.java b/src/main/java/cn/nukkit/block/BlockPolishedBasalt.java
new file mode 100644
index 00000000000..ae426211685
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPolishedBasalt.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockPolishedBasalt extends BlockBasalt {
+
+ public BlockPolishedBasalt() {
+ this(0);
+ }
+
+ public BlockPolishedBasalt(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Basalt";
+ }
+
+ @Override
+ public int getId() {
+ return BlockID.POLISHED_BASALT;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPolishedBlackstoneBrickWall.java b/src/main/java/cn/nukkit/block/BlockPolishedBlackstoneBrickWall.java
new file mode 100644
index 00000000000..a5992ee797c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPolishedBlackstoneBrickWall.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockPolishedBlackstoneBrickWall extends BlockWall {
+
+ public BlockPolishedBlackstoneBrickWall() {
+ this(0);
+ }
+
+ public BlockPolishedBlackstoneBrickWall(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone Brick Wall";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_BRICK_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPolishedBlackstoneWall.java b/src/main/java/cn/nukkit/block/BlockPolishedBlackstoneWall.java
new file mode 100644
index 00000000000..e61677a44e9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPolishedBlackstoneWall.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockPolishedBlackstoneWall extends BlockWall {
+
+ public BlockPolishedBlackstoneWall() {
+ this(0);
+ }
+
+ public BlockPolishedBlackstoneWall(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone Wall";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPotato.java b/src/main/java/cn/nukkit/block/BlockPotato.java
index 3d93d11d59f..65527080f70 100644
--- a/src/main/java/cn/nukkit/block/BlockPotato.java
+++ b/src/main/java/cn/nukkit/block/BlockPotato.java
@@ -1,9 +1,7 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemPotato;
-
-import java.util.Random;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 15.01.2016.
@@ -30,18 +28,25 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemPotato();
+ return Item.get(Item.POTATO);
}
@Override
public Item[] getDrops(Item item) {
if (getDamage() >= 0x07) {
- return new Item[]{
- new ItemPotato(0, new Random().nextInt(3) + 1)
- };
+ if (Utils.random.nextInt(100) < 2) {
+ return new Item[]{
+ Item.get(Item.POTATO, 0, Utils.random.nextInt(3) + 2),
+ Item.get(Item.POISONOUS_POTATO, 0, 1)
+ };
+ } else {
+ return new Item[]{
+ Item.get(Item.POTATO, 0, Utils.random.nextInt(3) + 2)
+ };
+ }
} else {
return new Item[]{
- new ItemPotato()
+ Item.get(Item.POTATO)
};
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockPowderSnow.java b/src/main/java/cn/nukkit/block/BlockPowderSnow.java
new file mode 100644
index 00000000000..0b8c71fcbfe
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPowderSnow.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockPowderSnow extends BlockTransparent {
+
+ public BlockPowderSnow() {
+ super();
+ }
+
+ @Override
+ public String getName() {
+ return "Powder Snow";
+ }
+
+ @Override
+ public int getId() {
+ return POWDER_SNOW;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.25;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.25;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SNOW_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateAcacia.java b/src/main/java/cn/nukkit/block/BlockPressurePlateAcacia.java
new file mode 100644
index 00000000000..1543cafd353
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateAcacia.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockPressurePlateAcacia extends BlockPressurePlateWood {
+
+ public BlockPressurePlateAcacia() {
+ this(0);
+ }
+
+ public BlockPressurePlateAcacia(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Acacia Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return ACACIA_PRESSURE_PLATE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java b/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java
index 5bcc098a27c..d2228247d3e 100644
--- a/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateBase.java
@@ -9,7 +9,6 @@
import cn.nukkit.event.player.PlayerInteractEvent.Action;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
-import cn.nukkit.level.GlobalBlockPalette;
import cn.nukkit.level.Level;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
@@ -33,11 +32,6 @@ protected BlockPressurePlateBase(int meta) {
super(meta);
}
- @Override
- public boolean canPassThrough() {
- return true;
- }
-
@Override
public boolean canHarvestWithHand() {
return false;
@@ -53,11 +47,6 @@ public double getMinZ() {
return this.z + 0.625;
}
- @Override
- public double getMinY() {
- return this.y + 0;
- }
-
@Override
public double getMaxX() {
return this.x + 0.9375;
@@ -70,7 +59,7 @@ public double getMaxZ() {
@Override
public double getMaxY() {
- return isActivated() ? this.y + 0.03125 : this.y + 0.0625;
+ return this.isActivated() ? this.y + 0.03125 : this.y + 0.0625;
}
@Override
@@ -107,7 +96,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
return false;
}
- this.level.setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@@ -147,7 +136,7 @@ protected void updateState(int oldStrength) {
this.level.setBlock(this, this, false, false);
this.level.updateAroundRedstone(this, null);
- this.level.updateAroundRedstone(this.getLocation().down(), null);
+ this.level.updateAroundRedstone(this.getSideVec(BlockFace.DOWN), null);
if (!isPowered && wasPowered) {
this.playOffSound();
@@ -169,7 +158,7 @@ public boolean onBreak(Item item) {
if (this.getRedstonePower() > 0) {
this.level.updateAroundRedstone(this, null);
- this.level.updateAroundRedstone(this.getLocation().down(), null);
+ this.level.updateAroundRedstone(this.getSideVec(BlockFace.DOWN), null);
}
return true;
@@ -194,11 +183,11 @@ public void setRedstonePower(int power) {
}
protected void playOnSound() {
- this.level.addLevelSoundEvent(this.add(0.5, 0.1, 0.5), LevelSoundEventPacket.SOUND_POWER_ON, GlobalBlockPalette.getOrCreateRuntimeId(this.getId(), this.getDamage()));
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_ON);
}
protected void playOffSound() {
- this.level.addLevelSoundEvent(this.add(0.5, 0.1, 0.5), LevelSoundEventPacket.SOUND_POWER_OFF, GlobalBlockPalette.getOrCreateRuntimeId(this.getId(), this.getDamage()));
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_OFF);
}
protected abstract int computeRedstoneStrength();
@@ -207,4 +196,9 @@ protected void playOffSound() {
public Item toItem() {
return new ItemBlock(this, 0, 1);
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateBirch.java b/src/main/java/cn/nukkit/block/BlockPressurePlateBirch.java
new file mode 100644
index 00000000000..a17fda05033
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateBirch.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockPressurePlateBirch extends BlockPressurePlateWood {
+
+ public BlockPressurePlateBirch() {
+ this(0);
+ }
+
+ public BlockPressurePlateBirch(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Birch Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return BIRCH_PRESSURE_PLATE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SAND_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateCrimson.java b/src/main/java/cn/nukkit/block/BlockPressurePlateCrimson.java
new file mode 100644
index 00000000000..e8014b9d19b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateCrimson.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockPressurePlateCrimson extends BlockPressurePlateWood {
+
+ public BlockPressurePlateCrimson() {
+ this(0);
+ }
+
+ public BlockPressurePlateCrimson(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Crimson Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_PRESSURE_PLATE;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateDarkOak.java b/src/main/java/cn/nukkit/block/BlockPressurePlateDarkOak.java
new file mode 100644
index 00000000000..0e5d6e89906
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateDarkOak.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockPressurePlateDarkOak extends BlockPressurePlateWood {
+
+ public BlockPressurePlateDarkOak() {
+ this(0);
+ }
+
+ public BlockPressurePlateDarkOak(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Dark Oak Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return DARK_OAK_PRESSURE_PLATE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BROWN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateJungle.java b/src/main/java/cn/nukkit/block/BlockPressurePlateJungle.java
new file mode 100644
index 00000000000..e2ab97d937a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateJungle.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockPressurePlateJungle extends BlockPressurePlateWood {
+
+ public BlockPressurePlateJungle() {
+ this(0);
+ }
+
+ public BlockPressurePlateJungle(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Jungle Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return JUNGLE_PRESSURE_PLATE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DIRT_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlatePolishedBlackstone.java b/src/main/java/cn/nukkit/block/BlockPressurePlatePolishedBlackstone.java
new file mode 100644
index 00000000000..d63ff6c0e6b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlatePolishedBlackstone.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockPressurePlatePolishedBlackstone extends BlockPressurePlateStone {
+
+ public BlockPressurePlatePolishedBlackstone() {
+ this(0);
+ }
+
+ public BlockPressurePlatePolishedBlackstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_PRESSURE_PLATE;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateSpruce.java b/src/main/java/cn/nukkit/block/BlockPressurePlateSpruce.java
new file mode 100644
index 00000000000..fe84bd1e102
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateSpruce.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockPressurePlateSpruce extends BlockPressurePlateWood {
+
+ public BlockPressurePlateSpruce() {
+ this(0);
+ }
+
+ public BlockPressurePlateSpruce(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Spruce Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return SPRUCE_PRESSURE_PLATE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SPRUCE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateStone.java b/src/main/java/cn/nukkit/block/BlockPressurePlateStone.java
index 2abf30e05d7..e18fa96f049 100644
--- a/src/main/java/cn/nukkit/block/BlockPressurePlateStone.java
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateStone.java
@@ -49,7 +49,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockPressurePlateWarped.java b/src/main/java/cn/nukkit/block/BlockPressurePlateWarped.java
new file mode 100644
index 00000000000..8523e4870e7
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateWarped.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockPressurePlateWarped extends BlockPressurePlateWood {
+
+ public BlockPressurePlateWarped() {
+ this(0);
+ }
+
+ public BlockPressurePlateWarped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Pressure Plate";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_PRESSURE_PLATE;
+ }
+}
+
diff --git a/src/main/java/cn/nukkit/block/BlockPrismarine.java b/src/main/java/cn/nukkit/block/BlockPrismarine.java
index 6073aeffa0c..73bbcb0b4d4 100644
--- a/src/main/java/cn/nukkit/block/BlockPrismarine.java
+++ b/src/main/java/cn/nukkit/block/BlockPrismarine.java
@@ -11,10 +11,10 @@ public class BlockPrismarine extends BlockSolidMeta {
public static final int DARK = 1;
public static final int BRICKS = 2;
- private static final String[] NAMES = new String[]{
+ private static final String[] NAMES = {
"Prismarine",
- "Dark prismarine",
- "Prismarine bricks"
+ "Dark Prismarine",
+ "Prismarine Bricks"
};
public BlockPrismarine() {
@@ -52,7 +52,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -68,11 +68,11 @@ public boolean canHarvestWithHand() {
@Override
public BlockColor getColor() {
- switch(getDamage() & 0x07){
+ switch (getDamage() & 0x07) {
case NORMAL:
return BlockColor.CYAN_BLOCK_COLOR;
- case DARK:
case BRICKS:
+ case DARK:
return BlockColor.DIAMOND_BLOCK_COLOR;
default:
return BlockColor.STONE_BLOCK_COLOR;
diff --git a/src/main/java/cn/nukkit/block/BlockPumpkin.java b/src/main/java/cn/nukkit/block/BlockPumpkin.java
index 82aabab6b0b..478900662d4 100644
--- a/src/main/java/cn/nukkit/block/BlockPumpkin.java
+++ b/src/main/java/cn/nukkit/block/BlockPumpkin.java
@@ -3,6 +3,7 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemID;
import cn.nukkit.item.ItemTool;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
@@ -13,6 +14,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockPumpkin extends BlockSolidMeta implements Faceable {
+
public BlockPumpkin() {
this(0);
}
@@ -48,12 +50,12 @@ public int getToolType() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- this.setDamage(player != null ? player.getDirection().getOpposite().getHorizontalIndex() : 0);
+ this.setBlockFace(player != null ? player.getDirection().getOpposite() : BlockFace.SOUTH);
this.getLevel().setBlock(block, this, true, true);
return true;
}
@@ -64,12 +66,36 @@ public BlockColor getColor() {
}
@Override
- public boolean canBePushed() {
- return false;
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
@Override
- public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (!item.isShears()) {
+ return false;
+ }
+
+ BlockPumpkinCarved carvedPumpkin = new BlockPumpkinCarved();
+ carvedPumpkin.setBlockFace(this.getBlockFace());
+ item.useOn(this);
+ this.level.setBlock(this, carvedPumpkin, true, true);
+ this.getLevel().dropItem(add(0.5, 0.5, 0.5), Item.get(ItemID.PUMPKIN_SEEDS));
+ this.getLevel().dropItem(add(0.5, 0.5, 0.5), Item.get(Item.PUMPKIN_SEEDS));return true;
+ }
+
+ public void setBlockFace(BlockFace blockFace) {
+ this.setDamage(blockFace.getHorizontalIndex());
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockPumpkinCarved.java b/src/main/java/cn/nukkit/block/BlockPumpkinCarved.java
new file mode 100644
index 00000000000..1a5786f4cac
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPumpkinCarved.java
@@ -0,0 +1,26 @@
+package cn.nukkit.block;
+
+public class BlockPumpkinCarved extends BlockPumpkin {
+
+ public BlockPumpkinCarved() {
+ }
+
+ public BlockPumpkinCarved(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Carved Pumpkin";
+ }
+
+ @Override
+ public int getId() {
+ return CARVED_PUMPKIN;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockPumpkinLit.java b/src/main/java/cn/nukkit/block/BlockPumpkinLit.java
index 0f55f876885..8cad971779f 100644
--- a/src/main/java/cn/nukkit/block/BlockPumpkinLit.java
+++ b/src/main/java/cn/nukkit/block/BlockPumpkinLit.java
@@ -5,6 +5,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockPumpkinLit extends BlockPumpkin {
+
public BlockPumpkinLit() {
this(0);
}
@@ -28,4 +29,8 @@ public int getLightLevel() {
return 15;
}
+ @Override
+ public boolean canBeActivated() {
+ return false;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockPurpur.java b/src/main/java/cn/nukkit/block/BlockPurpur.java
index 3f611508142..0265a386e2e 100644
--- a/src/main/java/cn/nukkit/block/BlockPurpur.java
+++ b/src/main/java/cn/nukkit/block/BlockPurpur.java
@@ -12,8 +12,17 @@ public class BlockPurpur extends BlockSolidMeta {
public static final int PURPUR_NORMAL = 0;
public static final int PURPUR_PILLAR = 2;
+ private static final short[] faces = {
+ 0,
+ 0,
+ 0b1000,
+ 0b1000,
+ 0b0100,
+ 0b0100
+ };
+
public BlockPurpur() {
- this(0);
+ this(PURPUR_NORMAL);
}
public BlockPurpur(int meta) {
@@ -22,7 +31,7 @@ public BlockPurpur(int meta) {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Purpur Block",
"",
"Purpur Pillar",
@@ -55,15 +64,6 @@ public int getToolType() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (this.getDamage() != PURPUR_NORMAL) {
- short[] faces = new short[]{
- 0,
- 0,
- 0b1000,
- 0b1000,
- 0b0100,
- 0b0100
- };
-
this.setDamage(((this.getDamage() & 0x03) | faces[face.getIndex()]));
}
this.getLevel().setBlock(block, this, true, true);
@@ -73,7 +73,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -84,7 +84,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.PURPUR_BLOCK), this.getDamage() & 0x03, 1);
+ return new ItemBlock(Block.get(Block.PURPUR_BLOCK), this.getDamage() & 0x03, 1);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockQuartz.java b/src/main/java/cn/nukkit/block/BlockQuartz.java
index 2bacdbc1ff8..e30f4d4b5d9 100644
--- a/src/main/java/cn/nukkit/block/BlockQuartz.java
+++ b/src/main/java/cn/nukkit/block/BlockQuartz.java
@@ -8,7 +8,7 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockQuartz extends BlockSolidMeta {
@@ -18,6 +18,14 @@ public class BlockQuartz extends BlockSolidMeta {
public static final int QUARTZ_PILLAR = 2;
public static final int QUARTZ_PILLAR2 = 3;
+ private static final short[] faces = {
+ 0,
+ 0,
+ 0b1000,
+ 0b1000,
+ 0b0100,
+ 0b0100
+ };
public BlockQuartz() {
this(0);
@@ -44,7 +52,7 @@ public double getResistance() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Quartz Block",
"Chiseled Quartz Block",
"Quartz Pillar",
@@ -57,15 +65,6 @@ public String getName() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (this.getDamage() != QUARTZ_NORMAL) {
- short[] faces = new short[]{
- 0,
- 0,
- 0b1000,
- 0b1000,
- 0b0100,
- 0b0100
- };
-
this.setDamage(((this.getDamage() & 0x03) | faces[face.getIndex()]));
}
this.getLevel().setBlock(block, this, true, true);
@@ -75,7 +74,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -86,7 +85,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.QUARTZ_BLOCK), this.getDamage() & 0x03, 1);
+ return new ItemBlock(this, this.getDamage() & 0x03, 1);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockQuartzBricks.java b/src/main/java/cn/nukkit/block/BlockQuartzBricks.java
new file mode 100644
index 00000000000..93983ec9c83
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockQuartzBricks.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockQuartzBricks extends BlockQuartz {
+
+ public BlockQuartzBricks() {
+ this(0);
+ }
+
+ public BlockQuartzBricks(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Quartz Bricks";
+ }
+
+ @Override
+ public int getId() {
+ return QUARTZ_BRICKS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRail.java b/src/main/java/cn/nukkit/block/BlockRail.java
index e7f348763ce..5f4aad016c1 100644
--- a/src/main/java/cn/nukkit/block/BlockRail.java
+++ b/src/main/java/cn/nukkit/block/BlockRail.java
@@ -58,11 +58,6 @@ public double getResistance() {
return 3.5;
}
- @Override
- public boolean canPassThrough() {
- return true;
- }
-
@Override
public int getToolType() {
return ItemTool.TYPE_PICKAXE;
@@ -72,23 +67,73 @@ public int getToolType() {
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
Optional ascendingDirection = this.getOrientation().ascendingDirection();
- Block down = this.down();
- if ((down.isTransparent() && down.getId() != HOPPER_BLOCK) || (ascendingDirection.isPresent() && this.getSide(ascendingDirection.get()).isTransparent())) {
+ if (!canStayOnFullNonSolid(this.down()) || (ascendingDirection.isPresent() && this.getSide(ascendingDirection.get()).isTransparent())) {
this.getLevel().useBreakOn(this);
return Level.BLOCK_UPDATE_NORMAL;
}
+ } else if (type == Level.BLOCK_UPDATE_REDSTONE) {
+ if (this instanceof BlockRailPowered || this instanceof BlockRailDetector || this instanceof BlockRailActivator) {
+ return 0;
+ }
+ boolean power = level.isBlockPowered(this);
+ Map railsAround = this.checkRailsAround(Arrays.asList(SOUTH, EAST, WEST, NORTH));
+ int railsAmount = railsAround.size();
+ if (railsAmount <= 2) {
+ return 0;
+ }
+ List rails = new ArrayList<>(railsAround.keySet());
+ List faces = new ArrayList<>(railsAround.values());
+ if (railsAmount == 4) {
+ if (this.isAbstract()) {
+ if (power) {
+ this.setDamage(this.connect(rails.get(faces.indexOf(NORTH)), NORTH, rails.get(faces.indexOf(WEST)), WEST).metadata());
+ } else {
+ this.setDamage(this.connect(rails.get(faces.indexOf(SOUTH)), SOUTH, rails.get(faces.indexOf(EAST)), EAST).metadata());
+ }
+ } else {
+ this.setDamage(this.connect(rails.get(faces.indexOf(EAST)), EAST, rails.get(faces.indexOf(WEST)), WEST).metadata());
+ }
+ } else if (!railsAround.isEmpty()) {
+ if (this.isAbstract()) {
+ List cd;
+ if (power) {
+ cd = Stream.of(CURVED_NORTH_WEST, CURVED_SOUTH_WEST, CURVED_NORTH_EAST)
+ .filter(o -> faces.containsAll(o.connectingDirections()))
+ .findFirst().get().connectingDirections();
+ } else {
+ cd = Stream.of(CURVED_SOUTH_EAST, CURVED_NORTH_EAST, CURVED_SOUTH_WEST)
+ .filter(o -> faces.containsAll(o.connectingDirections()))
+ .findFirst().get().connectingDirections();
+ }
+ BlockFace f1 = cd.get(0);
+ BlockFace f2 = cd.get(1);
+ this.setDamage(this.connect(rails.get(faces.indexOf(f1)), f1, rails.get(faces.indexOf(f2)), f2).metadata());
+ } else {
+ BlockFace face = faces.stream().min((f1, f2) -> (f1.getIndex() < f2.getIndex()) ? 1 : ((x == y) ? 0 : -1)).get();
+ BlockFace opposite = face.getOpposite();
+ if (faces.contains(opposite)) {
+ this.setDamage(this.connect(rails.get(faces.indexOf(face)), face, rails.get(faces.indexOf(opposite)), opposite).metadata());
+ } else {
+ this.setDamage(this.connect(rails.get(faces.indexOf(face)), face).metadata());
+ }
+ }
+ }
+ this.level.setBlock(this, this, true, true);
+ if (!isAbstract()) {
+ level.scheduleUpdate(this, this, 0);
+ }
}
return 0;
}
@Override
- public double getMaxY() {
- return this.y + 0.125;
+ public AxisAlignedBB recalculateBoundingBox() {
+ return this;
}
@Override
- public AxisAlignedBB recalculateBoundingBox() {
- return this;
+ public double getMaxY() {
+ return this.y + 0.125;
}
@Override
@@ -99,8 +144,7 @@ public BlockColor getColor() {
//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 || (down.isTransparent() && down.getId() != HOPPER_BLOCK)) {
+ if (!canStayOnFullNonSolid(this.down())) {
return false;
}
Map railsAround = this.checkRailsAroundAffected();
@@ -139,7 +183,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
}
- this.level.setBlock(this, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
if (!isAbstract()) {
level.scheduleUpdate(this, this, 0);
}
@@ -259,18 +303,28 @@ public void setActive(boolean active) {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- Item.get(Item.RAIL, 0, 1)
+ Item.get(Item.RAIL)
};
}
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockRailActivator.java b/src/main/java/cn/nukkit/block/BlockRailActivator.java
index c48fa6a4d4a..491b7fc6866 100644
--- a/src/main/java/cn/nukkit/block/BlockRailActivator.java
+++ b/src/main/java/cn/nukkit/block/BlockRailActivator.java
@@ -1,7 +1,9 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
import cn.nukkit.math.Vector3;
import cn.nukkit.utils.Rail;
@@ -36,21 +38,15 @@ public int onUpdate(int type) {
return 0; // Already broken
}
- boolean wasPowered = isActive();
- boolean isPowered = level.isBlockPowered(this.getLocation())
+ boolean isPowered = level.isBlockPowered(this)
|| checkSurrounding(this, true, 0)
|| checkSurrounding(this, false, 0);
- boolean hasUpdate = false;
- if (wasPowered != isPowered) {
+ if (isActive() != isPowered) {
setActive(isPowered);
- hasUpdate = true;
- }
-
- if (hasUpdate) {
- level.updateAround(down());
+ level.updateAround(getSideVec(BlockFace.DOWN));
if (getOrientation().isAscending()) {
- level.updateAround(up());
+ level.updateAround(getSideVec(BlockFace.UP));
}
}
return type;
@@ -75,7 +71,7 @@ protected boolean checkSurrounding(Vector3 pos, boolean relative, int power) {
int dz = pos.getFloorZ();
BlockRail block;
- Block block2 = level.getBlock(new Vector3(dx, dy, dz));
+ Block block2 = level.getBlock(dx, dy, dz);
if (Rail.isRailBlock(block2)) {
block = (BlockRail) block2;
@@ -169,15 +165,20 @@ protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boo
&& (level.isBlockPowered(pos) || checkSurrounding(pos, relative, power + 1));
}
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- Item.get(Item.ACTIVATOR_RAIL, 0, 1)
+ toItem()
};
}
@Override
public double getHardness() {
- return 0.5;
+ return 0.5; // 0.7
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockRailDetector.java b/src/main/java/cn/nukkit/block/BlockRailDetector.java
index 28f83f07b81..b028f4f1d3e 100644
--- a/src/main/java/cn/nukkit/block/BlockRailDetector.java
+++ b/src/main/java/cn/nukkit/block/BlockRailDetector.java
@@ -3,6 +3,7 @@
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.item.EntityMinecartAbstract;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.SimpleAxisAlignedBB;
@@ -10,7 +11,7 @@
/**
* Created on 2015/11/22 by CreeperFace.
* Contributed by: larryTheCoder on 2017/7/8.
- *
+ *
* Nukkit Project,
* Minecart and Riding Project,
* Package cn.nukkit.block in project Nukkit.
@@ -48,7 +49,7 @@ public int getWeakPower(BlockFace side) {
@Override
public int getStrongPower(BlockFace side) {
- return isActive() ? 0 : (side == BlockFace.UP ? 15 : 0);
+ return !isActive() ? 0 : (side == BlockFace.UP ? 15 : 0);
}
@Override
@@ -60,6 +61,11 @@ public int onUpdate(int type) {
return super.onUpdate(type);
}
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
@Override
public void onEntityCollide(Entity entity) {
updateState();
@@ -68,13 +74,14 @@ public void onEntityCollide(Entity entity) {
protected void updateState() {
boolean wasPowered = isActive();
boolean isPowered = false;
+ boolean changed = false;
- for (Entity entity : level.getNearbyEntities(new SimpleAxisAlignedBB(
+ for (Entity entity : level.getCollidingEntities(new SimpleAxisAlignedBB(
getFloorX() + 0.125D,
getFloorY(),
getFloorZ() + 0.125D,
getFloorX() + 0.875D,
- getFloorY() + 0.525D,
+ getFloorY() + 0.750D,
getFloorZ() + 0.875D))) {
if (entity instanceof EntityMinecartAbstract) {
isPowered = true;
@@ -85,22 +92,31 @@ protected void updateState() {
if (isPowered && !wasPowered) {
setActive(true);
level.scheduleUpdate(this, this, 0);
- level.scheduleUpdate(this, this.down(), 0);
+ level.scheduleUpdate(this, this.getSideVec(BlockFace.DOWN), 0);
+ changed = true;
}
if (!isPowered && wasPowered) {
setActive(false);
level.scheduleUpdate(this, this, 0);
- level.scheduleUpdate(this, this.down(), 0);
+ level.scheduleUpdate(this, this.getSideVec(BlockFace.DOWN), 0);
+ changed = true;
}
- level.updateComparatorOutputLevel(this);
+ if (changed) {
+ level.updateComparatorOutputLevel(this);
+ }
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- Item.get(Item.DETECTOR_RAIL, 0, 1)
+ toItem()
};
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockRailPowered.java b/src/main/java/cn/nukkit/block/BlockRailPowered.java
index 83a88072314..152b06e3b45 100644
--- a/src/main/java/cn/nukkit/block/BlockRailPowered.java
+++ b/src/main/java/cn/nukkit/block/BlockRailPowered.java
@@ -1,14 +1,16 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
import cn.nukkit.math.Vector3;
import cn.nukkit.utils.Rail;
/**
* Created by Snake1999 on 2016/1/11.
* Contributed by: larryTheCoder on 2017/7/18.
- *
+ *
* Nukkit Project,
* Minecart and Riding Project,
* Package cn.nukkit.block in project Nukkit.
@@ -36,25 +38,20 @@ public String getName() {
@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) {
if (super.onUpdate(type) == Level.BLOCK_UPDATE_NORMAL) {
return 0; // Already broken
}
- boolean wasPowered = isActive();
- boolean isPowered = level.isBlockPowered(this.getLocation())
+
+ boolean isPowered = level.isBlockPowered(this)
|| checkSurrounding(this, true, 0)
|| checkSurrounding(this, false, 0);
- // Avoid Block minstake
- if (wasPowered != isPowered) {
+ if (isActive() != isPowered) {
setActive(isPowered);
- level.updateAround(down());
+ level.updateAround(this.getSideVec(BlockFace.DOWN));
if (getOrientation().isAscending()) {
- level.updateAround(up());
+ level.updateAround(this.getSideVec(BlockFace.UP));
}
}
return type;
@@ -81,7 +78,7 @@ protected boolean checkSurrounding(Vector3 pos, boolean relative, int power) {
int dz = pos.getFloorZ();
// First: get the base block
BlockRail block;
- Block block2 = level.getBlock(new Vector3(dx, dy, dz));
+ Block block2 = level.getBlock(dx, dy, dz);
// Second: check if the rail is Powered rail
if (Rail.isRailBlock(block2)) {
@@ -182,10 +179,15 @@ protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boo
&& (level.isBlockPowered(pos) || checkSurrounding(pos, relative, power + 1));
}
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- Item.get(Item.POWERED_RAIL, 0, 1)
+ toItem()
};
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockRawCopper.java b/src/main/java/cn/nukkit/block/BlockRawCopper.java
new file mode 100644
index 00000000000..d2c7cd86d7c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockRawCopper.java
@@ -0,0 +1,17 @@
+package cn.nukkit.block;
+
+public class BlockRawCopper extends BlockRawOreVariant {
+
+ public BlockRawCopper() {
+ }
+
+ @Override
+ public String getName() {
+ return "Block of Raw Copper";
+ }
+
+ @Override
+ public int getId() {
+ return RAW_COPPER_BLOCK;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRawGold.java b/src/main/java/cn/nukkit/block/BlockRawGold.java
new file mode 100644
index 00000000000..b907481662d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockRawGold.java
@@ -0,0 +1,24 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockRawGold extends BlockRawOreVariant {
+
+ public BlockRawGold() {
+ }
+
+ @Override
+ public String getName() {
+ return "Block of Raw Gold";
+ }
+
+ @Override
+ public int getId() {
+ return RAW_GOLD_BLOCK;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_IRON;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRawIron.java b/src/main/java/cn/nukkit/block/BlockRawIron.java
new file mode 100644
index 00000000000..2a94a5e2fed
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockRawIron.java
@@ -0,0 +1,17 @@
+package cn.nukkit.block;
+
+public class BlockRawIron extends BlockRawOreVariant {
+
+ public BlockRawIron() {
+ }
+
+ @Override
+ public String getName() {
+ return "Block of Raw Iron";
+ }
+
+ @Override
+ public int getId() {
+ return RAW_IRON_BLOCK;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRawOreVariant.java b/src/main/java/cn/nukkit/block/BlockRawOreVariant.java
new file mode 100644
index 00000000000..369d62ff264
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockRawOreVariant.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+
+public abstract class BlockRawOreVariant extends BlockSolid {
+
+ public BlockRawOreVariant() {
+ }
+
+ @Override
+ public double getHardness() {
+ return 5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.getTier() < this.getToolTier()) {
+ return new Item[0];
+ }
+ return super.getDrops(item);
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_STONE;
+ }
+
+ // TODO:
+ /*@Override
+ public boolean isLavaResistant() {
+ return true;
+ }*/
+
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRedSandstone.java b/src/main/java/cn/nukkit/block/BlockRedSandstone.java
index 4439e716df8..49968097168 100644
--- a/src/main/java/cn/nukkit/block/BlockRedSandstone.java
+++ b/src/main/java/cn/nukkit/block/BlockRedSandstone.java
@@ -2,7 +2,6 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
-import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
/**
@@ -25,7 +24,7 @@ public int getId() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Red Sandstone",
"Chiseled Red Sandstone",
"Smooth Red Sandstone",
@@ -37,7 +36,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -51,11 +50,6 @@ public Item toItem() {
return new ItemBlock(this, this.getDamage() & 0x03);
}
- @Override
- public boolean canHarvestWithHand() {
- return false;
- }
-
@Override
public BlockColor getColor() {
return BlockColor.ORANGE_BLOCK_COLOR;
diff --git a/src/main/java/cn/nukkit/block/BlockRedstone.java b/src/main/java/cn/nukkit/block/BlockRedstone.java
index 19f8d33e01c..e44ec885d5f 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstone.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstone.java
@@ -1,5 +1,6 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
import cn.nukkit.math.BlockFace;
@@ -9,15 +10,7 @@
* Created on 2015/12/11 by Pub4Game.
* Package cn.nukkit.block in project Nukkit .
*/
-public class BlockRedstone extends BlockSolidMeta {
-
- public BlockRedstone() {
- this(0);
- }
-
- public BlockRedstone(int meta) {
- super(0);
- }
+public class BlockRedstone extends BlockSolid {
@Override
public int getId() {
@@ -41,14 +34,30 @@ public int getToolType() {
@Override
public String getName() {
- return "Redstone Block";
+ return "Block of Redstone";
+ }
+
+ @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)) {
+ return false;
+ }
+ this.level.updateAroundRedstone(this, null);
+ return true;
}
- //TODO: redstone
+ @Override
+ public boolean onBreak(Item item) {
+ if (!super.onBreak(item)) {
+ return false;
+ }
+ this.level.updateAroundRedstone(this, null);
+ return true;
+ }
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -76,4 +85,9 @@ public int getWeakPower(BlockFace face) {
public boolean canHarvestWithHand() {
return false;
}
-}
\ No newline at end of file
+
+ @Override
+ public boolean canBePushed() {
+ return false; // TODO: remove when crash issue fixed
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java b/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java
index 11d5711c0cd..05229a25ab5 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneComparator.java
@@ -4,12 +4,11 @@
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityComparator;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemRedstoneComparator;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;
-import cn.nukkit.network.protocol.LevelEventPacket;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.utils.BlockColor;
/**
@@ -41,12 +40,12 @@ public Mode getMode() {
@Override
protected BlockRedstoneComparator getUnpowered() {
- return (BlockRedstoneComparator) Block.get(BlockID.UNPOWERED_COMPARATOR, this.getDamage());
+ return (BlockRedstoneComparator) Block.get(UNPOWERED_COMPARATOR, this.getDamage());
}
@Override
protected BlockRedstoneComparator getPowered() {
- return (BlockRedstoneComparator) Block.get(BlockID.POWERED_COMPARATOR, this.getDamage());
+ return (BlockRedstoneComparator) Block.get(POWERED_COMPARATOR, this.getDamage());
}
@Override
@@ -64,13 +63,12 @@ public void updateState() {
int power = blockEntity instanceof BlockEntityComparator ? ((BlockEntityComparator) blockEntity).getOutputSignal() : 0;
if (output != power || this.isPowered() != this.shouldBePowered()) {
- /*if(isFacingTowardsRepeater()) {
+ /*if (isFacingTowardsRepeater()) {
this.level.scheduleUpdate(this, this, 2, -1);
} else {
this.level.scheduleUpdate(this, this, 2, 0);
}*/
- //System.out.println("schedule update 0");
this.level.scheduleUpdate(this, this, 2);
}
}
@@ -119,9 +117,12 @@ public boolean onActivate(Item item, Player player) {
this.setDamage(this.getDamage() + 4);
}
- this.level.addLevelEvent(this.add(0.5, 0.5, 0.5), LevelEventPacket.EVENT_SOUND_BUTTON_CLICK, this.getMode() == Mode.SUBTRACT ? 500 : 550);
+ if (this.getMode() == Mode.SUBTRACT) {
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_ON);
+ } else {
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POWER_OFF);
+ }
this.level.setBlock(this, this, true, false);
- //bug?
this.onChange();
return true;
@@ -158,7 +159,10 @@ private void onChange() {
this.level.setBlock(this, getPowered(), true, false);
}
- this.level.updateAroundRedstone(this, null);
+ this.level.updateAroundRedstone(this, null); //TODO: remove
+ //Block side = this.getSide(getFacing().getOpposite());
+ //side.onUpdate(Level.BLOCK_UPDATE_REDSTONE);
+ //this.level.updateAroundRedstone(side, null);
}
}
@@ -171,10 +175,8 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
.putInt("x", (int) this.x)
.putInt("y", (int) this.y)
.putInt("z", (int) this.z);
- BlockEntityComparator comparator = (BlockEntityComparator) BlockEntity.createBlockEntity(BlockEntity.COMPARATOR, this.level.getChunk((int) this.x >> 4, (int) this.z >> 4), nbt);
- if (comparator == null) {
- return false;
- }
+ BlockEntity.createBlockEntity(BlockEntity.COMPARATOR, this.getChunk(), nbt);
+
onUpdate(Level.BLOCK_UPDATE_REDSTONE);
return true;
}
@@ -189,7 +191,7 @@ public boolean isPowered() {
@Override
public Item toItem() {
- return new ItemRedstoneComparator();
+ return Item.get(Item.COMPARATOR);
}
public enum Mode {
@@ -201,4 +203,9 @@ public enum Mode {
public BlockColor getColor() {
return BlockColor.AIR_BLOCK_COLOR;
}
+
+ @Override
+ public boolean canBePushed() {
+ return false; // prevent item loss issue with pistons until a working implementation
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java
index 668d3853bfb..9dd0ed9faa8 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneDiode.java
@@ -26,18 +26,18 @@ public BlockRedstoneDiode(int meta) {
@Override
public boolean onBreak(Item item) {
- Vector3 pos = getLocation();
this.level.setBlock(this, Block.get(BlockID.AIR), true, true);
for (BlockFace face : BlockFace.values()) {
- this.level.updateAroundRedstone(pos.getSide(face), null);
+ this.level.updateAroundRedstone(this.getSideVec(face), null);
}
+
return true;
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (block.getSide(BlockFace.DOWN).isTransparent()) {
+ if (!canStayOnFullSolid(this.down())) {
return false;
}
@@ -54,19 +54,17 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_SCHEDULED) {
if (!this.isLocked()) {
- Vector3 pos = getLocation();
boolean shouldBePowered = this.shouldBePowered();
if (this.isPowered && !shouldBePowered) {
- this.level.setBlock(pos, this.getUnpowered(), true, true);
+ this.level.setBlock(this, this.getUnpowered(), true, true);
- this.level.updateAroundRedstone(this.getLocation().getSide(getFacing().getOpposite()), null);
+ this.level.updateAroundRedstone(this.getSideVec(getFacing().getOpposite()), null);
} else if (!this.isPowered) {
- this.level.setBlock(pos, this.getPowered(), true, true);
- this.level.updateAroundRedstone(this.getLocation().getSide(getFacing().getOpposite()), null);
+ this.level.setBlock(this, this.getPowered(), true, true);
+ this.level.updateAroundRedstone(this.getSideVec(getFacing().getOpposite()), null);
if (!shouldBePowered) {
-// System.out.println("schedule update 2");
level.scheduleUpdate(getPowered(), this, this.getDelay());
}
}
@@ -78,13 +76,12 @@ public int onUpdate(int type) {
if (ev.isCancelled()) {
return 0;
}
- if (type == Level.BLOCK_UPDATE_NORMAL && this.getSide(BlockFace.DOWN).isTransparent()) {
+ if (type == Level.BLOCK_UPDATE_NORMAL && !canStayOnFullSolid(this.down())) {
this.level.useBreakOn(this);
- return Level.BLOCK_UPDATE_NORMAL;
} else {
this.updateState();
- return Level.BLOCK_UPDATE_NORMAL;
}
+ return Level.BLOCK_UPDATE_NORMAL;
}
return 0;
}
@@ -113,7 +110,7 @@ public boolean isLocked() {
protected int calculateInputStrength() {
BlockFace face = getFacing();
- Vector3 pos = this.getLocation().getSide(face);
+ Vector3 pos = this.getSideVec(face);
int power = this.level.getRedstonePower(pos, face);
if (power >= 15) {
@@ -125,12 +122,10 @@ protected int calculateInputStrength() {
}
protected int getPowerOnSides() {
- Vector3 pos = getLocation();
-
BlockFace face = getFacing();
BlockFace face1 = face.rotateY();
BlockFace face2 = face.rotateYCCW();
- return Math.max(this.getPowerOnSide(pos.getSide(face1), face1), this.getPowerOnSide(pos.getSide(face2), face2));
+ return Math.max(this.getPowerOnSide(this.getSideVec(face1), face1), this.getPowerOnSide(this.getSideVec(face2), face2));
}
protected int getPowerOnSide(Vector3 pos, BlockFace side) {
@@ -202,11 +197,26 @@ public boolean isFacingTowardsRepeater() {
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
@Override
public BlockColor getColor() {
return BlockColor.AIR_BLOCK_COLOR;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java b/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java
index d812b27b55a..b26d471f725 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneLamp.java
@@ -14,9 +14,6 @@
*/
public class BlockRedstoneLamp extends BlockSolid {
- public BlockRedstoneLamp() {
- }
-
@Override
public String getName() {
return "Redstone Lamp";
@@ -44,8 +41,8 @@ public int getToolType() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (this.level.isBlockPowered(this.getLocation())) {
- this.level.setBlock(this, Block.get(BlockID.LIT_REDSTONE_LAMP), false, true);
+ if (this.level.isBlockPowered(this)) {
+ this.level.setBlock(this, Block.get(LIT_REDSTONE_LAMP), false, true);
} else {
this.level.setBlock(this, this, false, true);
}
@@ -61,8 +58,8 @@ public int onUpdate(int type) {
if (ev.isCancelled()) {
return 0;
}
- if (this.level.isBlockPowered(this.getLocation())) {
- this.level.setBlock(this, Block.get(BlockID.LIT_REDSTONE_LAMP), false, false);
+ if (this.level.isBlockPowered(this)) {
+ this.level.setBlock(this, Block.get(LIT_REDSTONE_LAMP), false, false);
return 1;
}
}
@@ -73,7 +70,7 @@ public int onUpdate(int type) {
@Override
public Item[] getDrops(Item item) {
return new Item[]{
- new ItemBlock(Block.get(BlockID.REDSTONE_LAMP))
+ new ItemBlock(Block.get(REDSTONE_LAMP))
};
}
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java b/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java
index 876e847bb82..7d547700129 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneLampLit.java
@@ -10,9 +10,6 @@
*/
public class BlockRedstoneLampLit extends BlockRedstoneLamp {
- public BlockRedstoneLampLit() {
- }
-
@Override
public String getName() {
return "Lit Redstone Lamp";
@@ -30,13 +27,12 @@ public int getLightLevel() {
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.REDSTONE_LAMP));
+ return new ItemBlock(Block.get(REDSTONE_LAMP));
}
@Override
public int onUpdate(int type) {
- if ((type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) && !this.level.isBlockPowered(this.getLocation())) {
- // Redstone event
+ if ((type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) && !this.level.isBlockPowered(this)) {
RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this);
getLevel().getServer().getPluginManager().callEvent(ev);
if (ev.isCancelled()) {
@@ -46,8 +42,8 @@ public int onUpdate(int type) {
return 1;
}
- if (type == Level.BLOCK_UPDATE_SCHEDULED && !this.level.isBlockPowered(this.getLocation())) {
- this.level.setBlock(this, Block.get(BlockID.REDSTONE_LAMP), false, false);
+ if (type == Level.BLOCK_UPDATE_SCHEDULED && !this.level.isBlockPowered(this)) {
+ this.level.setBlock(this, Block.get(REDSTONE_LAMP), false, false);
}
return 0;
}
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterPowered.java b/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterPowered.java
index 18bac5eccde..c02a7ecca2a 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterPowered.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterPowered.java
@@ -2,7 +2,6 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemRedstoneRepeater;
import cn.nukkit.math.BlockFace;
/**
@@ -41,12 +40,12 @@ protected boolean isAlternateInput(Block block) {
@Override
public Item toItem() {
- return new ItemRedstoneRepeater();
+ return Item.get(Item.REPEATER);
}
@Override
protected int getDelay() {
- return (1 + (getDamage() >> 2)) * 2;
+ return (1 + (getDamage() >> 2)) << 1;
}
@Override
@@ -56,7 +55,7 @@ protected Block getPowered() {
@Override
protected Block getUnpowered() {
- return Block.get(BlockID.UNPOWERED_REPEATER, this.getDamage());
+ return Block.get(UNPOWERED_REPEATER, this.getDamage());
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterUnpowered.java b/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterUnpowered.java
index 47e376018c1..81950f605f1 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterUnpowered.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneRepeaterUnpowered.java
@@ -2,7 +2,6 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemRedstoneRepeater;
import cn.nukkit.math.BlockFace;
/**
@@ -50,17 +49,17 @@ protected boolean isAlternateInput(Block block) {
@Override
public Item toItem() {
- return new ItemRedstoneRepeater();
+ return Item.get(Item.REPEATER);
}
@Override
protected int getDelay() {
- return (1 + (getDamage() >> 2)) * 2;
+ return (1 + (getDamage() >> 2)) << 1;
}
@Override
protected Block getPowered() {
- return Block.get(BlockID.POWERED_REPEATER, this.getDamage());
+ return Block.get(POWERED_REPEATER, this.getDamage());
}
@Override
@@ -72,4 +71,4 @@ protected Block getUnpowered() {
public boolean isLocked() {
return this.getPowerOnSides() > 0;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java b/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java
index 4619dd35f48..e832da6faa7 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneTorch.java
@@ -5,14 +5,14 @@
import cn.nukkit.item.Item;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.Vector3;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Faceable;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
-public class BlockRedstoneTorch extends BlockTorch {
+public class BlockRedstoneTorch extends BlockTorch implements Faceable {
public BlockRedstoneTorch() {
this(0);
@@ -43,19 +43,6 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
return false;
}
-// if (!checkState()) {
-// BlockFace facing = getFacing().getOpposite();
-// Vector3 pos = getLocation();
-//
-// for (BlockFace side : BlockFace.values()) {
-// if (facing == side) {
-// continue;
-// }
-//
-// this.level.updateAround(pos.getSide(side));
-// }
-// }
-
checkState();
return true;
@@ -75,8 +62,6 @@ public int getStrongPower(BlockFace side) {
public boolean onBreak(Item item) {
super.onBreak(item);
- Vector3 pos = getLocation();
-
BlockFace face = getBlockFace().getOpposite();
for (BlockFace side : BlockFace.values()) {
@@ -84,7 +69,7 @@ public boolean onBreak(Item item) {
continue;
}
- this.level.updateAroundRedstone(pos.getSide(side), null);
+ this.level.updateAroundRedstone(this.getSideVec(side), null);
}
return true;
}
@@ -114,16 +99,15 @@ public int onUpdate(int type) {
protected boolean checkState() {
if (isPoweredFromSide()) {
BlockFace face = getBlockFace().getOpposite();
- Vector3 pos = getLocation();
- this.level.setBlock(pos, Block.get(BlockID.UNLIT_REDSTONE_TORCH, getDamage()), false, true);
+ this.level.setBlock(this, Block.get(UNLIT_REDSTONE_TORCH, getDamage()), false, true);
for (BlockFace side : BlockFace.values()) {
if (side == face) {
continue;
}
- this.level.updateAroundRedstone(pos.getSide(side), null);
+ this.level.updateAroundRedstone(this.getSideVec(side), null);
}
return true;
@@ -134,10 +118,9 @@ protected boolean checkState() {
protected boolean isPoweredFromSide() {
BlockFace face = getBlockFace().getOpposite();
- return this.level.isSidePowered(this.getLocation().getSide(face), face);
+ return this.level.isSidePowered(this.getSideVec(face), face);
}
-
- @Override
+ @Override
public int tickRate() {
return 2;
}
@@ -151,4 +134,4 @@ public boolean isPowerSource() {
public BlockColor getColor() {
return BlockColor.AIR_BLOCK_COLOR;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java b/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java
index eb0c46d6de4..64231974802 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneTorchUnlit.java
@@ -5,7 +5,6 @@
import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.Vector3;
/**
* Created by CreeperFace on 10.4.2017.
@@ -35,19 +34,9 @@ public int getLightLevel() {
return 0;
}
- @Override
- public int getWeakPower(BlockFace side) {
- return 0;
- }
-
- @Override
- public int getStrongPower(BlockFace side) {
- return 0;
- }
-
@Override
public Item toItem() {
- return new ItemBlock(Block.get(BlockID.REDSTONE_TORCH));
+ return new ItemBlock(Block.get(REDSTONE_TORCH));
}
@Override
@@ -73,17 +62,16 @@ public int onUpdate(int type) {
protected boolean checkState() {
BlockFace face = getBlockFace().getOpposite();
- Vector3 pos = getLocation();
- if (!this.level.isSidePowered(pos.getSide(face), face)) {
- this.level.setBlock(pos, Block.get(BlockID.REDSTONE_TORCH, getDamage()), false, true);
+ if (!this.level.isSidePowered(this.getSideVec(face), face)) {
+ this.level.setBlock(this, Block.get(REDSTONE_TORCH, getDamage()), false, true);
for (BlockFace side : BlockFace.values()) {
if (side == face) {
continue;
}
- this.level.updateAroundRedstone(pos.getSide(side), null);
+ this.level.updateAroundRedstone(this.getSideVec(side), null);
}
return true;
}
@@ -95,4 +83,4 @@ protected boolean checkState() {
public int tickRate() {
return 2;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java
index 06f26de3e5f..def1f0f126f 100644
--- a/src/main/java/cn/nukkit/block/BlockRedstoneWire.java
+++ b/src/main/java/cn/nukkit/block/BlockRedstoneWire.java
@@ -4,7 +4,6 @@
import cn.nukkit.event.block.BlockRedstoneEvent;
import cn.nukkit.event.redstone.RedstoneUpdateEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemRedstone;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.BlockFace.Plane;
@@ -14,7 +13,7 @@
import java.util.EnumSet;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockRedstoneWire extends BlockFlowable {
@@ -41,51 +40,48 @@ public int getId() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (!canBePlacedOn(block.down())) {
+ if (block instanceof BlockWater) {
+ return false;
+ }
+
+ if (!canStayOnFullSolid(block.down())) {
return false;
}
this.getLevel().setBlock(block, this, true, false);
- this.updateSurroundingRedstone(true);
- Vector3 pos = getLocation();
+ this.calculateCurrentChanges(true);
for (BlockFace blockFace : Plane.VERTICAL) {
- this.level.updateAroundRedstone(pos.getSide(blockFace), blockFace.getOpposite());
+ this.level.updateAroundRedstone(this.getSideVec(blockFace), blockFace.getOpposite());
}
for (BlockFace blockFace : Plane.VERTICAL) {
- this.updateAround(pos.getSide(blockFace), blockFace.getOpposite());
+ this.updateAround(this.getSideVec(blockFace), blockFace.getOpposite());
}
for (BlockFace blockFace : Plane.HORIZONTAL) {
- Vector3 v = pos.getSide(blockFace);
+ Vector3 v = this.getSideVec(blockFace);
if (this.level.getBlock(v).isNormalBlock()) {
- this.updateAround(v.up(), BlockFace.DOWN);
+ this.updateAround(v.getSideVec(BlockFace.UP), BlockFace.DOWN);
} else {
- this.updateAround(v.down(), BlockFace.UP);
+ this.updateAround(v.getSideVec(BlockFace.DOWN), BlockFace.UP);
}
}
return true;
}
private void updateAround(Vector3 pos, BlockFace face) {
- if (this.level.getBlock(pos).getId() == Block.REDSTONE_WIRE) {
+ if (this.level.getBlockIdAt((int) pos.x, (int) pos.y, (int) pos.z) == Block.REDSTONE_WIRE) {
this.level.updateAroundRedstone(pos, face);
for (BlockFace side : BlockFace.values()) {
- this.level.updateAroundRedstone(pos.getSide(side), side.getOpposite());
+ this.level.updateAroundRedstone(pos.getSideVec(side), side.getOpposite());
}
}
}
- private void updateSurroundingRedstone(boolean force) {
- this.calculateCurrentChanges(force);
- }
-
private void calculateCurrentChanges(boolean force) {
- Vector3 pos = this.getLocation();
-
int meta = this.getDamage();
int maxStrength = meta;
this.canProvidePower = false;
@@ -100,7 +96,7 @@ private void calculateCurrentChanges(boolean force) {
int strength = 0;
for (BlockFace face : Plane.HORIZONTAL) {
- Vector3 v = pos.getSide(face);
+ Vector3 v = this.getSideVec(face);
if (v.getX() == this.getX() && v.getZ() == this.getZ()) {
continue;
@@ -111,10 +107,10 @@ private void calculateCurrentChanges(boolean force) {
boolean vNormal = this.level.getBlock(v).isNormalBlock();
- if (vNormal && !this.level.getBlock(pos.up()).isNormalBlock()) {
- strength = this.getMaxCurrentStrength(v.up(), strength);
+ if (vNormal && !this.level.getBlock(this.getSideVec(BlockFace.UP)).isNormalBlock()) {
+ strength = this.getMaxCurrentStrength(v.getSideVec(BlockFace.UP), strength);
} else if (!vNormal) {
- strength = this.getMaxCurrentStrength(v.down(), strength);
+ strength = this.getMaxCurrentStrength(v.getSideVec(BlockFace.DOWN), strength);
}
}
@@ -140,17 +136,17 @@ private void calculateCurrentChanges(boolean force) {
this.level.updateAroundRedstone(this, null);
for (BlockFace face : BlockFace.values()) {
- this.level.updateAroundRedstone(pos.getSide(face), face.getOpposite());
+ this.level.updateAroundRedstone(this.getSideVec(face), face.getOpposite());
}
} else if (force) {
for (BlockFace face : BlockFace.values()) {
- this.level.updateAroundRedstone(pos.getSide(face), face.getOpposite());
+ this.level.updateAroundRedstone(this.getSideVec(face), face.getOpposite());
}
}
}
private int getMaxCurrentStrength(Vector3 pos, int maxStrength) {
- if (this.level.getBlockIdAt(pos.getFloorX(), pos.getFloorY(), pos.getFloorZ()) != this.getId()) {
+ if (this.level.getBlockIdAt(pos.getFloorX(), pos.getFloorY(), pos.getFloorZ()) != REDSTONE_WIRE) {
return maxStrength;
} else {
int strength = this.level.getBlockDataAt(pos.getFloorX(), pos.getFloorY(), pos.getFloorZ());
@@ -162,20 +158,19 @@ private int getMaxCurrentStrength(Vector3 pos, int maxStrength) {
public boolean onBreak(Item item) {
this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, true);
- Vector3 pos = getLocation();
+ this.level.updateAroundRedstone(this, null);
- this.level.updateAroundRedstone(pos, null);
for (BlockFace blockFace : BlockFace.values()) {
- this.level.updateAroundRedstone(pos.getSide(blockFace), null);
+ this.level.updateAroundRedstone(this.getSideVec(blockFace), null);
}
for (BlockFace blockFace : Plane.HORIZONTAL) {
- Vector3 v = pos.getSide(blockFace);
+ Vector3 v = this.getSideVec(blockFace);
if (this.level.getBlock(v).isNormalBlock()) {
- this.updateAround(v.up(), BlockFace.DOWN);
+ this.updateAround(v.getSideVec(BlockFace.UP), BlockFace.DOWN);
} else {
- this.updateAround(v.down(), BlockFace.UP);
+ this.updateAround(v.getSideVec(BlockFace.DOWN), BlockFace.UP);
}
}
return true;
@@ -183,7 +178,7 @@ public boolean onBreak(Item item) {
@Override
public Item toItem() {
- return new ItemRedstone();
+ return Item.get(Item.REDSTONE_DUST);
}
@Override
@@ -196,6 +191,12 @@ public int onUpdate(int type) {
if (type != Level.BLOCK_UPDATE_NORMAL && type != Level.BLOCK_UPDATE_REDSTONE) {
return 0;
}
+
+ if (type == Level.BLOCK_UPDATE_NORMAL && !canStayOnFullSolid(this.down())) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+
// Redstone event
RedstoneUpdateEvent ev = new RedstoneUpdateEvent(this);
getLevel().getServer().getPluginManager().callEvent(ev);
@@ -203,22 +204,18 @@ public int onUpdate(int type) {
return 0;
}
- if (type == Level.BLOCK_UPDATE_NORMAL && !this.canBePlacedOn(this.down())) {
- this.getLevel().useBreakOn(this);
- return Level.BLOCK_UPDATE_NORMAL;
+ // Make sure the block still exists to prevent item duplication
+ if (this.level.getBlockIdAt((int) this.x, (int) this.y, (int) this.z) != this.getId()) {
+ return 0;
}
- this.updateSurroundingRedstone(false);
+ this.calculateCurrentChanges(false);
- return Level.BLOCK_UPDATE_NORMAL;
+ return Level.BLOCK_UPDATE_REDSTONE;
}
public boolean canBePlacedOn(Vector3 v) {
- return this.canBePlacedOn(this.level.getBlock(v));
- }
-
- private boolean canBePlacedOn(Block b) {
- return (b.isSolid() && !b.isTransparent() && b.getId() != GLOWSTONE) || b.getId() == HOPPER_BLOCK;
+ return canStayOnFullSolid(this.level.getBlock(v));
}
public int getStrongPower(BlockFace side) {
@@ -256,16 +253,10 @@ public int getWeakPower(BlockFace side) {
}
private boolean isPowerSourceAt(BlockFace side) {
- Vector3 pos = getLocation();
- Vector3 v = pos.getSide(side);
- Block block = this.level.getBlock(v);
- boolean flag = block.isNormalBlock();
- boolean flag1 = this.level.getBlock(pos.up()).isNormalBlock();
- return !flag1 && flag && canConnectUpwardsTo(this.level, v.up()) || (canConnectTo(block, side) || !flag && canConnectUpwardsTo(this.level, block.down()));
- }
-
- protected static boolean canConnectUpwardsTo(Level level, Vector3 pos) {
- return canConnectUpwardsTo(level.getBlock(pos));
+ Block sideBlock = this.getSide(side);
+ boolean sideBlockIsNormal = sideBlock.isNormalBlock();
+ return (sideBlockIsNormal && !this.up().isNormalBlock() && canConnectUpwardsTo(sideBlock.up())) ||
+ (canConnectTo(sideBlock, side) || (!sideBlockIsNormal && canConnectUpwardsTo(sideBlock.down())));
}
protected static boolean canConnectUpwardsTo(Block block) {
@@ -290,10 +281,9 @@ public boolean isPowerSource() {
private int getIndirectPower() {
int power = 0;
- Vector3 pos = getLocation();
for (BlockFace face : BlockFace.values()) {
- int blockPower = this.getIndirectPower(pos.getSide(face), face);
+ int blockPower = this.getIndirectPower(this.getSideVec(face), face);
if (blockPower >= 15) {
return 15;
@@ -312,7 +302,7 @@ private int getIndirectPower(Vector3 pos, BlockFace face) {
if (block.getId() == Block.REDSTONE_WIRE) {
return 0;
}
- return block.isNormalBlock() ? getStrongPower(pos.getSide(face), face) : block.getWeakPower(face);
+ return block.isNormalBlock() ? getStrongPower(pos.getSideVec(face), face) : block.getWeakPower(face);
}
private int getStrongPower(Vector3 pos, BlockFace direction) {
@@ -324,4 +314,9 @@ private int getStrongPower(Vector3 pos, BlockFace direction) {
return block.getStrongPower(direction);
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockRespawnAnchor.java b/src/main/java/cn/nukkit/block/BlockRespawnAnchor.java
new file mode 100644
index 00000000000..0d9f3c10746
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockRespawnAnchor.java
@@ -0,0 +1,151 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Explosion;
+import cn.nukkit.level.GameRule;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.Vector3;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+
+public class BlockRespawnAnchor extends BlockSolidMeta {
+
+ public BlockRespawnAnchor() {
+ this(0);
+ }
+
+ public BlockRespawnAnchor(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return RESPAWN_ANCHOR;
+ }
+
+ @Override
+ public double getHardness() {
+ return 50;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1200;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_DIAMOND) {
+ return new Item[]{
+ toItem()
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public int getLightLevel() {
+ switch (this.getDamage()) {
+ case 0:
+ return 0;
+ case 1:
+ return 3;
+ case 2:
+ return 7;
+ default:
+ return 15;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "Respawn Anchor";
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ int chargeLevel = this.getDamage();
+
+ if (item.getId() == GLOWSTONE && chargeLevel < 4) {
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+
+ this.setDamage(chargeLevel + 1);
+ this.getLevel().setBlock(this, this, true);
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_RESPAWN_ANCHOR_CHARGE);
+ return true;
+ }
+
+ if (chargeLevel > 0 && this.level.getDimension() != Level.DIMENSION_NETHER) {
+ if (this.level.getGameRules().getBoolean(GameRule.RESPAWN_BLOCKS_EXPLODE)) {
+ this.getLevel().setBlock(this, Block.get(BlockID.AIR), true, true);
+
+ Explosion explosion = new Explosion(this.add(0.5, 0, 0.5), 5, this);
+ explosion.explodeA();
+ explosion.explodeB();
+ }
+ return false;
+ }
+
+ if (player != null && chargeLevel > 0 && this.level.getDimension() == Level.DIMENSION_NETHER) {
+ if (player.distanceSquared(this) > 36) {
+ return false;
+ }
+
+ if (!this.equals(player.getSpawnPosition())) {
+ player.setSpawn(this);
+
+ player.sendMessage("§7%tile.respawn_anchor.respawnSet", true);
+
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_RESPAWN_ANCHOR_SET_SPAWN);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onBreak(Item item) {
+ boolean r = super.onBreak(item);
+ if (r) {
+ if (level.getDimension() == Level.DIMENSION_NETHER) {
+ Vector3 safeSpawn = null;
+ for (Player player : level.getServer().getOnlinePlayers().values()) {
+ if (this.equals(player.getSpawnPosition())) {
+ player.setSpawn(safeSpawn == null ? (safeSpawn = level.getServer().getDefaultLevel().getSafeSpawn()) : safeSpawn);
+ }
+ }
+ }
+ }
+ return r;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockRoots.java b/src/main/java/cn/nukkit/block/BlockRoots.java
new file mode 100644
index 00000000000..e929d59b999
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockRoots.java
@@ -0,0 +1,53 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+
+public abstract class BlockRoots extends BlockFlowable {
+
+ protected BlockRoots() {
+ super(0);
+ }
+
+ protected BlockRoots(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL && !isSupportValid()) {
+ level.useBreakOn(this);
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ return this.isSupportValid() && super.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ protected boolean isSupportValid() {
+ switch (this.down().getId()) {
+ case BlockID.GRASS:
+ case BlockID.DIRT:
+ case BlockID.PODZOL:
+ case BlockID.FARMLAND:
+ case BlockID.CRIMSON_NYLIUM:
+ case BlockID.WARPED_NYLIUM:
+ case BlockID.MYCELIUM:
+ case BlockID.SOUL_SOIL:
+ case BlockID.ROOTED_DIRT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 5;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockRootsHanging.java b/src/main/java/cn/nukkit/block/BlockRootsHanging.java
new file mode 100644
index 00000000000..2eff0d9b432
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockRootsHanging.java
@@ -0,0 +1,28 @@
+package cn.nukkit.block;
+
+public class BlockRootsHanging extends BlockRoots {
+
+ public BlockRootsHanging() {
+ this(0);
+ }
+
+ public BlockRootsHanging(int meta) {
+ super(0); // hanging roots have no variants
+ }
+
+ @Override
+ protected boolean isSupportValid() {
+ Block up = this.up();
+ return up.isSolid() && !up.isTransparent();
+ }
+
+ @Override
+ public String getName() {
+ return "Hanging Roots";
+ }
+
+ @Override
+ public int getId() {
+ return HANGING_ROOTS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSand.java b/src/main/java/cn/nukkit/block/BlockSand.java
index 45791d253c9..574675c2a17 100644
--- a/src/main/java/cn/nukkit/block/BlockSand.java
+++ b/src/main/java/cn/nukkit/block/BlockSand.java
@@ -1,40 +1,28 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.generator.object.ObjectTallGrass;
+import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
-public class BlockSand extends BlockFallable {
+public class BlockSand extends BlockFallableMeta {
public static final int DEFAULT = 0;
public static final int RED = 1;
- private int meta;
-
public BlockSand() {
this(0);
}
public BlockSand(int meta) {
- this.meta = meta;
- }
-
- @Override
- public int getFullId() {
- return (getId() << 4) + getDamage();
- }
-
- @Override
- public final int getDamage() {
- return this.meta;
- }
-
- @Override
- public final void setDamage(int meta) {
- this.meta = meta;
+ super(meta);
}
@Override
@@ -74,4 +62,28 @@ public BlockColor getColor() {
return BlockColor.SAND_BLOCK_COLOR;
}
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player != null && item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ Block up = this.up();
+ if (up instanceof BlockWater) {
+ if (!player.isCreative()) {
+ item.count--;
+ }
+ this.level.addParticle(new BoneMealParticle(this));
+ if (up.getDamage() == 0 && up.up() instanceof BlockWater) {
+ ObjectTallGrass.growSeagrass(this.getLevel(), this);
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockSandstone.java b/src/main/java/cn/nukkit/block/BlockSandstone.java
index 2fef1655c9e..b4f0829839d 100644
--- a/src/main/java/cn/nukkit/block/BlockSandstone.java
+++ b/src/main/java/cn/nukkit/block/BlockSandstone.java
@@ -6,10 +6,11 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockSandstone extends BlockSolidMeta {
+
public static final int NORMAL = 0;
public static final int CHISELED = 1;
public static final int SMOOTH = 2;
@@ -39,7 +40,7 @@ public double getResistance() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Sandstone",
"Chiseled Sandstone",
"Smooth Sandstone",
@@ -51,7 +52,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockSapling.java b/src/main/java/cn/nukkit/block/BlockSapling.java
index a010867cb0e..cfc0182dbef 100644
--- a/src/main/java/cn/nukkit/block/BlockSapling.java
+++ b/src/main/java/cn/nukkit/block/BlockSapling.java
@@ -3,6 +3,8 @@
import cn.nukkit.Player;
import cn.nukkit.event.level.StructureGrowEvent;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.level.ChunkManager;
import cn.nukkit.level.Level;
import cn.nukkit.level.ListChunkManager;
import cn.nukkit.level.generator.object.BasicGenerator;
@@ -13,27 +15,23 @@
import cn.nukkit.math.Vector2;
import cn.nukkit.math.Vector3;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockSapling extends BlockFlowable {
+
public static final int OAK = 0;
public static final int SPRUCE = 1;
public static final int BIRCH = 2;
- /**
- * placeholder
- */
- public static final int BIRCH_TALL = 8 | BIRCH;
public static final int JUNGLE = 3;
public static final int ACACIA = 4;
public static final int DARK_OAK = 5;
+ public static final int BIRCH_TALL = 10;
public BlockSapling() {
this(0);
@@ -50,7 +48,7 @@ public int getId() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Oak Sapling",
"Spruce Sapling",
"Birch Sapling",
@@ -81,8 +79,8 @@ public boolean canBeActivated() {
}
public boolean onActivate(Item item, Player player) {
- if (item.getId() == Item.DYE && item.getDamage() == 0x0F) { //BoneMeal
- if (player != null && (player.gamemode & 0x01) == 0) {
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
+ if (player != null && !player.isCreative()) {
item.count--;
}
@@ -91,42 +89,19 @@ public boolean onActivate(Item item, Player player) {
return true;
}
- this.grow();
-
- return true;
+ return growTreeHere();
}
return false;
}
- public int onUpdate(int type) {
- if (type == Level.BLOCK_UPDATE_NORMAL) {
- if (this.down().isTransparent()) {
- this.getLevel().useBreakOn(this);
- return Level.BLOCK_UPDATE_NORMAL;
- }
- } else if (type == Level.BLOCK_UPDATE_RANDOM) { //Growth
- if (ThreadLocalRandom.current().nextInt(1, 8) == 1) {
- if ((this.getDamage() & 0x08) == 0x08) {
- this.grow();
- } else {
- this.setDamage(this.getDamage() | 0x08);
- this.getLevel().setBlock(this, this, true);
- return Level.BLOCK_UPDATE_RANDOM;
- }
- } else {
- return Level.BLOCK_UPDATE_RANDOM;
- }
- }
- return Level.BLOCK_UPDATE_NORMAL;
- }
-
- private void grow() {
+ private boolean growTreeHere() {
BasicGenerator generator = null;
boolean bigTree = false;
- Vector3 vector3 = new Vector3();
+ Vector3 vector3 = new Vector3(this.x, this.y - 1, this.z);
- switch (this.getDamage() & 0x07) {
+ int woodType = this.getDamage() & 0x7;
+ switch (woodType) {
case JUNGLE:
Vector2 vector2;
if ((vector2 = this.findSaplings(JUNGLE)) != null) {
@@ -137,12 +112,12 @@ private void grow() {
if (!bigTree) {
generator = new NewJungleTree(4, 7);
- vector3 = this.add(0,0,0);
+ vector3 = this.add(0, 0, 0);
}
break;
case ACACIA:
generator = new ObjectSavannaTree();
- vector3 = this.add(0,0,0);
+ vector3 = this.add(0, 0, 0);
break;
case DARK_OAK:
if ((vector2 = this.findSaplings(DARK_OAK)) != null) {
@@ -152,37 +127,58 @@ private void grow() {
}
if (!bigTree) {
- return;
+ return false;
}
break;
- //TODO: big spruce
+ case SPRUCE:
+ if ((vector2 = this.findSaplings(SPRUCE)) != null) {
+ vector3 = this.add(vector2.getFloorX(), 0, vector2.getFloorY());
+ generator = new HugeTreesGenerator(0, 0, null, null) {
+ @Override
+ public boolean generate(ChunkManager level, NukkitRandom rand, Vector3 position) {
+ ObjectBigSpruceTree object = new ObjectBigSpruceTree(0.5f, 4, true);
+ if (!this.ensureGrowable(level, position, object.getTreeHeight())) {
+ return false;
+ }
+ object.placeObject(level, position.getFloorX(), position.getFloorY(), position.getFloorZ(), rand);
+ return true;
+ }
+ };
+ bigTree = true;
+ }
+
+ if (bigTree) {
+ break;
+ }
default:
ListChunkManager chunkManager = new ListChunkManager(this.level);
- ObjectTree.growTree(chunkManager, this.getFloorX(), this.getFloorY(), this.getFloorZ(), new NukkitRandom(), this.getDamage() & 0x07);
+ ObjectTree.growTree(chunkManager, this.getFloorX(), this.getFloorY(), this.getFloorZ(), new NukkitRandom(), woodType);
StructureGrowEvent ev = new StructureGrowEvent(this, chunkManager.getBlocks());
this.level.getServer().getPluginManager().callEvent(ev);
if (ev.isCancelled()) {
- return;
+ return false;
}
- for(Block block : ev.getBlockList()) {
- this.level.setBlockAt(block.getFloorX(), block.getFloorY(), block.getFloorZ(), block.getId(), block.getDamage());
+
+ for (Block block : ev.getBlockList()) {
+ this.level.setBlock(block, block);
}
- return;
+ return true;
}
if (bigTree) {
- this.level.setBlock(vector3, get(AIR), true, false);
- this.level.setBlock(vector3.add(1, 0, 0), get(AIR), true, false);
- this.level.setBlock(vector3.add(0, 0, 1), get(AIR), true, false);
- this.level.setBlock(vector3.add(1, 0, 1), get(AIR), true, false);
+ this.level.setBlock(vector3, Block.get(AIR), true, false);
+ this.level.setBlock(vector3.add(1, 0, 0), Block.get(AIR), true, false);
+ this.level.setBlock(vector3.add(0, 0, 1), Block.get(AIR), true, false);
+ this.level.setBlock(vector3.add(1, 0, 1), Block.get(AIR), true, false);
} else {
- this.level.setBlock(this, get(AIR), true, false);
+ this.level.setBlock(this, Block.get(AIR), true, false);
}
ListChunkManager chunkManager = new ListChunkManager(this.level);
boolean success = generator.generate(chunkManager, new NukkitRandom(), vector3);
StructureGrowEvent ev = new StructureGrowEvent(this, chunkManager.getBlocks());
this.level.getServer().getPluginManager().callEvent(ev);
+
if (ev.isCancelled() || !success) {
if (bigTree) {
this.level.setBlock(vector3, this, true, false);
@@ -192,38 +188,35 @@ private void grow() {
} else {
this.level.setBlock(this, this, true, false);
}
- return;
+ return false;
}
- for(Block block : ev.getBlockList()) {
- this.level.setBlockAt(block.getFloorX(), block.getFloorY(), block.getFloorZ(), block.getId(), block.getDamage());
+
+ for (Block block : ev.getBlockList()) {
+ this.level.setBlock(block, block);
}
+ return true;
}
- private Vector2 findSaplings(int type) {
- List> validVectorsList = new ArrayList<>();
- validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1)));
- validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(-1, 0), new Vector2(0, -1), new Vector2(-1, -1)));
- validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, -1), new Vector2(1, -1)));
- validVectorsList.add(Arrays.asList(new Vector2(0, 0), new Vector2(-1, 0), new Vector2(0, 1), new Vector2(-1, 1)));
- for(List validVectors : validVectorsList) {
- boolean correct = true;
- for(Vector2 vector2 : validVectors) {
- if(!this.isSameType(this.add(vector2.x, 0, vector2.y), type))
- correct = false;
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (this.down().isTransparent()) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
}
- if(correct) {
- int lowestX = 0;
- int lowestZ = 0;
- for(Vector2 vector2 : validVectors) {
- if(vector2.getFloorX() < lowestX)
- lowestX = vector2.getFloorX();
- if(vector2.getFloorY() < lowestZ)
- lowestZ = vector2.getFloorY();
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) { //Growth
+ if (Utils.rand(1, 7) == 1) {
+ if ((this.getDamage() & 0x08) == 0x08) {
+ growTreeHere();
+ } else {
+ this.setDamage(this.getDamage() | 0x08);
+ this.getLevel().setBlock(this, this, true);
+ return Level.BLOCK_UPDATE_RANDOM;
}
- return new Vector2(lowestX, lowestZ);
+ } else {
+ return Level.BLOCK_UPDATE_RANDOM;
}
}
- return null;
+ return 1;
}
public boolean isSameType(Vector3 pos, int type) {
@@ -240,4 +233,45 @@ public Item toItem() {
public BlockColor getColor() {
return BlockColor.FOLIAGE_BLOCK_COLOR;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ private static final Vector2[][] VALID_SAPLINGS = new Vector2[4][4];
+ static {
+ VALID_SAPLINGS[0] = new Vector2[]{new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1)};
+ VALID_SAPLINGS[1] = new Vector2[]{new Vector2(0, 0), new Vector2(-1, 0), new Vector2(0, -1), new Vector2(-1, -1)};
+ VALID_SAPLINGS[2] = new Vector2[]{new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, -1), new Vector2(1, -1)};
+ VALID_SAPLINGS[3] = new Vector2[]{new Vector2(0, 0), new Vector2(-1, 0), new Vector2(0, 1), new Vector2(-1, 1)};
+ }
+
+ private Vector2 findSaplings(int type) {
+ for (Vector2[] validVectors : VALID_SAPLINGS) {
+ boolean found = true;
+
+ for (Vector2 vector2 : validVectors) {
+ if (!this.isSameType(this.add(vector2.x, 0, vector2.y), type)) {
+ found = false;
+ }
+ }
+
+ if (found) {
+ int lowestX = 0;
+ int lowestZ = 0;
+ for (Vector2 vector2 : validVectors) {
+ if (vector2.getFloorX() < lowestX) {
+ lowestX = vector2.getFloorX();
+ }
+ if (vector2.getFloorY() < lowestZ) {
+ lowestZ = vector2.getFloorY();
+ }
+ }
+ return new Vector2(lowestX, lowestZ);
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockScaffolding.java b/src/main/java/cn/nukkit/block/BlockScaffolding.java
new file mode 100644
index 00000000000..b105894b73e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockScaffolding.java
@@ -0,0 +1,256 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockScaffolding extends BlockFallableMeta {
+
+ public BlockScaffolding() {
+ this(0);
+ }
+
+ public BlockScaffolding(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Scaffolding";
+ }
+
+ @Override
+ public int getId() {
+ return SCAFFOLDING;
+ }
+
+ public int getStability() {
+ return this.getDamage() & 0x7;
+ }
+
+ public void setStability(int stability) {
+ this.setDamage(stability & 0x7 | (this.getDamage() & 0x8));
+ }
+
+ public boolean getStabilityCheck() {
+ return (this.getDamage() & 0x8) > 0;
+ }
+
+ public void setStabilityCheck(boolean check) {
+ if (check) {
+ this.setDamage(getDamage() | 0x8);
+ } else {
+ this.setDamage(getDamage() & 0x7);
+ }
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(SCAFFOLDING));
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (block instanceof BlockLava) {
+ return false;
+ }
+
+ Block down = this.down();
+ if (target.getId() != SCAFFOLDING && down.getId() != SCAFFOLDING && down.getId() != AIR && !down.isSolid()) {
+ boolean scaffoldOnSide = false;
+ for (int i = 0; i < 4; i++) {
+ BlockFace sideFace = BlockFace.fromHorizontalIndex(i);
+ if (sideFace != face) {
+ Block side = this.getSide(sideFace);
+ if (side.getId() == SCAFFOLDING) {
+ scaffoldOnSide = true;
+ break;
+ }
+ }
+ }
+
+ if (!scaffoldOnSide) {
+ return false;
+ }
+ }
+
+ this.setDamage(0x8);
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block down = this.down();
+ if (down.isSolid()) {
+ if (this.getDamage() != 0) {
+ this.setDamage(0);
+ this.getLevel().setBlock(this, this, true, true);
+ }
+ return type;
+ }
+
+ int stability = 7;
+ for (BlockFace face : BlockFace.values()) {
+ if (face == BlockFace.UP) {
+ continue;
+ }
+
+ Block otherBlock = this.getSide(face);
+ if (otherBlock.getId() == SCAFFOLDING) {
+ BlockScaffolding other = (BlockScaffolding) otherBlock;
+ int otherStability = other.getStability();
+ if (otherStability < stability) {
+ if (face == BlockFace.DOWN) {
+ stability = otherStability;
+ } else {
+ stability = otherStability + 1;
+ }
+ }
+ }
+ }
+
+ if (stability >= 7) {
+ if (this.getStabilityCheck()) {
+ super.onUpdate(type);
+ } else {
+ this.getLevel().scheduleUpdate(this, 0);
+ }
+ return type;
+ }
+
+ this.setStabilityCheck(false);
+ this.setStability(stability);
+ this.getLevel().setBlock(this, this, true, true);
+ return type;
+ } else if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.getLevel().useBreakOn(this);
+ return type;
+ }
+
+ return 0;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 60;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 60;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean canBeClimbed() {
+ return true;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ entity.resetFallDistance();
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public double getMinY() {
+ return this.y + 0.875;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return false;
+ }
+
+ @Override
+ public boolean isTransparent() {
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.TRANSPARENT_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getBlockUnsafe() instanceof BlockScaffolding) {
+ int top = (int) y;
+
+ for (int i = 1; i <= 16; i++) {
+ int id = this.level.getBlockIdAt(this.getFloorX(), this.getFloorY() - i, this.getFloorZ());
+ if (id != SCAFFOLDING) {
+ break;
+ }
+ }
+
+ for (int i = 1; i <= 16; i++) {
+ int id = this.level.getBlockIdAt(this.getFloorX(), this.getFloorY() + i, this.getFloorZ());
+ if (id == SCAFFOLDING) {
+ top++;
+ } else {
+ break;
+ }
+ }
+
+ boolean success = false;
+
+ Block block = this.up(top - (int) y + 1);
+ if (block.getId() == BlockID.AIR) {
+ success = this.level.setBlock(block, Block.get(SCAFFOLDING));
+ }
+
+ if (success) {
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSeaLantern.java b/src/main/java/cn/nukkit/block/BlockSeaLantern.java
index b1854ccac17..463f7ad8141 100644
--- a/src/main/java/cn/nukkit/block/BlockSeaLantern.java
+++ b/src/main/java/cn/nukkit/block/BlockSeaLantern.java
@@ -1,15 +1,11 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemPrismarineCrystals;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
-
+import cn.nukkit.utils.Utils;
public class BlockSeaLantern extends BlockTransparent {
- public BlockSeaLantern() {
- }
@Override
public String getName() {
@@ -38,8 +34,11 @@ public int getLightLevel() {
@Override
public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
return new Item[]{
- new ItemPrismarineCrystals(0, ThreadLocalRandom.current().nextInt(2, 4))
+ Item.get(Item.PRISMARINE_CRYSTALS, 0, Utils.random.nextInt(2, 4))
};
}
@@ -47,7 +46,7 @@ public Item[] getDrops(Item item) {
public BlockColor getColor() {
return BlockColor.QUARTZ_BLOCK_COLOR;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
diff --git a/src/main/java/cn/nukkit/block/BlockSeaPickle.java b/src/main/java/cn/nukkit/block/BlockSeaPickle.java
new file mode 100644
index 00000000000..47630a2e9eb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSeaPickle.java
@@ -0,0 +1,186 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.event.block.BlockFadeEvent;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.event.block.BlockSpreadEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.SimpleAxisAlignedBB;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockSeaPickle extends BlockTransparentMeta {
+
+ public BlockSeaPickle() {
+ this(0);
+ }
+
+ public BlockSeaPickle(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return SEA_PICKLE;
+ }
+
+ public double getHardness() {
+ return 0;
+ }
+
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public String getName() {
+ return "Sea Pickle";
+ }
+
+ public boolean isDead() {
+ return (getDamage() & 0x4) == 0x4;
+ }
+
+ public void setDead(boolean dead) {
+ if (dead) {
+ this.setDamage(getDamage() | 0x4);
+ } else {
+ this.setDamage(getDamage() ^ 0x4);
+ }
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block down = this.down();
+ if (!down.isSolid() || down.getId() == MAGMA) {
+ this.getLevel().useBreakOn(this);
+ return type;
+ }
+
+ Block layer1 = getLevelBlock(Block.LAYER_WATERLOGGED);
+ if (layer1 instanceof BlockWater) {
+ if (this.isDead() || layer1.getDamage() == 0 && layer1.getDamage() == 8) {
+ this.getLevel().useBreakOn(this);
+ return type;
+ }
+ } else if (!this.isDead()) {
+ BlockFadeEvent event = new BlockFadeEvent(this, Block.get(SEA_PICKLE, this.getDamage() ^ 0x4));
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(this, event.getNewState(), true, true);
+ }
+ }
+
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (target.getId() == SEA_PICKLE && (target.getDamage() & 0b11) < 3) {
+ target.setDamage(target.getDamage() + 1);
+ this.getLevel().setBlock(target, target, true, true);
+ return true;
+ }
+ if (!this.down().isTransparent()) {
+ Block layer1 = block.getLevelBlock(BlockLayer.WATERLOGGED);
+ if (layer1 instanceof BlockWater) {
+ if (layer1.getDamage() != 0 && layer1.getDamage() != 8) {
+ return false;
+ }
+
+ if (layer1.getDamage() == 8) {
+ this.getLevel().setBlock(block, BlockLayer.WATERLOGGED, Block.get(WATER), true, false);
+ }
+ } else {
+ this.setDead(true);
+ }
+ this.getLevel().setBlock(block, this, true, true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() != Item.DYE || item.getDamage() != ItemDye.BONE_MEAL) {
+ return super.onActivate(item, player);
+ }
+
+ BlockSeaPickle block = (BlockSeaPickle) this.clone();
+ if (!block.isDead()) {
+ block.setDamage(3);
+ }
+
+ BlockGrowEvent blockGrowEvent = new BlockGrowEvent(this, block);
+ this.level.getServer().getPluginManager().callEvent(blockGrowEvent);
+
+ if (blockGrowEvent.isCancelled()) {
+ return false;
+ }
+
+ this.getLevel().setBlock(this, blockGrowEvent.getNewState(), false, true);
+ this.level.addParticle(new BoneMealParticle(this));
+
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ Block[] blocksAround = this.getLevel().getCollisionBlocks(new SimpleAxisAlignedBB(x - 2, y - 2, z - 2, x + 3, y, z + 3));
+ for (Block blockNearby : blocksAround) {
+ if (blockNearby.getId() == CORAL_BLOCK) {
+ Block up = blockNearby.up();
+ if (up instanceof BlockWater && (up.getDamage() == 0 || up.getDamage() == 8) && random.nextInt(6) == 0) {
+ BlockSpreadEvent blockSpreadEvent = new BlockSpreadEvent(up, this, Block.get(SEA_PICKLE, random.nextInt(3)));
+ if (!blockSpreadEvent.isCancelled()) {
+ this.getLevel().setBlock(up, BlockLayer.WATERLOGGED, Block.get(WATER), true, false);
+ this.getLevel().setBlock(up, blockSpreadEvent.getNewState(), true, true);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(SEA_PICKLE), 0);
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[]{new ItemBlock(Block.get(SEA_PICKLE), 0, (this.getDamage() & 0x3) + 1)};
+ }
+
+ @Override
+ public int getLightLevel() {
+ if (this.isDead()) {
+ return 0;
+ } else {
+ return (this.getDamage() + 1) * 3;
+ }
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSeagrass.java b/src/main/java/cn/nukkit/block/BlockSeagrass.java
new file mode 100644
index 00000000000..ed7781fc642
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSeagrass.java
@@ -0,0 +1,178 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.*;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.format.anvil.Anvil;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.Vector3;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSeagrass extends BlockFlowable {
+
+ public BlockSeagrass() {
+ this(0);
+ }
+
+ public BlockSeagrass(int meta) {
+ super(meta % 3);
+ }
+
+ @Override
+ public String getName() {
+ return "Seagrass";
+ }
+
+ @Override
+ public int getId() {
+ return SEAGRASS;
+ }
+
+ public int getToolType() {
+ return ItemTool.SHEARS;
+ }
+
+ public double getHardness() {
+ return 0;
+ }
+
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (level != null && level.getProvider() instanceof Anvil) {
+ if (!(block instanceof BlockWater && block.getDamage() == 0) || this.down().isTransparent()) {
+ return false;
+ }
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+
+
+ Block down = this.down();
+ Block layer1Block = block.getLevelBlock(BlockLayer.WATERLOGGED);
+ int waterDamage;
+ if (down.isSolid() && down.getId() != MAGMA && down.getId() != SOUL_SAND &&
+ (layer1Block instanceof BlockWater && ((waterDamage = (block.getDamage())) == 0 || waterDamage == 8))
+ ) {
+ if (waterDamage == 8) {
+ this.getLevel().setBlock((int) this.x, (int) this.y, (int) this.z, Block.LAYER_WATERLOGGED, Block.get(Block.STILL_WATER), true, true);
+ }
+ this.getLevel().setBlock(this, BlockLayer.NORMAL, this, true, true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (level != null && level.getProvider() instanceof Anvil) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block down = this.down();
+ if (down.isTransparent() && down.getId() != SEAGRASS) {
+ this.getLevel().useBreakOn(this);
+ }
+ }
+ return type;
+ }
+
+
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block blockLayer1 = this.getLevelBlock(BlockLayer.WATERLOGGED);
+ int damage;
+ if (!(blockLayer1 instanceof BlockIceFrosted)
+ && (!(blockLayer1 instanceof BlockWater) || ((damage = blockLayer1.getDamage()) != 0 && damage != 8))) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+
+ Block down = this.down();
+ damage = this.getDamage();
+ if (damage == 0 || damage == 2) {
+ if (!down.isSolid() || down.getId() == MAGMA || down.getId() == SOUL_SAND) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+
+ if (damage == 2) {
+ Block up = up();
+ if (up.getId() != getId() || up.getDamage() != 1) {
+ this.getLevel().useBreakOn(this);
+ }
+ }
+ } else if (down.getId() != getId() || down.getDamage() != 2) {
+ this.getLevel().useBreakOn(this);
+ }
+
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+
+ return 0;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isShears()) {
+ return new Item[] { toItem() };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(SEAGRASS), 0, 1);
+ }
+
+ @Override
+ public void setDamage(int meta) {
+ super.setDamage(meta % 3);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return level == null || !(level.getProvider() instanceof Anvil);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WATER_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canBeReplaced() {
+ return true;
+ }
+
+
+ @Override
+ public boolean canBeActivated() {
+ return this.getDamage() == 0;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (this.getDamage() == 0 && item.getId() == ItemID.DYE && item.getDamage() == ItemDye.BONE_MEAL && up() instanceof BlockWater) {
+ Vector3 up = this.getSideVec(BlockFace.UP);
+ if (this.level.setBlock(up, Block.get(SEAGRASS, 1), true, true)) {
+ this.level.setBlock(this, Block.get(SEAGRASS, 2), true, true);
+ }
+
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+
+ this.level.addParticle(new BoneMealParticle(this));
+
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockShroomlight.java b/src/main/java/cn/nukkit/block/BlockShroomlight.java
new file mode 100644
index 00000000000..6934a70a81a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockShroomlight.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockShroomlight extends BlockSolid {
+
+ public BlockShroomlight() {
+ // Does nothing
+ }
+
+ @Override
+ public int getId() {
+ return SHROOMLIGHT;
+ }
+
+ @Override
+ public String getName() {
+ return "Shroomlight";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 15;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockShulkerBox.java b/src/main/java/cn/nukkit/block/BlockShulkerBox.java
index 046cda38896..09d76f8c362 100644
--- a/src/main/java/cn/nukkit/block/BlockShulkerBox.java
+++ b/src/main/java/cn/nukkit/block/BlockShulkerBox.java
@@ -1,22 +1,32 @@
package cn.nukkit.block;
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntityShulkerBox;
+import cn.nukkit.inventory.ShulkerBoxInventory;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.DyeColor;
-/**
- * Created by PetteriM1
- */
-public class BlockShulkerBox extends BlockUndyedShulkerBox {
-
- private int meta;
+public class BlockShulkerBox extends BlockTransparentMeta {
public BlockShulkerBox() {
this(0);
}
public BlockShulkerBox(int meta) {
- super();
- this.meta = meta;
+ super(meta);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
}
@Override
@@ -29,6 +39,108 @@ public String getName() {
return this.getDyeColor().getName() + " Shulker Box";
}
+ @Override
+ public double getHardness() {
+ return 0.6;
+ }
+
+ @Override
+ public double getResistance() {
+ return 2;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public Item toItem() {
+ ItemBlock item = new ItemBlock(this, this.getDamage(), 1);
+
+ BlockEntity be = this.getLevel().getBlockEntity(this);
+
+ if (be instanceof BlockEntityShulkerBox) {
+ BlockEntityShulkerBox t = (BlockEntityShulkerBox) be;
+
+ ShulkerBoxInventory i = t.getRealInventory();
+
+ if (!i.slots.isEmpty()) {
+
+ CompoundTag nbt = item.getNamedTag();
+ if (nbt == null)
+ nbt = new CompoundTag("");
+
+ ListTag items = new ListTag<>();
+
+ for (int it = 0; it < i.getSize(); it++) {
+ if (i.getItem(it).getId() != Item.AIR) {
+ CompoundTag d = NBTIO.putItemHelper(i.getItem(it), it);
+ items.add(d);
+ }
+ }
+
+ nbt.put("Items", items);
+
+ item.setCompoundTag(nbt);
+ }
+
+ if (t.hasName()) {
+ item.setCustomName(t.getName());
+ }
+ }
+
+ return item;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.getLevel().setBlock(this, this, true, true);
+
+ CompoundTag nbt = BlockEntity.getDefaultCompound(this, BlockEntity.SHULKER_BOX)
+ .putByte("facing", face.getIndex());
+
+ if (item.hasCustomName()) {
+ nbt.putString("CustomName", item.getCustomName());
+ }
+
+ CompoundTag t = item.getNamedTag();
+
+ if (t != null) {
+ if (t.contains("Items")) {
+ nbt.putList(t.getList("Items"));
+ }
+ }
+
+ BlockEntity.createBlockEntity(BlockEntity.SHULKER_BOX, this.getChunk(), nbt);
+ return true;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player != null) {
+ BlockEntity t = this.getLevel().getBlockEntity(this);
+ if (!(t instanceof BlockEntityShulkerBox)) {
+ return false;
+ }
+
+ BlockEntityShulkerBox box = (BlockEntityShulkerBox) t;
+ Block block = this.getSide(BlockFace.fromIndex(box.namedTag.getByte("facing")));
+ if (!(block instanceof BlockAir) && !(block instanceof BlockLiquid) && !(block instanceof BlockFlowable)) {
+ return true;
+ }
+
+ player.addWindow(box.getInventory());
+ }
+
+ return true;
+ }
+
@Override
public BlockColor getColor() {
return this.getDyeColor().getColor();
@@ -39,17 +151,17 @@ public DyeColor getDyeColor() {
}
@Override
- public int getFullId() {
- return (this.getId() << 4) + this.getDamage();
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
}
@Override
- public final int getDamage() {
- return this.meta;
+ public boolean breakWhenPushed() {
+ return true;
}
@Override
- public void setDamage(int meta) {
- this.meta = meta;
+ public boolean alwaysDropsOnExplosion() {
+ return true;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockSignPost.java b/src/main/java/cn/nukkit/block/BlockSignPost.java
index 496cb06b285..e0641e011e9 100644
--- a/src/main/java/cn/nukkit/block/BlockSignPost.java
+++ b/src/main/java/cn/nukkit/block/BlockSignPost.java
@@ -7,7 +7,6 @@
import cn.nukkit.event.block.SignGlowEvent;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemDye;
-import cn.nukkit.item.ItemSign;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
import cn.nukkit.math.AxisAlignedBB;
@@ -63,6 +62,14 @@ public AxisAlignedBB getBoundingBox() {
return null;
}
+ protected int getPostId() {
+ return SIGN_POST;
+ }
+
+ protected int getWallId() {
+ return WALL_SIGN;
+ }
+
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (face != BlockFace.DOWN) {
@@ -78,10 +85,13 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
if (face == BlockFace.UP) {
setDamage((int) Math.floor(((player.yaw + 180) * 16 / 360) + 0.5) & 0x0f);
- getLevel().setBlock(block, Block.get(BlockID.SIGN_POST, getDamage()), true);
+ getLevel().setBlock(block, Block.get(getPostId(), getDamage()), true);
+ } else if (target.canBeReplaced()) {
+ setDamage((int) Math.floor(((player.yaw + 180) * 16 / 360) + 0.5) & 0x0f);
+ getLevel().setBlock(target, Block.get(getPostId(), getDamage()), true);
} else {
setDamage(face.getIndex());
- getLevel().setBlock(block, Block.get(BlockID.WALL_SIGN, getDamage()), true);
+ getLevel().setBlock(block, Block.get(getWallId(), getDamage()), true);
}
if (player != null) {
@@ -94,7 +104,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- BlockEntitySign sign = (BlockEntitySign) BlockEntity.createBlockEntity(BlockEntity.SIGN, getLevel().getChunk((int) block.x >> 4, (int) block.z >> 4), nbt);
+ BlockEntity.createBlockEntity(BlockEntity.SIGN, this.getChunk(), nbt);
if (player != null) {
OpenSignPacket pk = new OpenSignPacket();
@@ -102,8 +112,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
pk.frontSide = true;
player.dataPacket(pk);
}
-
- return sign != null;
+ return true;
}
return false;
@@ -124,7 +133,7 @@ public int onUpdate(int type) {
@Override
public Item toItem() {
- return new ItemSign();
+ return Item.get(Item.SIGN);
}
@Override
@@ -150,7 +159,7 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
if (item.getId() == Item.DYE) {
- BlockEntity blockEntity = this.level.getBlockEntity(this);
+ BlockEntity blockEntity = this.level.getBlockEntityIfLoaded(player == null ? null : player.chunk, this);
if (!(blockEntity instanceof BlockEntitySign)) {
return false;
}
@@ -169,9 +178,9 @@ public boolean onActivate(Item item, Player player) {
SignGlowEvent event = new SignGlowEvent(this, player, glow);
this.level.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
- if (player != null) {
+ /*if (player != null) {
sign.spawnTo(player);
- }
+ }*/
return false;
}
@@ -189,9 +198,9 @@ public boolean onActivate(Item item, Player player) {
BlockColor color = DyeColor.getByDyeData(meta).getSignColor();
if (color.equals(sign.getColor())) {
- if (player != null) {
+ /*if (player != null) {
sign.spawnTo(player);
- }
+ }*/
return false;
}
@@ -217,4 +226,9 @@ public boolean onActivate(Item item, Player player) {
}
return false;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockSkull.java b/src/main/java/cn/nukkit/block/BlockSkull.java
index c11158ee98b..9672c1bab7e 100644
--- a/src/main/java/cn/nukkit/block/BlockSkull.java
+++ b/src/main/java/cn/nukkit/block/BlockSkull.java
@@ -15,7 +15,7 @@
import cn.nukkit.utils.Faceable;
/**
- * author: Justin
+ * @author Justin
*/
public class BlockSkull extends BlockTransparentMeta implements Faceable {
@@ -73,7 +73,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
default:
return false;
}
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
CompoundTag nbt = new CompoundTag()
.putString("id", BlockEntity.SKULL)
@@ -90,9 +90,6 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
BlockEntitySkull blockEntity = (BlockEntitySkull) BlockEntity.createBlockEntity(BlockEntity.SKULL, this.getChunk(), nbt);
blockEntity.spawnToAll();
-
- // TODO: 2016/2/3 SPAWN WITHER
-
return true;
}
@@ -123,7 +120,6 @@ public int getToolType() {
public BlockColor getColor() {
return BlockColor.AIR_BLOCK_COLOR;
}
-
@Override
public BlockFace getBlockFace() {
return BlockFace.fromIndex(this.getDamage() & 0x7);
@@ -144,4 +140,24 @@ protected AxisAlignedBB recalculateBoundingBox() {
}
return bb;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return true;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean alwaysDropsOnExplosion() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockSlab.java b/src/main/java/cn/nukkit/block/BlockSlab.java
index 64cfe2f1a9d..493eb093a07 100644
--- a/src/main/java/cn/nukkit/block/BlockSlab.java
+++ b/src/main/java/cn/nukkit/block/BlockSlab.java
@@ -2,11 +2,14 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.SimpleAxisAlignedBB;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockSlab extends BlockTransparentMeta {
@@ -19,13 +22,35 @@ public BlockSlab(int meta, int doubleSlab) {
}
@Override
- public double getMinY() {
- return ((this.getDamage() & 0x08) > 0) ? this.y + 0.5 : this.y;
+ protected AxisAlignedBB recalculateBoundingBox() {
+ if (this.hasTopBit()) {
+ return new SimpleAxisAlignedBB(
+ this.x,
+ this.y + 0.5,
+ this.z,
+ this.x + 1,
+ this.y + 1,
+ this.z + 1
+ );
+ } else {
+ return new SimpleAxisAlignedBB(
+ this.x,
+ this.y,
+ this.z,
+ this.x + 1,
+ this.y + 0.5,
+ this.z + 1
+ );
+ }
+ }
+
+ public String getSlabName() {
+ return "";
}
@Override
- public double getMaxY() {
- return ((this.getDamage() & 0x08) > 0) ? this.y + 1 : this.y + 0.5;
+ public String getName() {
+ return (this.hasTopBit()? "Upper " : "") + this.getSlabName() + " Slab";
}
@Override
@@ -42,30 +67,30 @@ public double getResistance() {
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
this.setDamage(this.getDamage() & 0x07);
if (face == BlockFace.DOWN) {
- if (target instanceof BlockSlab && (target.getDamage() & 0x08) == 0x08 && (target.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
+ if (target instanceof BlockSlab && ((BlockSlab) target).doubleSlab == this.doubleSlab && ((BlockSlab) target).hasTopBit() && (target.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
this.getLevel().setBlock(target, Block.get(doubleSlab, this.getDamage()), true);
return true;
- } else if (block instanceof BlockSlab && (block.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
+ } else if (block instanceof BlockSlab && ((BlockSlab) block).doubleSlab == this.doubleSlab && (block.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
this.getLevel().setBlock(block, Block.get(doubleSlab, this.getDamage()), true);
return true;
} else {
- this.setDamage(this.getDamage() | 0x08);
+ this.setTopBit(true);
}
} else if (face == BlockFace.UP) {
- if (target instanceof BlockSlab && (target.getDamage() & 0x08) == 0 && (target.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
+ if (target instanceof BlockSlab && ((BlockSlab) target).doubleSlab == this.doubleSlab && !((BlockSlab) target).hasTopBit() && (target.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
this.getLevel().setBlock(target, Block.get(doubleSlab, this.getDamage()), true);
return true;
- } else if (block instanceof BlockSlab && (block.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
+ } else if (block instanceof BlockSlab && ((BlockSlab) block).doubleSlab == this.doubleSlab && (block.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
this.getLevel().setBlock(block, Block.get(doubleSlab, this.getDamage()), true);
return true;
}
//TODO: check for collision
} else {
- if (block instanceof BlockSlab) {
+ if (block instanceof BlockSlab && ((BlockSlab) block).doubleSlab == this.doubleSlab) {
if ((block.getDamage() & 0x07) == (this.getDamage() & 0x07)) {
this.getLevel().setBlock(block, Block.get(doubleSlab, this.getDamage()), true);
@@ -75,16 +100,45 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
return false;
} else {
if (fy > 0.5) {
- this.setDamage(this.getDamage() | 0x08);
+ this.setTopBit(true);
}
}
}
- if (block instanceof BlockSlab && (target.getDamage() & 0x07) != (this.getDamage() & 0x07)) {
+ if (block instanceof BlockSlab && ((BlockSlab) block).doubleSlab == this.doubleSlab && (target.getDamage() & 0x07) != (this.getDamage() & 0x07)) {
return false;
}
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
-}
\ No newline at end of file
+
+ @Override
+ public boolean isTransparent() {
+ //HACK: Fix unable to place many blocks on slabs
+ return !this.hasTopBit();
+ }
+
+ public boolean hasTopBit() {
+ return (this.getDamage() & 0x08) > 0;
+ }
+
+ public void setTopBit(boolean topBit) {
+ if (topBit) {
+ this.setDamage(this.getDamage() | 0x08);
+ } else {
+ this.setDamage(this.getDamage() & 0x07);
+ }
+ }
+
+ @Override
+ public Item toItem() {
+ int damage = this.getDamage() & 0x07;
+ return new ItemBlock(Block.get(this.getId(), damage), damage);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabBlackstone.java b/src/main/java/cn/nukkit/block/BlockSlabBlackstone.java
new file mode 100644
index 00000000000..ab512d630b6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabBlackstone.java
@@ -0,0 +1,70 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabBlackstone extends BlockSlab {
+
+ public BlockSlabBlackstone() {
+ this(0);
+ }
+
+ public BlockSlabBlackstone(int meta) {
+ super(meta, BLACKSTONE_DOUBLE_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return BLACKSTONE_SLAB;
+ }
+
+ @Override
+ public boolean hasTopBit() {
+ return (this.getDamage() & 0x01) == 1;
+ }
+
+ @Override
+ public void setTopBit(boolean topBit) {
+ this.setDamage(topBit ? 1 : 0);
+ }
+
+ @Override
+ public String getName() {
+ return "Blackstone Slab";
+ }
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{this.toItem()};
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockSlabBlackstonePolished.java
new file mode 100644
index 00000000000..260b884ecdb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabBlackstonePolished.java
@@ -0,0 +1,74 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabBlackstonePolished extends BlockSlab {
+
+ public BlockSlabBlackstonePolished() {
+ this(0);
+ }
+
+ public BlockSlabBlackstonePolished(int meta) {
+ super(meta, POLISHED_BLACKSTONE_DOUBLE_SLAB);
+ }
+
+ protected BlockSlabBlackstonePolished(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_SLAB;
+ }
+
+ @Override
+ public boolean hasTopBit() {
+ return (this.getDamage() & 0x01) == 1;
+ }
+
+ @Override
+ public void setTopBit(boolean topBit) {
+ this.setDamage(topBit ? 1 : 0);
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Polished Blackstone";
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{ this.toItem() };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public double getResistance() {
+ return 6.0;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabBrickBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockSlabBrickBlackstonePolished.java
new file mode 100644
index 00000000000..8cd18484070
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabBrickBlackstonePolished.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockSlabBrickBlackstonePolished extends BlockSlabBlackstonePolished {
+
+ public BlockSlabBrickBlackstonePolished() {
+ this(0);
+ }
+
+ public BlockSlabBrickBlackstonePolished(int meta) {
+ super(meta, POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB);
+ }
+
+ protected BlockSlabBrickBlackstonePolished(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_BRICK_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Polished Blackstone Brick";
+ }
+
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabBrickDeepslate.java b/src/main/java/cn/nukkit/block/BlockSlabBrickDeepslate.java
new file mode 100644
index 00000000000..c7283cad770
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabBrickDeepslate.java
@@ -0,0 +1,56 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabBrickDeepslate extends BlockSlab {
+
+ public BlockSlabBrickDeepslate() {
+ this(0);
+ }
+
+ public BlockSlabBrickDeepslate(int meta) {
+ super(meta, DEEPSLATE_BRICK_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_BRICK_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Deepslate Brick";
+ }
+
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperBase.java b/src/main/java/cn/nukkit/block/BlockSlabCopperBase.java
new file mode 100644
index 00000000000..d466b8c7ad6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperBase.java
@@ -0,0 +1,91 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public abstract class BlockSlabCopperBase extends BlockSlab implements Waxable, Oxidizable {
+
+ public BlockSlabCopperBase(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
+ }
+
+ @Override
+ public boolean onActivate(@Nonnull Item item, @Nullable Player player) {
+ return Waxable.super.onActivate(item, player)
+ || Oxidizable.super.onActivate(item, player);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ return Oxidizable.super.onUpdate(type);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_STONE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Block getStateWithOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ return Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel), this.getDamage());
+ }
+
+ @Override
+ public boolean setOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ if (this.getOxidizationLevel().equals(oxidizationLevel)) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel)));
+ }
+
+ @Override
+ public boolean setWaxed(boolean waxed) {
+ if (this.isWaxed() == waxed) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(getCopperId(waxed, getOxidizationLevel())));
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return false;
+ }
+
+ protected abstract int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel);
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCut.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCut.java
new file mode 100644
index 00000000000..55373beb66c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCut.java
@@ -0,0 +1,62 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+
+public class BlockSlabCopperCut extends BlockSlabCopperBase {
+
+ public BlockSlabCopperCut() {
+ this(0);
+ }
+
+ public BlockSlabCopperCut(int meta) {
+ super(meta, DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ protected BlockSlabCopperCut(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
+ }
+
+ @Override
+ public int getId() {
+ return CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ String name = "";
+ if (this.isWaxed()) {
+ name += "Waxed ";
+ }
+
+ OxidizationLevel oxidizationLevel = this.getOxidizationLevel();
+ if (oxidizationLevel != OxidizationLevel.UNAFFECTED) {
+ String oxidationName = oxidizationLevel.name();
+ name += oxidationName.charAt(0) + oxidationName.substring(1).toLowerCase();
+ }
+ return name + " Cut Copper";
+ }
+
+ @Override
+ protected int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel) {
+ if (oxidizationLevel == null) {
+ return getId();
+ }
+ switch (oxidizationLevel) {
+ case UNAFFECTED:
+ return waxed ? WAXED_CUT_COPPER_SLAB : CUT_COPPER_SLAB;
+ case EXPOSED:
+ return waxed ? WAXED_EXPOSED_CUT_COPPER_SLAB : EXPOSED_CUT_COPPER_SLAB;
+ case WEATHERED:
+ return waxed ? WAXED_WEATHERED_CUT_COPPER_SLAB : WEATHERED_CUT_COPPER_SLAB;
+ case OXIDIZED:
+ return waxed ? WAXED_OXIDIZED_CUT_COPPER_SLAB : OXIDIZED_CUT_COPPER_SLAB;
+ default:
+ return getId();
+ }
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.UNAFFECTED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCutExposed.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCutExposed.java
new file mode 100644
index 00000000000..8f2580568fa
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCutExposed.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabCopperCutExposed extends BlockSlabCopperCut {
+
+ public BlockSlabCopperCutExposed() {
+ this(0);
+ }
+
+ public BlockSlabCopperCutExposed(int meta) {
+ super(meta, EXPOSED_DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ protected BlockSlabCopperCutExposed(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
+ }
+
+ @Override
+ public int getId() {
+ return EXPOSED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.EXPOSED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.LIGHT_GRAY_TERRACOTA_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCutExposedWaxed.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCutExposedWaxed.java
new file mode 100644
index 00000000000..1cbc80b757d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCutExposedWaxed.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockSlabCopperCutExposedWaxed extends BlockSlabCopperCutExposed {
+
+ public BlockSlabCopperCutExposedWaxed() {
+ this(0);
+ }
+
+ public BlockSlabCopperCutExposedWaxed(int meta) {
+ super(meta, WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_EXPOSED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCutOxidized.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCutOxidized.java
new file mode 100644
index 00000000000..5cc2a84d04c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCutOxidized.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabCopperCutOxidized extends BlockSlabCopperCut {
+
+ public BlockSlabCopperCutOxidized() {
+ this(0);
+ }
+
+ public BlockSlabCopperCutOxidized(int meta) {
+ super(meta, OXIDIZED_DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ protected BlockSlabCopperCutOxidized(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
+ }
+
+ @Override
+ public int getId() {
+ return OXIDIZED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.OXIDIZED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_NYLIUM_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCutOxidizedWaxed.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCutOxidizedWaxed.java
new file mode 100644
index 00000000000..d5c425c7247
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCutOxidizedWaxed.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockSlabCopperCutOxidizedWaxed extends BlockSlabCopperCutOxidized {
+
+ public BlockSlabCopperCutOxidizedWaxed() {
+ this(0);
+ }
+
+ public BlockSlabCopperCutOxidizedWaxed(int meta) {
+ super(meta, WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_OXIDIZED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCutWaxed.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCutWaxed.java
new file mode 100644
index 00000000000..2d8a8ac1495
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCutWaxed.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockSlabCopperCutWaxed extends BlockSlabCopperCut {
+
+ public BlockSlabCopperCutWaxed() {
+ this(0);
+ }
+
+ public BlockSlabCopperCutWaxed(int meta) {
+ super(meta, WAXED_DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCutWeathered.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCutWeathered.java
new file mode 100644
index 00000000000..7edf336bd05
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCutWeathered.java
@@ -0,0 +1,34 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabCopperCutWeathered extends BlockSlabCopperCut {
+
+ public BlockSlabCopperCutWeathered() {
+ this(0);
+ }
+
+ public BlockSlabCopperCutWeathered(int meta) {
+ super(meta, WEATHERED_DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ protected BlockSlabCopperCutWeathered(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
+ }
+
+ @Override
+ public int getId() {
+ return WEATHERED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.WEATHERED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCopperCutWeatheredWaxed.java b/src/main/java/cn/nukkit/block/BlockSlabCopperCutWeatheredWaxed.java
new file mode 100644
index 00000000000..17eded2a46d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCopperCutWeatheredWaxed.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockSlabCopperCutWeatheredWaxed extends BlockSlabCopperCutWeathered {
+
+ public BlockSlabCopperCutWeatheredWaxed() {
+ this(0);
+ }
+
+ public BlockSlabCopperCutWeatheredWaxed(int meta) {
+ super(meta, WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_WEATHERED_CUT_COPPER_SLAB;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabCrimson.java b/src/main/java/cn/nukkit/block/BlockSlabCrimson.java
new file mode 100644
index 00000000000..2d8720eaa97
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabCrimson.java
@@ -0,0 +1,64 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabCrimson extends BlockSlab {
+
+ public BlockSlabCrimson() {
+ this(0);
+ }
+
+ public BlockSlabCrimson(int meta) {
+ super(meta, CRIMSON_DOUBLE_SLAB);
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Crimson";
+ }
+
+ @Override
+ public int getId() {
+ return CRIMSON_SLAB;
+ }
+
+ @Override
+ public boolean hasTopBit() {
+ return (this.getDamage() & 0x01) == 1;
+ }
+
+ @Override
+ public void setTopBit(boolean topBit) {
+ this.setDamage(topBit ? 1 : 0);
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[]{
+ this.toItem()
+ };
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabDeepslateCobbled.java b/src/main/java/cn/nukkit/block/BlockSlabDeepslateCobbled.java
new file mode 100644
index 00000000000..ddd0ca85453
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabDeepslateCobbled.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockSlabDeepslateCobbled extends BlockSlab {
+
+ public BlockSlabDeepslateCobbled() {
+ this(0);
+ }
+
+ public BlockSlabDeepslateCobbled(int meta) {
+ super(meta, COBBLED_DEEPSLATE_DOUBLE_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return COBBLED_DEEPSLATE_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Cobbled Deepslate";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabDeepslatePolished.java b/src/main/java/cn/nukkit/block/BlockSlabDeepslatePolished.java
new file mode 100644
index 00000000000..ea218f10b87
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabDeepslatePolished.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabDeepslatePolished extends BlockSlab {
+
+ public BlockSlabDeepslatePolished() {
+ this(0);
+ }
+
+ public BlockSlabDeepslatePolished(int meta) {
+ super(meta, POLISHED_DEEPSLATE_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_DEEPSLATE_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Polished Deepslate";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabRedSandstone.java b/src/main/java/cn/nukkit/block/BlockSlabRedSandstone.java
index 3eb447dfdd7..2231e482498 100644
--- a/src/main/java/cn/nukkit/block/BlockSlabRedSandstone.java
+++ b/src/main/java/cn/nukkit/block/BlockSlabRedSandstone.java
@@ -2,7 +2,6 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
-import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
/**
@@ -11,10 +10,10 @@
public class BlockSlabRedSandstone extends BlockSlab {
public static final int RED_SANDSTONE = 0;
- public static final int PURPUR = 1; //WHY THIS
+ public static final int PURPUR = 1;
public BlockSlabRedSandstone() {
- this(0);
+ this(RED_SANDSTONE);
}
public BlockSlabRedSandstone(int meta) {
@@ -28,7 +27,7 @@ public int getId() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Red Sandstone",
"Purpur",
"",
@@ -44,7 +43,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -55,7 +54,8 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(this, this.getDamage() & 0x07);
+ int damage = this.getDamage() & 0x07;
+ return new ItemBlock(Block.get(this.getId(),damage ), damage);
}
@Override
@@ -65,6 +65,25 @@ public boolean canHarvestWithHand() {
@Override
public BlockColor getColor() {
- return BlockColor.ORANGE_BLOCK_COLOR;
+ int damage = this.getDamage() & 0x07;
+ switch (damage) {
+ case 0:
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ case 1:
+ return BlockColor.PURPLE_BLOCK_COLOR;
+ case 2:
+ return BlockColor.CYAN_BLOCK_COLOR;
+ case 3:
+ return BlockColor.DIAMOND_BLOCK_COLOR;
+ case 4:
+ return BlockColor.CYAN_BLOCK_COLOR;
+ case 5:
+ return BlockColor.STONE_BLOCK_COLOR;
+ case 6:
+ return BlockColor.SAND_BLOCK_COLOR;
+ case 7:
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+ return BlockColor.STONE_BLOCK_COLOR;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabStone.java b/src/main/java/cn/nukkit/block/BlockSlabStone.java
index 82cde51a5e2..5621b1190dd 100644
--- a/src/main/java/cn/nukkit/block/BlockSlabStone.java
+++ b/src/main/java/cn/nukkit/block/BlockSlabStone.java
@@ -9,6 +9,7 @@
* Created by CreeperFace on 26. 11. 2016.
*/
public class BlockSlabStone extends BlockSlab {
+
public static final int STONE = 0;
public static final int SANDSTONE = 1;
public static final int WOODEN = 2;
@@ -23,7 +24,11 @@ public BlockSlabStone() {
}
public BlockSlabStone(int meta) {
- super(meta, DOUBLE_STONE_SLAB);
+ this(meta, DOUBLE_STONE_SLAB);
+ }
+
+ public BlockSlabStone(int meta, int doubleSlab) {
+ super(meta, doubleSlab);
}
@Override
@@ -33,7 +38,7 @@ public int getId() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Stone",
"Sandstone",
"Wooden",
@@ -49,7 +54,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -60,7 +65,8 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(this, this.getDamage() & 0x07);
+ int damage = this.getDamage() & 0x07;
+ return new ItemBlock(Block.get(this.getId(),damage ), damage);
}
@Override
@@ -92,4 +98,4 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabStone3.java b/src/main/java/cn/nukkit/block/BlockSlabStone3.java
new file mode 100644
index 00000000000..193d6a50b2f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabStone3.java
@@ -0,0 +1,72 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabStone3 extends BlockSlabStone {
+
+ public static final int END_STONE_BRICKS = 0;
+ public static final int SMOOTH_RED_SANDSTONE = 1;
+ public static final int POLISHED_ANDESITE = 2;
+ public static final int ANDESITE = 3;
+ public static final int DIORITE = 4;
+ public static final int POLISHED_DIORITE = 5;
+ public static final int GRANITE = 6;
+ public static final int POLISHED_GRANITE = 7;
+
+ private static final String[] names = new String[]{
+ "End Stone Brick",
+ "Smooth Red Sandstone",
+ "Polished Andesite",
+ "Andesite",
+ "Diorite",
+ "Polished Diorite",
+ "Granite",
+ "Polisehd Granite"
+ };
+
+ public BlockSlabStone3() {
+ this(0);
+ }
+
+ public BlockSlabStone3(int meta) {
+ super(meta, DOUBLE_STONE_SLAB3);
+ }
+
+ @Override
+ public String getName() {
+ return ((this.getDamage() & 0x08) > 0 ? "Upper " : "") + names[this.getDamage() & 0x07] + " Slab";
+ }
+
+ @Override
+ public int getId() {
+ return STONE_SLAB3;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ switch (this.getDamage() & 0x07) {
+ case END_STONE_BRICKS:
+ return BlockColor.SAND_BLOCK_COLOR;
+ case SMOOTH_RED_SANDSTONE:
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ default:
+ case POLISHED_ANDESITE:
+ case ANDESITE:
+ return BlockColor.STONE_BLOCK_COLOR;
+ case DIORITE:
+ case POLISHED_DIORITE:
+ return BlockColor.QUARTZ_BLOCK_COLOR;
+ case GRANITE:
+ case POLISHED_GRANITE:
+ return BlockColor.DIRT_BLOCK_COLOR;
+ }
+ }
+
+ @Override
+ public Item toItem() {
+ int damage = this.getDamage() & 0x07;
+ return new ItemBlock(Block.get(this.getId(),damage ), damage);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabStone4.java b/src/main/java/cn/nukkit/block/BlockSlabStone4.java
new file mode 100644
index 00000000000..69f5ff3b258
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabStone4.java
@@ -0,0 +1,64 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabStone4 extends BlockSlabStone {
+
+ public static final int MOSSY_STONE_BRICKS = 0;
+ public static final int SMOOTH_QUARTZ = 1;
+ public static final int STONE = 2;
+ public static final int CUT_SANDSTONE = 3;
+ public static final int CUT_RED_SANDSTONE = 4;
+
+ private static final String[] names = new String[]{
+ "Mossy Stone Brick",
+ "Smooth Quartz",
+ "Stone",
+ "Cut Sandstone",
+ "Cut Red Sandstone"
+ };
+
+ public BlockSlabStone4() {
+ this(0);
+ }
+
+ public BlockSlabStone4(int meta) {
+ super(meta, DOUBLE_STONE_SLAB4);
+ }
+
+ @Override
+ public String getName() {
+ int variant = this.getDamage() & 0x07;
+ String name = variant >= names.length ? names[0] : names[variant];
+ return ((this.getDamage() & 0x08) > 0 ? "Upper " : "") + name + " Slab";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ switch (this.getDamage() & 0x07) {
+ default:
+ case MOSSY_STONE_BRICKS:
+ case STONE:
+ return BlockColor.STONE_BLOCK_COLOR;
+ case SMOOTH_QUARTZ:
+ return BlockColor.QUARTZ_BLOCK_COLOR;
+ case CUT_SANDSTONE:
+ return BlockColor.SAND_BLOCK_COLOR;
+ case CUT_RED_SANDSTONE:
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+ }
+
+ @Override
+ public int getId() {
+ return STONE_SLAB4;
+ }
+
+ @Override
+ public Item toItem() {
+ int damage = this.getDamage() & 0x07;
+ return new ItemBlock(Block.get(this.getId(),damage ), damage);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabTileDeepslate.java b/src/main/java/cn/nukkit/block/BlockSlabTileDeepslate.java
new file mode 100644
index 00000000000..90a8d911626
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabTileDeepslate.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabTileDeepslate extends BlockSlab {
+
+ public BlockSlabTileDeepslate() {
+ this(0);
+ }
+
+ public BlockSlabTileDeepslate(int meta) {
+ super(meta, DEEPSLATE_TILE_SLAB);
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_TILE_SLAB;
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Deepslate Tile";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabWarped.java b/src/main/java/cn/nukkit/block/BlockSlabWarped.java
new file mode 100644
index 00000000000..5b2f07ab7af
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSlabWarped.java
@@ -0,0 +1,62 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSlabWarped extends BlockSlab {
+
+ public BlockSlabWarped() {
+ this(0);
+ }
+
+ public BlockSlabWarped(int meta) {
+ super(meta, WARPED_DOUBLE_SLAB);
+ }
+
+ @Override
+ public String getSlabName() {
+ return "Warped";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_SLAB;
+ }
+
+ @Override
+ public boolean hasTopBit() {
+ return (this.getDamage() & 0x01) == 1;
+ }
+
+ @Override
+ public void setTopBit(boolean topBit) {
+ this.setDamage(topBit ? 1 : 0);
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[]{ this.toItem() };
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CYAN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSlabWood.java b/src/main/java/cn/nukkit/block/BlockSlabWood.java
index 6f9b968e7be..b9310e8077e 100644
--- a/src/main/java/cn/nukkit/block/BlockSlabWood.java
+++ b/src/main/java/cn/nukkit/block/BlockSlabWood.java
@@ -21,7 +21,7 @@ public BlockSlabWood(int meta) {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Oak",
"Spruce",
"Birch",
@@ -63,12 +63,13 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(this, this.getDamage() & 0x07);
+ int damage = this.getDamage() & 0x07;
+ return new ItemBlock(Block.get(this.getId(),damage ), damage);
}
@Override
public BlockColor getColor() {
- switch(getDamage() & 0x07){
+ switch (getDamage() & 0x07) {
default:
case 0: //OAK
return BlockColor.WOOD_BLOCK_COLOR;
diff --git a/src/main/java/cn/nukkit/block/BlockSlime.java b/src/main/java/cn/nukkit/block/BlockSlime.java
index 7b54d72c551..19ea184995c 100644
--- a/src/main/java/cn/nukkit/block/BlockSlime.java
+++ b/src/main/java/cn/nukkit/block/BlockSlime.java
@@ -7,9 +7,6 @@
*/
public class BlockSlime extends BlockSolid {
- public BlockSlime() {
- }
-
@Override
public double getHardness() {
return 0;
@@ -32,6 +29,6 @@ public double getResistance() {
@Override
public BlockColor getColor() {
- return BlockColor.GRASS_BLOCK_COLOR;
+ return BlockColor.GREEN_BLOCK_COLOR;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockSmithingTable.java b/src/main/java/cn/nukkit/block/BlockSmithingTable.java
new file mode 100644
index 00000000000..582f0fc052f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSmithingTable.java
@@ -0,0 +1,60 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.inventory.SmithingInventory;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSmithingTable extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Smithing Table";
+ }
+
+ @Override
+ public int getId() {
+ return SMITHING_TABLE;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public double getResistance() {
+ return 12.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2.5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 5;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player == null) {
+ return false;
+ }
+
+ player.addWindow(new SmithingInventory(player.getUIInventory(), this), Player.SMITHING_WINDOW_ID);
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSmoker.java b/src/main/java/cn/nukkit/block/BlockSmoker.java
new file mode 100644
index 00000000000..d99c5f94e60
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSmoker.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockSmoker extends BlockSmokerLit {
+
+ public BlockSmoker() {
+ this(0);
+ }
+
+ public BlockSmoker(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Smoker";
+ }
+
+ @Override
+ public int getId() {
+ return SMOKER;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSmokerLit.java b/src/main/java/cn/nukkit/block/BlockSmokerLit.java
new file mode 100644
index 00000000000..332162d2b16
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSmokerLit.java
@@ -0,0 +1,87 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.blockentity.BlockEntitySmoker;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+import cn.nukkit.nbt.tag.StringTag;
+import cn.nukkit.nbt.tag.Tag;
+
+import java.util.Map;
+
+public class BlockSmokerLit extends BlockFurnaceBurning {
+
+ public BlockSmokerLit() {
+ this(0);
+ }
+
+ public BlockSmokerLit(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return LIT_SMOKER;
+ }
+
+ @Override
+ public String getName() {
+ return "Lit Smoker";
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(SMOKER));
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ this.getLevel().setBlock(block, this, true, true);
+ CompoundTag nbt = new CompoundTag()
+ .putList(new ListTag<>("Items"))
+ .putString("id", BlockEntity.SMOKER)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ if (item.hasCustomName()) {
+ nbt.putString("CustomName", item.getCustomName());
+ }
+
+ if (item.hasCustomBlockData()) {
+ Map customData = item.getCustomBlockData().getTags();
+ for (Map.Entry tag : customData.entrySet()) {
+ nbt.put(tag.getKey(), tag.getValue());
+ }
+ }
+
+ BlockEntitySmoker smoker = (BlockEntitySmoker) BlockEntity.createBlockEntity(BlockEntity.SMOKER, this.getChunk(), nbt);
+ return smoker != null;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (player != null) {
+ BlockEntity t = this.getLevel().getBlockEntity(this);
+ if (!(t instanceof BlockEntitySmoker)) {
+ return false;
+ }
+
+ BlockEntitySmoker smoker = (BlockEntitySmoker) t;
+ if (smoker.namedTag.contains("Lock") && smoker.namedTag.get("Lock") instanceof StringTag) {
+ if (!smoker.namedTag.getString("Lock").equals(item.getCustomName())) {
+ return true;
+ }
+ }
+
+ player.addWindow(smoker.getInventory());
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSmoothStone.java b/src/main/java/cn/nukkit/block/BlockSmoothStone.java
new file mode 100644
index 00000000000..b5d2db45a6b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSmoothStone.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockSmoothStone extends BlockSolid {
+
+ @Override
+ public String getName() {
+ return "Smooth Stone";
+ }
+
+ @Override
+ public int getId() {
+ return SMOOTH_STONE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 10;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSnow.java b/src/main/java/cn/nukkit/block/BlockSnow.java
index 2ca14c92264..d452aee3dbc 100644
--- a/src/main/java/cn/nukkit/block/BlockSnow.java
+++ b/src/main/java/cn/nukkit/block/BlockSnow.java
@@ -2,15 +2,12 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSnowball;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.utils.BlockColor;
public class BlockSnow extends BlockSolid {
- public BlockSnow() {
- }
-
@Override
public String getName() {
return "Snow";
@@ -26,11 +23,6 @@ public double getHardness() {
return 0.2;
}
- @Override
- public double getResistance() {
- return 1;
- }
-
@Override
public int getToolType() {
return ItemTool.TYPE_SHOVEL;
@@ -39,8 +31,11 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
if (item.isShovel() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
return new Item[]{
- new ItemSnowball(0, 4)
+ Item.get(Item.SNOWBALL, 0, 4)
};
} else {
return new Item[0];
@@ -57,7 +52,7 @@ public BlockColor getColor() {
public boolean canHarvestWithHand() {
return false;
}
-
+
@Override
public boolean canSilkTouch() {
return true;
@@ -70,11 +65,16 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.isShovel()) {
+ if (item.isShovel() && (player == null || (player.gamemode & 0x2) == 0)) {
item.useOn(this);
this.level.useBreakOn(this, item.clone().clearNamedTag(), null, true);
return true;
}
return false;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockSnowLayer.java b/src/main/java/cn/nukkit/block/BlockSnowLayer.java
index 6c9b959a492..d773e6d3547 100644
--- a/src/main/java/cn/nukkit/block/BlockSnowLayer.java
+++ b/src/main/java/cn/nukkit/block/BlockSnowLayer.java
@@ -3,7 +3,6 @@
import cn.nukkit.Player;
import cn.nukkit.event.block.BlockFadeEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSnowball;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.GameRule;
import cn.nukkit.level.Level;
@@ -14,31 +13,14 @@
* Created on 2015/12/6 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
-public class BlockSnowLayer extends BlockFallable {
-
- private int meta;
+public class BlockSnowLayer extends BlockFallableMeta {
public BlockSnowLayer() {
this(0);
}
public BlockSnowLayer(int meta) {
- this.meta = meta;
- }
-
- @Override
- public final int getFullId() {
- return (this.getId() << 4) + this.getDamage();
- }
-
- @Override
- public final int getDamage() {
- return this.meta;
- }
-
- @Override
- public final void setDamage(int meta) {
- this.meta = meta;
+ super(meta);
}
@Override
@@ -71,6 +53,11 @@ public boolean canBeReplaced() {
return (this.getDamage() & 0x7) != 0x7;
}
+ @Override
+ public boolean canPassThrough() {
+ return (this.getDamage() & 0x7) < 3;
+ }
+
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (this.canSurvive()) {
@@ -114,12 +101,12 @@ public int onUpdate(int type) {
@Override
public Item toItem() {
- return new ItemSnowball();
+ return Item.get(Item.SNOWBALL);
}
@Override
public Item[] getDrops(Item item) {
- if (item.isShovel() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isShovel()) {
Item drop = this.toItem();
int height = this.getDamage() & 0x7;
drop.setCount(height < 3 ? 1 : height < 5 ? 2 : height == 7 ? 4 : 3);
@@ -134,11 +121,6 @@ public BlockColor getColor() {
return BlockColor.SNOW_BLOCK_COLOR;
}
- @Override
- public boolean canHarvestWithHand() {
- return false;
- }
-
@Override
public boolean isTransparent() {
return (this.getDamage() & 0x7) != 0x7;
@@ -167,11 +149,11 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.isShovel() && (player.gamemode & 0x2) == 0) {
+ if (item.isShovel() && (player == null || (player.gamemode & 0x2) == 0)) {
item.useOn(this);
this.level.useBreakOn(this, item.clone().clearNamedTag(), null, true);
return true;
- } else if (item.getId() == SNOW_LAYER && (player.gamemode & 0x2) == 0) {
+ } else if (item.getId() == SNOW_LAYER && (player == null || (player.gamemode & 0x2) == 0)) {
if ((this.getDamage() & 0x7) != 0x7) {
this.setDamage(this.getDamage() + 1);
this.level.setBlock(this ,this, true);
@@ -188,7 +170,7 @@ public boolean onActivate(Item item, Player player) {
}
@Override
- public boolean canPassThrough() {
- return (this.getDamage() & 0x7) < 3;
+ public boolean breakWhenPushed() {
+ return true;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockSolid.java b/src/main/java/cn/nukkit/block/BlockSolid.java
index d472699f769..889be6f58cb 100644
--- a/src/main/java/cn/nukkit/block/BlockSolid.java
+++ b/src/main/java/cn/nukkit/block/BlockSolid.java
@@ -3,7 +3,7 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockSolid extends Block {
@@ -11,11 +11,6 @@ public abstract class BlockSolid extends Block {
protected BlockSolid() {
}
- @Override
- public boolean isSolid() {
- return true;
- }
-
@Override
public BlockColor getColor() {
return BlockColor.STONE_BLOCK_COLOR;
diff --git a/src/main/java/cn/nukkit/block/BlockSolidMeta.java b/src/main/java/cn/nukkit/block/BlockSolidMeta.java
index 463f797a9cd..f650f403bd3 100644
--- a/src/main/java/cn/nukkit/block/BlockSolidMeta.java
+++ b/src/main/java/cn/nukkit/block/BlockSolidMeta.java
@@ -3,15 +3,11 @@
import cn.nukkit.utils.BlockColor;
public abstract class BlockSolidMeta extends BlockMeta {
+
protected BlockSolidMeta(int meta) {
super(meta);
}
- @Override
- public boolean isSolid() {
- return true;
- }
-
@Override
public BlockColor getColor() {
return BlockColor.STONE_BLOCK_COLOR;
diff --git a/src/main/java/cn/nukkit/block/BlockSoulFire.java b/src/main/java/cn/nukkit/block/BlockSoulFire.java
new file mode 100644
index 00000000000..bf70df73c0f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSoulFire.java
@@ -0,0 +1,42 @@
+package cn.nukkit.block;
+
+import cn.nukkit.level.Level;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSoulFire extends BlockFire {
+
+ public BlockSoulFire() {
+ this(0);
+ }
+
+ public BlockSoulFire(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return SOUL_FIRE;
+ }
+
+ @Override
+ public String getName() {
+ return "Soul Fire";
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ int downId = down().getId();
+ if (downId != Block.SOUL_SAND && downId != Block.SOUL_SOIL) {
+ this.getLevel().setBlock(this, Block.get(Block.FIRE, this.getDamage()));
+ }
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.LIGHT_BLUE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSoulLantern.java b/src/main/java/cn/nukkit/block/BlockSoulLantern.java
new file mode 100644
index 00000000000..6fe086d8494
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSoulLantern.java
@@ -0,0 +1,35 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockSoulLantern extends BlockLantern {
+
+ public BlockSoulLantern() {
+ this(0);
+ }
+
+ public BlockSoulLantern(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(SOUL_LANTERN));
+ }
+
+ @Override
+ public int getId() {
+ return SOUL_LANTERN;
+ }
+
+ @Override
+ public String getName() {
+ return "Soul Lantern";
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 10;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSoulSand.java b/src/main/java/cn/nukkit/block/BlockSoulSand.java
index e4dbd455cb0..e9407c86099 100644
--- a/src/main/java/cn/nukkit/block/BlockSoulSand.java
+++ b/src/main/java/cn/nukkit/block/BlockSoulSand.java
@@ -1,7 +1,10 @@
package cn.nukkit.block;
+import cn.nukkit.Server;
import cn.nukkit.entity.Entity;
+import cn.nukkit.event.block.BlockFormEvent;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
import cn.nukkit.utils.BlockColor;
/**
@@ -9,9 +12,6 @@
*/
public class BlockSoulSand extends BlockSolid {
- public BlockSoulSand() {
- }
-
@Override
public String getName() {
return "Soul Sand";
@@ -29,7 +29,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 2.5;
+ return 0.5;
}
@Override
@@ -39,7 +39,7 @@ public int getToolType() {
@Override
public double getMaxY() {
- return this.y + 1 - 0.125;
+ return this.y + 0.875;
}
@Override
@@ -58,4 +58,20 @@ public BlockColor getColor() {
return BlockColor.BROWN_BLOCK_COLOR;
}
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ Block up = this.up();
+ if (up instanceof BlockWater && (up.getDamage() == 0 || up.getDamage() == 8)) {
+ BlockFormEvent event = new BlockFormEvent(up, Block.get(BUBBLE_COLUMN, BlockBubbleColumn.DIRECTION_UP));
+ Server.getInstance().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(up, event.getNewState(), false, true);
+ }
+ }
+ }
+
+ return 0;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockSoulSoil.java b/src/main/java/cn/nukkit/block/BlockSoulSoil.java
new file mode 100644
index 00000000000..3ef745a54f1
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSoulSoil.java
@@ -0,0 +1,53 @@
+package cn.nukkit.block;
+
+import cn.nukkit.entity.Entity;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockSoulSoil extends BlockSolid {
+
+ public BlockSoulSoil() {
+ super();
+ }
+
+ @Override
+ public int getId() {
+ return SOUL_SOIL;
+ }
+
+ @Override
+ public String getName() {
+ return "Soul Soil";
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_SHOVEL;
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ entity.motionX *= 0.4d;
+ entity.motionZ *= 0.4d;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BROWN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSoulTorch.java b/src/main/java/cn/nukkit/block/BlockSoulTorch.java
new file mode 100644
index 00000000000..fdeb4243312
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSoulTorch.java
@@ -0,0 +1,27 @@
+package cn.nukkit.block;
+
+public class BlockSoulTorch extends BlockTorch {
+
+ public BlockSoulTorch() {
+ this(0);
+ }
+
+ public BlockSoulTorch(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Soul Torch";
+ }
+
+ @Override
+ public int getId() {
+ return SOUL_TORCH;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 10;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSponge.java b/src/main/java/cn/nukkit/block/BlockSponge.java
index db77752a515..51523ebcaf1 100644
--- a/src/main/java/cn/nukkit/block/BlockSponge.java
+++ b/src/main/java/cn/nukkit/block/BlockSponge.java
@@ -5,24 +5,25 @@
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.GlobalBlockPalette;
import cn.nukkit.level.Level;
-import cn.nukkit.level.particle.SmokeParticle;
+import cn.nukkit.level.particle.ExplodeParticle;
import cn.nukkit.math.BlockFace;
import cn.nukkit.network.protocol.LevelEventPacket;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.utils.BlockColor;
import java.util.ArrayDeque;
import java.util.Queue;
-import java.util.concurrent.ThreadLocalRandom;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockSponge extends BlockSolidMeta {
public static final int DRY = 0;
public static final int WET = 1;
- private static final String[] NAMES = new String[]{
+
+ private static final String[] NAMES = {
"Sponge",
"Wet sponge"
};
@@ -50,11 +51,6 @@ public double getResistance() {
return 3;
}
- @Override
- public int getToolType() {
- return ItemTool.TYPE_HOE;
- }
-
@Override
public String getName() {
return NAMES[this.getDamage() & 0b1];
@@ -62,21 +58,22 @@ public String getName() {
@Override
public BlockColor getColor() {
- return BlockColor.YELLOW_BLOCK_COLOR;
+ return BlockColor.CLOTH_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (this.getDamage() == WET && level.getDimension() == Level.DIMENSION_NETHER) {
level.setBlock(block, Block.get(BlockID.SPONGE, DRY), true, true);
- this.getLevel().addLevelEvent(block.add(0.5, 0.875, 0.5), LevelEventPacket.EVENT_SOUND_EXPLODE);
-
- for (int i = 0; i < 8; ++i) {
- level.addParticle(new SmokeParticle(block.getLocation().add(ThreadLocalRandom.current().nextDouble(), 1, ThreadLocalRandom.current().nextDouble())));
- }
-
+ level.addLevelSoundEvent(block, LevelSoundEventPacket.SOUND_FIZZ);
+ level.addParticle(new ExplodeParticle(block.add(0.5, 1, 0.5)));
return true;
- } else if (this.getDamage() == DRY && block instanceof BlockWater && performWaterAbsorb(block)) {
+ } else if (this.getDamage() == DRY && performWaterAbsorb(block)) {
level.setBlock(block, Block.get(BlockID.SPONGE, WET), true, true);
for (int i = 0; i < 4; i++) {
@@ -96,6 +93,17 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
private boolean performWaterAbsorb(Block block) {
+ boolean waterFound = false;
+ for (BlockFace side : BlockFace.values()) {
+ if (getSide(side) instanceof BlockWater) {
+ waterFound = true;
+ break;
+ }
+ }
+ if (!waterFound) {
+ return false;
+ }
+
Queue entries = new ArrayDeque<>();
entries.add(new Entry(block, 0));
@@ -106,7 +114,7 @@ private boolean performWaterAbsorb(Block block) {
for (BlockFace face : BlockFace.values()) {
Block faceBlock = entry.block.getSide(face);
- if (faceBlock.getId() == BlockID.WATER || faceBlock.getId() == BlockID.STILL_WATER) {
+ if (Block.hasWater(faceBlock.getId())) {
this.level.setBlock(faceBlock, Block.get(BlockID.AIR));
++waterRemoved;
if (entry.distance < 6) {
diff --git a/src/main/java/cn/nukkit/block/BlockSporeBlossom.java b/src/main/java/cn/nukkit/block/BlockSporeBlossom.java
new file mode 100644
index 00000000000..f94a226c659
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSporeBlossom.java
@@ -0,0 +1,75 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+
+public class BlockSporeBlossom extends BlockTransparent {
+
+ public BlockSporeBlossom() {
+ }
+
+ @Override
+ public int getId() {
+ return SPORE_BLOSSOM;
+ }
+
+ @Override
+ public String getName() {
+ return "Spore Blossom";
+ }
+
+ @Override
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (block.up().isSolid()) {
+ return super.place(item, block, target, face, fx, fy, fz, player);
+ }
+ return false;
+ }
+
+ @Override
+ public double getMinX() {
+ return this.x + 2D / 16D;
+ }
+
+ @Override
+ public double getMinY() {
+ return this.y + 13D / 16D;
+ }
+
+ @Override
+ public double getMinZ() {
+ return this.z + 2D / 16D;
+ }
+
+ @Override
+ public double getMaxX() {
+ return this.x + 14D / 16D;
+ }
+
+ @Override
+ public double getMaxZ() {
+ return this.z + 14D / 16D;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.getLevel().useBreakOn(this, null, null, true);
+ } else if (type == Level.BLOCK_UPDATE_NORMAL && !this.up().isSolid()) {
+ this.getLevel().scheduleUpdate(this, 1);
+ }
+ return type;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSpruceSignStanding.java b/src/main/java/cn/nukkit/block/BlockSpruceSignStanding.java
new file mode 100644
index 00000000000..df26ce756ca
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSpruceSignStanding.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockSpruceSignStanding extends BlockSignPost {
+
+ public BlockSpruceSignStanding() {
+ this(0);
+ }
+
+ public BlockSpruceSignStanding(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Spruce Sign Post";
+ }
+
+ @Override
+ public int getId() {
+ return SPRUCE_STANDING_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.SPRUCE_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return SPRUCE_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return SPRUCE_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSpruceWallSign.java b/src/main/java/cn/nukkit/block/BlockSpruceWallSign.java
new file mode 100644
index 00000000000..c6f2674b3e8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSpruceWallSign.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockSpruceWallSign extends BlockWallSign {
+
+ public BlockSpruceWallSign() {
+ this(0);
+ }
+
+ public BlockSpruceWallSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Spruce Wall Sign";
+ }
+
+ @Override
+ public int getId() {
+ return SPRUCE_WALL_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.SPRUCE_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return SPRUCE_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return SPRUCE_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairs.java b/src/main/java/cn/nukkit/block/BlockStairs.java
index 135cc49704c..9a096684ca9 100644
--- a/src/main/java/cn/nukkit/block/BlockStairs.java
+++ b/src/main/java/cn/nukkit/block/BlockStairs.java
@@ -2,17 +2,18 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.SimpleAxisAlignedBB;
import cn.nukkit.utils.Faceable;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
-public abstract class BlockStairs extends BlockTransparentMeta implements Faceable {
+public abstract class BlockStairs extends BlockSolidMeta implements Faceable {
+
+ private static final short[] faces = {2, 1, 3, 0};
protected BlockStairs(int meta) {
super(meta);
@@ -21,32 +22,31 @@ protected BlockStairs(int meta) {
@Override
public double getMinY() {
// TODO: this seems wrong
- return this.y + ((getDamage() & 0x04) > 0 ? 0.5 : 0);
+ return this.y + (this.getDamage() & 0x04) > 0 ? 0.5 : 0;
}
@Override
public double getMaxY() {
// TODO: this seems wrong
- return this.y + ((getDamage() & 0x04) > 0 ? 1 : 0.5);
+ return this.y + (this.getDamage() & 0x04) > 0 ? 1 : 0.5;
}
+
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- int[] faces = new int[]{2, 1, 3, 0};
this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
if ((fy > 0.5 && face != BlockFace.UP) || face == BlockFace.DOWN) {
this.setDamage(this.getDamage() | 0x04); //Upside-down stairs
}
- this.getLevel().setBlock(block, this, true, true);
-
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
- toItem()
+ toItem()
};
} else {
return new Item[0];
@@ -88,49 +88,41 @@ public boolean collidesWithBB(AxisAlignedBB bb) {
if (side == 0) {
- if (bb.intersectsWith(new SimpleAxisAlignedBB(
+ return bb.intersectsWith(new SimpleAxisAlignedBB(
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 SimpleAxisAlignedBB(
+ return bb.intersectsWith(new SimpleAxisAlignedBB(
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 SimpleAxisAlignedBB(
+ return bb.intersectsWith(new SimpleAxisAlignedBB(
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 SimpleAxisAlignedBB(
+ return bb.intersectsWith(new SimpleAxisAlignedBB(
this.x,
this.y + f2,
this.z,
this.x + 1,
this.y + f3,
this.z + 0.5
- ))) {
- return true;
- }
+ ));
}
return false;
@@ -140,4 +132,9 @@ public boolean collidesWithBB(AxisAlignedBB bb) {
public BlockFace getBlockFace() {
return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsAcacia.java b/src/main/java/cn/nukkit/block/BlockStairsAcacia.java
index cd9481407d7..78f31ba5c12 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsAcacia.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsAcacia.java
@@ -3,7 +3,7 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockStairsAcacia extends BlockStairsWood {
@@ -30,5 +30,4 @@ public String getName() {
public BlockColor getColor() {
return BlockColor.ORANGE_BLOCK_COLOR;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsAndesite.java b/src/main/java/cn/nukkit/block/BlockStairsAndesite.java
new file mode 100644
index 00000000000..cd52f6a49fe
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsAndesite.java
@@ -0,0 +1,44 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockStairsAndesite extends BlockStairs {
+
+ public BlockStairsAndesite() {
+ this(0);
+ }
+
+ public BlockStairsAndesite(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Andesite Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return ANDESITE_STAIRS;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 30;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsAndesitePolished.java b/src/main/java/cn/nukkit/block/BlockStairsAndesitePolished.java
new file mode 100644
index 00000000000..a43c5f32b82
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsAndesitePolished.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsAndesitePolished extends BlockStairsAndesite {
+
+ public BlockStairsAndesitePolished() {
+ this(0);
+ }
+
+ public BlockStairsAndesitePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Andesite Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_ANDESITE_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsBirch.java b/src/main/java/cn/nukkit/block/BlockStairsBirch.java
index fd3b0cb7f4a..d24eff46b1f 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsBirch.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsBirch.java
@@ -30,5 +30,4 @@ public String getName() {
public BlockColor getColor() {
return BlockColor.SAND_BLOCK_COLOR;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsBlackstone.java b/src/main/java/cn/nukkit/block/BlockStairsBlackstone.java
new file mode 100644
index 00000000000..dbde24a74d3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsBlackstone.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsBlackstone extends BlockStairs {
+
+ public BlockStairsBlackstone() {
+ this(0);
+ }
+
+ public BlockStairsBlackstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Blackstone Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return BLACKSTONE_STAIRS;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockStairsBlackstonePolished.java
new file mode 100644
index 00000000000..52e703f6cdf
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsBlackstonePolished.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsBlackstonePolished extends BlockStairsBlackstone {
+
+ public BlockStairsBlackstonePolished() {
+ this(0);
+ }
+
+ public BlockStairsBlackstonePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsBrick.java b/src/main/java/cn/nukkit/block/BlockStairsBrick.java
index a6ba171bc8e..d73f6de546c 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsBrick.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsBrick.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsBrick extends BlockStairs {
+
public BlockStairsBrick() {
this(0);
}
@@ -42,12 +43,12 @@ public String getName() {
}
@Override
- public BlockColor getColor() {
- return BlockColor.RED_BLOCK_COLOR;
+ public boolean canHarvestWithHand() {
+ return false;
}
@Override
- public boolean canHarvestWithHand() {
- return false;
+ public BlockColor getColor() {
+ return BlockColor.RED_BLOCK_COLOR;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsBrickBlackstonePolished.java b/src/main/java/cn/nukkit/block/BlockStairsBrickBlackstonePolished.java
new file mode 100644
index 00000000000..b55bbee1656
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsBrickBlackstonePolished.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsBrickBlackstonePolished extends BlockStairsBlackstonePolished {
+
+ public BlockStairsBrickBlackstonePolished() {
+ this(0);
+ }
+
+ public BlockStairsBrickBlackstonePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_BLACKSTONE_BRICK_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Blackstone Brick Stairs";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsBrickDeepslate.java b/src/main/java/cn/nukkit/block/BlockStairsBrickDeepslate.java
new file mode 100644
index 00000000000..80409525579
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsBrickDeepslate.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsBrickDeepslate extends BlockStairs {
+
+ public BlockStairsBrickDeepslate() {
+ this(0);
+ }
+
+ public BlockStairsBrickDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_BRICK_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Brick Stairs";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCobblestone.java b/src/main/java/cn/nukkit/block/BlockStairsCobblestone.java
index 6abff3dd072..1497c3e4682 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsCobblestone.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsCobblestone.java
@@ -1,13 +1,13 @@
package cn.nukkit.block;
import cn.nukkit.item.ItemTool;
-import cn.nukkit.utils.BlockColor;
/**
* Created on 2015/11/25 by xtypr.
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsCobblestone extends BlockStairs {
+
public BlockStairsCobblestone() {
this(0);
}
@@ -41,11 +41,6 @@ public String getName() {
return "Cobblestone Stairs";
}
- @Override
- public BlockColor getColor() {
- return BlockColor.STONE_BLOCK_COLOR;
- }
-
@Override
public boolean canHarvestWithHand() {
return false;
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperBase.java b/src/main/java/cn/nukkit/block/BlockStairsCopperBase.java
new file mode 100644
index 00000000000..536fe95045d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperBase.java
@@ -0,0 +1,86 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+
+public abstract class BlockStairsCopperBase extends BlockStairs implements Waxable, Oxidizable {
+
+ public BlockStairsCopperBase() {
+ this(0);
+ }
+
+ public BlockStairsCopperBase(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_STONE;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ return Waxable.super.onActivate(item, player)
+ || Oxidizable.super.onActivate(item, player);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ return Oxidizable.super.onUpdate(type);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Block getStateWithOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ return Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel), this.getDamage());
+ }
+
+ @Override
+ public boolean setOxidizationLevel(OxidizationLevel oxidizationLevel) {
+ if (this.getOxidizationLevel().equals(oxidizationLevel)) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(this.getCopperId(this.isWaxed(), oxidizationLevel)));
+ }
+
+ @Override
+ public boolean setWaxed(boolean waxed) {
+ if (this.isWaxed() == waxed) {
+ return true;
+ }
+ return this.level.setBlock(this, Block.get(getCopperId(waxed, getOxidizationLevel())));
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return false;
+ }
+
+ protected abstract int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel);
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCut.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCut.java
new file mode 100644
index 00000000000..ccb8812101c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCut.java
@@ -0,0 +1,59 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+
+public class BlockStairsCopperCut extends BlockStairsCopperBase {
+
+ public BlockStairsCopperCut() {
+ this(0);
+ }
+
+ public BlockStairsCopperCut(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ String name = "";
+ if (this.isWaxed()) {
+ name += "Waxed ";
+ }
+
+ OxidizationLevel oxidizationLevel = this.getOxidizationLevel();
+ if (oxidizationLevel != OxidizationLevel.UNAFFECTED) {
+ String oxidationName = oxidizationLevel.name();
+ name += oxidationName.charAt(0) + oxidationName.substring(1).toLowerCase();
+ }
+ return name + " Cut Copper Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return CUT_COPPER_STAIRS;
+ }
+
+
+ @Override
+ protected int getCopperId(boolean waxed, OxidizationLevel oxidizationLevel) {
+ if (oxidizationLevel == null) {
+ return this.getId();
+ }
+ switch (oxidizationLevel) {
+ case UNAFFECTED:
+ return waxed ? WAXED_CUT_COPPER_STAIRS : CUT_COPPER_STAIRS;
+ case EXPOSED:
+ return waxed ? WAXED_EXPOSED_CUT_COPPER_STAIRS : EXPOSED_CUT_COPPER_STAIRS;
+ case WEATHERED:
+ return waxed ? WAXED_WEATHERED_CUT_COPPER_STAIRS : WEATHERED_CUT_COPPER_STAIRS;
+ case OXIDIZED:
+ return waxed ? WAXED_OXIDIZED_CUT_COPPER_STAIRS : OXIDIZED_CUT_COPPER_STAIRS;
+ default:
+ return this.getId();
+ }
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.UNAFFECTED;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCutExposed.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCutExposed.java
new file mode 100644
index 00000000000..7cd15258d06
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCutExposed.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsCopperCutExposed extends BlockStairsCopperCut {
+
+ public BlockStairsCopperCutExposed() {
+ this(0);
+ }
+
+ public BlockStairsCopperCutExposed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return EXPOSED_CUT_COPPER_STAIRS;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.EXPOSED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.LIGHT_GRAY_TERRACOTA_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCutExposedWaxed.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCutExposedWaxed.java
new file mode 100644
index 00000000000..bce28120047
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCutExposedWaxed.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsCopperCutExposedWaxed extends BlockStairsCopperCutExposed {
+
+ public BlockStairsCopperCutExposedWaxed() {
+ this(0);
+ }
+
+ public BlockStairsCopperCutExposedWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_EXPOSED_CUT_COPPER_STAIRS;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCutOxidized.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCutOxidized.java
new file mode 100644
index 00000000000..cd6ff03f596
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCutOxidized.java
@@ -0,0 +1,31 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsCopperCutOxidized extends BlockStairsCopperCut {
+
+ public BlockStairsCopperCutOxidized() {
+ this(0);
+ }
+
+ public BlockStairsCopperCutOxidized(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return OXIDIZED_CUT_COPPER_STAIRS;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.OXIDIZED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_NYLIUM_BLOCK_COLOR;
+ }
+
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCutOxidizedWaxed.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCutOxidizedWaxed.java
new file mode 100644
index 00000000000..998288aecc9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCutOxidizedWaxed.java
@@ -0,0 +1,24 @@
+package cn.nukkit.block;
+
+public class BlockStairsCopperCutOxidizedWaxed extends BlockStairsCopperCutOxidized {
+
+ public BlockStairsCopperCutOxidizedWaxed() {
+ this(0);
+ }
+
+
+ public BlockStairsCopperCutOxidizedWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_OXIDIZED_CUT_COPPER_STAIRS;
+ }
+
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCutWaxed.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCutWaxed.java
new file mode 100644
index 00000000000..78715d789a0
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCutWaxed.java
@@ -0,0 +1,23 @@
+package cn.nukkit.block;
+
+public class BlockStairsCopperCutWaxed extends BlockStairsCopperCut {
+
+ public BlockStairsCopperCutWaxed() {
+ this(0);
+ }
+
+ public BlockStairsCopperCutWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_CUT_COPPER_STAIRS;
+ }
+
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCutWeathered.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCutWeathered.java
new file mode 100644
index 00000000000..734730109f3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCutWeathered.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsCopperCutWeathered extends BlockStairsCopperCut {
+
+ public BlockStairsCopperCutWeathered() {
+ this(0);
+ }
+
+ public BlockStairsCopperCutWeathered(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WEATHERED_CUT_COPPER_STAIRS;
+ }
+
+ @Override
+ public OxidizationLevel getOxidizationLevel() {
+ return OxidizationLevel.WEATHERED;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsCopperCutWeatheredWaxed.java b/src/main/java/cn/nukkit/block/BlockStairsCopperCutWeatheredWaxed.java
new file mode 100644
index 00000000000..8077378c085
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsCopperCutWeatheredWaxed.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsCopperCutWeatheredWaxed extends BlockStairsCopperCutWeathered {
+
+ public BlockStairsCopperCutWeatheredWaxed() {
+ this(0);
+ }
+
+ public BlockStairsCopperCutWeatheredWaxed(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WAXED_WEATHERED_CUT_COPPER_STAIRS;
+ }
+
+ @Override
+ public boolean isWaxed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsDarkOak.java b/src/main/java/cn/nukkit/block/BlockStairsDarkOak.java
index 9df9077d6cf..19d5b74ed98 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsDarkOak.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsDarkOak.java
@@ -30,5 +30,4 @@ public String getName() {
public BlockColor getColor() {
return BlockColor.BROWN_BLOCK_COLOR;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsDarkPrismarine.java b/src/main/java/cn/nukkit/block/BlockStairsDarkPrismarine.java
new file mode 100644
index 00000000000..ab5a7b36550
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsDarkPrismarine.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsDarkPrismarine extends BlockStairs {
+
+ public BlockStairsDarkPrismarine() {
+ this(0);
+ }
+
+ public BlockStairsDarkPrismarine(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DARK_PRISMARINE_STAIRS;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.8;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public String getName() {
+ return "Dark Prismarine Stairs";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DIAMOND_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsDeepslateCobbled.java b/src/main/java/cn/nukkit/block/BlockStairsDeepslateCobbled.java
new file mode 100644
index 00000000000..794129dee53
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsDeepslateCobbled.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsDeepslateCobbled extends BlockStairs {
+
+ public BlockStairsDeepslateCobbled() {
+ this(0);
+ }
+
+ public BlockStairsDeepslateCobbled(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return COBBLED_DEEPSLATE_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Cobbled Deepslate Stairs";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsDeepslatePolished.java b/src/main/java/cn/nukkit/block/BlockStairsDeepslatePolished.java
new file mode 100644
index 00000000000..f1b8dd94989
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsDeepslatePolished.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsDeepslatePolished extends BlockStairs {
+
+ public BlockStairsDeepslatePolished() {
+ this(0);
+ }
+
+ public BlockStairsDeepslatePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_DEEPSLATE_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Deepslate Stairs";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsDiorite.java b/src/main/java/cn/nukkit/block/BlockStairsDiorite.java
new file mode 100644
index 00000000000..647d9186b61
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsDiorite.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsDiorite extends BlockStairs {
+
+ public BlockStairsDiorite() {
+ this(0);
+ }
+
+ public BlockStairsDiorite(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Diorite Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return DIORITE_STAIRS;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 30;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.QUARTZ_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsDioritePolished.java b/src/main/java/cn/nukkit/block/BlockStairsDioritePolished.java
new file mode 100644
index 00000000000..a440fd0429e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsDioritePolished.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsDioritePolished extends BlockStairsDiorite {
+
+ public BlockStairsDioritePolished() {
+ this(0);
+ }
+
+ public BlockStairsDioritePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Diorite Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_DIORITE_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsEndBrick.java b/src/main/java/cn/nukkit/block/BlockStairsEndBrick.java
new file mode 100644
index 00000000000..81f71de42f2
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsEndBrick.java
@@ -0,0 +1,44 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockStairsEndBrick extends BlockStairs {
+
+ public BlockStairsEndBrick() {
+ this(0);
+ }
+
+ public BlockStairsEndBrick(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "End Brick Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return END_BRICK_STAIRS;
+ }
+
+ @Override
+ public double getHardness() {
+ return 2; //3
+ }
+
+ @Override
+ public double getResistance() {
+ return 9;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsGranite.java b/src/main/java/cn/nukkit/block/BlockStairsGranite.java
new file mode 100644
index 00000000000..33cdb328ffc
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsGranite.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsGranite extends BlockStairs {
+
+ public BlockStairsGranite() {
+ this(0);
+ }
+
+ public BlockStairsGranite(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Granite Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return GRANITE_STAIRS;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 30;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DIRT_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsGranitePolished.java b/src/main/java/cn/nukkit/block/BlockStairsGranitePolished.java
new file mode 100644
index 00000000000..64d7f6bc12f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsGranitePolished.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsGranitePolished extends BlockStairsGranite {
+
+ public BlockStairsGranitePolished() {
+ this(0);
+ }
+
+ public BlockStairsGranitePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Granite Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_GRANITE_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsJungle.java b/src/main/java/cn/nukkit/block/BlockStairsJungle.java
index 7af5f2b3d9a..390f7915a62 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsJungle.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsJungle.java
@@ -30,5 +30,4 @@ public String getName() {
public BlockColor getColor() {
return BlockColor.DIRT_BLOCK_COLOR;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsMossyCobblestone.java b/src/main/java/cn/nukkit/block/BlockStairsMossyCobblestone.java
new file mode 100644
index 00000000000..37515a34892
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsMossyCobblestone.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsMossyCobblestone extends BlockStairsCobblestone {
+
+ public BlockStairsMossyCobblestone() {
+ this(0);
+ }
+
+ public BlockStairsMossyCobblestone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Mossy Cobblestone Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return MOSSY_COBBLESTONE_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsMossyStoneBrick.java b/src/main/java/cn/nukkit/block/BlockStairsMossyStoneBrick.java
new file mode 100644
index 00000000000..b35398ff8a6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsMossyStoneBrick.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsMossyStoneBrick extends BlockStairsStoneBrick {
+
+ public BlockStairsMossyStoneBrick() {
+ this(0);
+ }
+
+ public BlockStairsMossyStoneBrick(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Mossy Stone Brick Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return MOSSY_STONE_BRICK_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsNetherBrick.java b/src/main/java/cn/nukkit/block/BlockStairsNetherBrick.java
index a45d63d204f..f64eae90c38 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsNetherBrick.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsNetherBrick.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsNetherBrick extends BlockStairs {
+
public BlockStairsNetherBrick() {
this(0);
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsPrismarine.java b/src/main/java/cn/nukkit/block/BlockStairsPrismarine.java
new file mode 100644
index 00000000000..276ea09ac99
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsPrismarine.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsPrismarine extends BlockStairs {
+
+ public BlockStairsPrismarine() {
+ this(0);
+ }
+
+ public BlockStairsPrismarine(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return PRISMARINE_STAIRS;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.8;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public String getName() {
+ return "Prismarine Stairs";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CYAN_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsPrismarineBrick.java b/src/main/java/cn/nukkit/block/BlockStairsPrismarineBrick.java
new file mode 100644
index 00000000000..428937e611a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsPrismarineBrick.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsPrismarineBrick extends BlockStairsPrismarine {
+
+ public BlockStairsPrismarineBrick() {
+ this(0);
+ }
+
+ public BlockStairsPrismarineBrick(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return PRISMARINE_BRICKS_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Prismarine Brick Stairs";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DIAMOND_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsPurpur.java b/src/main/java/cn/nukkit/block/BlockStairsPurpur.java
index 46d4dd53533..d374d5f42b9 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsPurpur.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsPurpur.java
@@ -42,4 +42,9 @@ public String getName() {
public BlockColor getColor() {
return BlockColor.MAGENTA_BLOCK_COLOR;
}
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsQuartz.java b/src/main/java/cn/nukkit/block/BlockStairsQuartz.java
index 9482765d3c3..d9aac17c7ef 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsQuartz.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsQuartz.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsQuartz extends BlockStairs {
+
public BlockStairsQuartz() {
this(0);
}
@@ -23,12 +24,12 @@ public int getId() {
@Override
public double getHardness() {
- return 0.8;
+ return 2;
}
@Override
public double getResistance() {
- return 4;
+ return 6;
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockStairsRedNetherBrick.java b/src/main/java/cn/nukkit/block/BlockStairsRedNetherBrick.java
new file mode 100644
index 00000000000..a7b4f06aed3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsRedNetherBrick.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsRedNetherBrick extends BlockStairsNetherBrick {
+
+ public BlockStairsRedNetherBrick() {
+ this(0);
+ }
+
+ public BlockStairsRedNetherBrick(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Red Nether Brick Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return RED_NETHER_BRICK_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsRedSandstone.java b/src/main/java/cn/nukkit/block/BlockStairsRedSandstone.java
index 9aa25e29e27..0de03be8c2c 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsRedSandstone.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsRedSandstone.java
@@ -45,7 +45,7 @@ public String getName() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -68,4 +68,4 @@ public boolean canHarvestWithHand() {
public BlockColor getColor() {
return BlockColor.ORANGE_BLOCK_COLOR;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsSandstone.java b/src/main/java/cn/nukkit/block/BlockStairsSandstone.java
index 18557d87a85..a3597e02dd2 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsSandstone.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsSandstone.java
@@ -8,6 +8,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsSandstone extends BlockStairs {
+
public BlockStairsSandstone() {
this(0);
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsSmoothQuartz.java b/src/main/java/cn/nukkit/block/BlockStairsSmoothQuartz.java
new file mode 100644
index 00000000000..66c0564d520
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsSmoothQuartz.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockStairsSmoothQuartz extends BlockStairsQuartz {
+
+ public BlockStairsSmoothQuartz() {
+ this(0);
+ }
+
+ public BlockStairsSmoothQuartz(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Smooth Quartz Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return SMOOTH_QUARTZ_STAIRS;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsSmoothRedSandstone.java b/src/main/java/cn/nukkit/block/BlockStairsSmoothRedSandstone.java
new file mode 100644
index 00000000000..9633b01fcdb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsSmoothRedSandstone.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockStairsSmoothRedSandstone extends BlockStairsRedSandstone {
+
+ public BlockStairsSmoothRedSandstone() {
+ this(0);
+ }
+
+ public BlockStairsSmoothRedSandstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Smooth RedSand stone Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return SMOOTH_RED_SANDSTONE_STAIRS;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsSmoothSandstone.java b/src/main/java/cn/nukkit/block/BlockStairsSmoothSandstone.java
new file mode 100644
index 00000000000..58b743c4fbc
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsSmoothSandstone.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockStairsSmoothSandstone extends BlockStairsSandstone {
+
+ public BlockStairsSmoothSandstone() {
+ this(0);
+ }
+
+ public BlockStairsSmoothSandstone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Smooth Sandstone Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return SMOOTH_SANDSTONE_STAIRS;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsSpruce.java b/src/main/java/cn/nukkit/block/BlockStairsSpruce.java
index 09a54a098fe..cb9b3cfb789 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsSpruce.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsSpruce.java
@@ -30,5 +30,4 @@ public String getName() {
public BlockColor getColor() {
return BlockColor.SPRUCE_BLOCK_COLOR;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsStone.java b/src/main/java/cn/nukkit/block/BlockStairsStone.java
new file mode 100644
index 00000000000..9f357417130
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsStone.java
@@ -0,0 +1,44 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockStairsStone extends BlockStairs {
+
+ public BlockStairsStone() {
+ this(0);
+ }
+
+ public BlockStairsStone(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stone Stairs";
+ }
+
+ @Override
+ public int getId() {
+ return NORMAL_STONE_STAIRS;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 30;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsStoneBrick.java b/src/main/java/cn/nukkit/block/BlockStairsStoneBrick.java
index d84255e8295..a7145c21dfa 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsStoneBrick.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsStoneBrick.java
@@ -7,6 +7,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsStoneBrick extends BlockStairs {
+
public BlockStairsStoneBrick() {
this(0);
}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsTileDeepslate.java b/src/main/java/cn/nukkit/block/BlockStairsTileDeepslate.java
new file mode 100644
index 00000000000..0984a0559a6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStairsTileDeepslate.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStairsTileDeepslate extends BlockStairs {
+
+ public BlockStairsTileDeepslate() {
+ this(0);
+ }
+
+ public BlockStairsTileDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_TILE_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Tile Stairs";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStairsWood.java b/src/main/java/cn/nukkit/block/BlockStairsWood.java
index 11ae3209cf9..1add1fc352c 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsWood.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsWood.java
@@ -10,6 +10,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsWood extends BlockStairs {
+
public BlockStairsWood() {
this(0);
}
@@ -35,7 +36,7 @@ public int getToolType() {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
@@ -62,6 +63,7 @@ public int getBurnAbility() {
public BlockColor getColor() {
return BlockColor.WOOD_BLOCK_COLOR;
}
+
@Override
public Item[] getDrops(Item item) {
return new Item[]{
diff --git a/src/main/java/cn/nukkit/block/BlockStem.java b/src/main/java/cn/nukkit/block/BlockStem.java
new file mode 100644
index 00000000000..daf07887f48
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStem.java
@@ -0,0 +1,73 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+
+public abstract class BlockStem extends BlockSolidMeta {
+
+ private static final short[] faces = new short[]{
+ 0,
+ 0,
+ 2,
+ 2,
+ 1,
+ 1
+ };
+
+ public BlockStem() {
+ this(0);
+ }
+
+ protected BlockStem(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(faces[face.getIndex()]);
+ this.getLevel().setBlock(block, this, true, true);
+ return true;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.isAxe()) {
+ Block strippedBlock = Block.get(this.getStrippedId());
+ strippedBlock.setDamage(this.getDamage());
+ item.useOn(this);
+ this.level.setBlock(this, strippedBlock, true, true);
+ return true;
+ }
+ return false;
+ }
+
+ public abstract int getStrippedId();
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(this, 0);
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 2;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockStemMelon.java b/src/main/java/cn/nukkit/block/BlockStemMelon.java
index 90b32f823cc..abeaed8b0f7 100644
--- a/src/main/java/cn/nukkit/block/BlockStemMelon.java
+++ b/src/main/java/cn/nukkit/block/BlockStemMelon.java
@@ -3,11 +3,10 @@
import cn.nukkit.Server;
import cn.nukkit.event.block.BlockGrowEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSeedsMelon;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.BlockFace.Plane;
-import cn.nukkit.math.NukkitRandom;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 15.01.2016.
@@ -40,8 +39,7 @@ public int onUpdate(int type) {
return Level.BLOCK_UPDATE_NORMAL;
}
} else if (type == Level.BLOCK_UPDATE_RANDOM) {
- NukkitRandom random = new NukkitRandom();
- if (random.nextRange(1, 2) == 1) {
+ if (Utils.rand()) {
if (this.getDamage() < 0x07) {
Block block = this.clone();
block.setDamage(block.getDamage() + 1);
@@ -58,10 +56,10 @@ public int onUpdate(int type) {
return Level.BLOCK_UPDATE_RANDOM;
}
}
- Block side = this.getSide(Plane.HORIZONTAL.random(random));
- Block d = side.down();
- if (side.getId() == AIR && (d.getId() == FARMLAND || d.getId() == GRASS || d.getId() == DIRT)) {
- BlockGrowEvent ev = new BlockGrowEvent(side, Block.get(BlockID.MELON_BLOCK));
+ Block side = this.getSide(Plane.HORIZONTAL.random());
+ Block d;
+ if (side.getId() == AIR && ((d = side.down()).getId() == FARMLAND || d.getId() == GRASS || d.getId() == DIRT)) {
+ BlockGrowEvent ev = new BlockGrowEvent(side, Block.get(MELON_BLOCK));
Server.getInstance().getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
this.getLevel().setBlock(side, ev.getNewState(), true);
@@ -76,14 +74,19 @@ public int onUpdate(int type) {
@Override
public Item toItem() {
- return new ItemSeedsMelon();
+ return Item.get(Item.MELON_SEEDS);
}
@Override
public Item[] getDrops(Item item) {
- NukkitRandom random = new NukkitRandom();
+ if (this.getDamage() < 4) return new Item[0];
return new Item[]{
- new ItemSeedsMelon(0, random.nextRange(0, 3))
+ Item.get(Item.MELON_SEEDS, 0, Utils.rand(0, 48) >> 4)
};
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockStemPumpkin.java b/src/main/java/cn/nukkit/block/BlockStemPumpkin.java
index e5982255dca..308ded3d6ab 100644
--- a/src/main/java/cn/nukkit/block/BlockStemPumpkin.java
+++ b/src/main/java/cn/nukkit/block/BlockStemPumpkin.java
@@ -3,11 +3,10 @@
import cn.nukkit.Server;
import cn.nukkit.event.block.BlockGrowEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSeedsPumpkin;
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.BlockFace.Plane;
-import cn.nukkit.math.NukkitRandom;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 15.01.2016.
@@ -40,8 +39,7 @@ public int onUpdate(int type) {
return Level.BLOCK_UPDATE_NORMAL;
}
} else if (type == Level.BLOCK_UPDATE_RANDOM) {
- NukkitRandom random = new NukkitRandom();
- if (random.nextRange(1, 2) == 1) {
+ if (Utils.rand()) {
if (this.getDamage() < 0x07) {
Block block = this.clone();
block.setDamage(block.getDamage() + 1);
@@ -58,10 +56,10 @@ public int onUpdate(int type) {
return Level.BLOCK_UPDATE_RANDOM;
}
}
- Block side = this.getSide(Plane.HORIZONTAL.random(random));
- Block d = side.down();
- if (side.getId() == AIR && (d.getId() == FARMLAND || d.getId() == GRASS || d.getId() == DIRT)) {
- BlockGrowEvent ev = new BlockGrowEvent(side, Block.get(BlockID.PUMPKIN));
+ Block side = this.getSide(Plane.HORIZONTAL.random());
+ Block d;
+ if (side.getId() == AIR && ((d = side.down()).getId() == FARMLAND || d.getId() == GRASS || d.getId() == DIRT)) {
+ BlockGrowEvent ev = new BlockGrowEvent(side, Block.get(PUMPKIN));
Server.getInstance().getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
this.getLevel().setBlock(side, ev.getNewState(), true);
@@ -76,14 +74,19 @@ public int onUpdate(int type) {
@Override
public Item toItem() {
- return new ItemSeedsPumpkin();
+ return Item.get(Item.PUMPKIN_SEEDS);
}
@Override
public Item[] getDrops(Item item) {
- NukkitRandom random = new NukkitRandom();
+ if (this.getDamage() < 4) return new Item[0];
return new Item[]{
- new ItemSeedsPumpkin(0, random.nextRange(0, 3))
+ Item.get(Item.PUMPKIN_SEEDS, 0, Utils.rand(0, 48) >> 4)
};
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockStemStripped.java b/src/main/java/cn/nukkit/block/BlockStemStripped.java
new file mode 100644
index 00000000000..affcf5cb935
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStemStripped.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+
+public abstract class BlockStemStripped extends BlockStem {
+
+ public BlockStemStripped() {
+ this(0);
+ }
+
+ public BlockStemStripped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return false;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ return false;
+ }
+
+ @Override
+ public int getStrippedId() {
+ return this.getId();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockStone.java b/src/main/java/cn/nukkit/block/BlockStone.java
index 15f01b008ab..0caf20472bb 100644
--- a/src/main/java/cn/nukkit/block/BlockStone.java
+++ b/src/main/java/cn/nukkit/block/BlockStone.java
@@ -2,12 +2,15 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockStone extends BlockSolidMeta {
+
public static final int NORMAL = 0;
public static final int GRANITE = 1;
public static final int POLISHED_GRANITE = 2;
@@ -16,6 +19,16 @@ public class BlockStone extends BlockSolidMeta {
public static final int ANDESITE = 5;
public static final int POLISHED_ANDESITE = 6;
+ private static final String[] names = {
+ "Stone",
+ "Granite",
+ "Polished Granite",
+ "Diorite",
+ "Polished Diorite",
+ "Andesite",
+ "Polished Andesite",
+ "Unknown Stone"
+ };
public BlockStone() {
this(0);
@@ -37,7 +50,7 @@ public double getHardness() {
@Override
public double getResistance() {
- return 10;
+ return 30;
}
@Override
@@ -47,22 +60,15 @@ public int getToolType() {
@Override
public String getName() {
- String[] names = new String[]{
- "Stone",
- "Granite",
- "Polished Granite",
- "Diorite",
- "Polished Diorite",
- "Andesite",
- "Polished Andesite",
- "Unknown Stone"
- };
return names[this.getDamage() & 0x07];
}
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{this.toItem()};
+ }
return new Item[]{
Item.get(this.getDamage() == 0 ? Item.COBBLESTONE : Item.STONE, this.getDamage(), 1)
};
@@ -80,4 +86,15 @@ public boolean canHarvestWithHand() {
public boolean canSilkTouch() {
return true;
}
+
+ @Override
+ public BlockColor getColor() {
+ int damage = this.getDamage() & 0x07;
+ if (damage == GRANITE || damage == POLISHED_GRANITE) {
+ return BlockColor.DIRT_BLOCK_COLOR;
+ } else if (damage == DIORITE || damage == POLISHED_DIORITE) {
+ return BlockColor.QUARTZ_BLOCK_COLOR;
+ }
+ return BlockColor.STONE_BLOCK_COLOR;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockStonecutter.java b/src/main/java/cn/nukkit/block/BlockStonecutter.java
index f077ac20d58..14cd4bdc7aa 100644
--- a/src/main/java/cn/nukkit/block/BlockStonecutter.java
+++ b/src/main/java/cn/nukkit/block/BlockStonecutter.java
@@ -1,52 +1,53 @@
-package cn.nukkit.block;
-
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
-
-public class BlockStonecutter extends BlockSolid {
-
- public BlockStonecutter() {
-
- }
-
- @Override
- public int getId() {
- return STONECUTTER;
- }
-
- @Override
- public String getName() {
- return "Stonecutter";
- }
-
- @Override
- public double getHardness() {
- return 3.5;
- }
-
- @Override
- public double getResistance() {
- return 17.5;
- }
-
- @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[]{
- this.toItem()
- };
- } else {
- return new Item[0];
- }
- }
-
- @Override
- public boolean canHarvestWithHand() {
- return false;
- }
-}
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+
+public class BlockStonecutter extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return STONECUTTER;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 17.5;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public String getName() {
+ return "Stonecutter";
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.isPickaxe()) {
+ return new Item[]{
+ toItem()
+ };
+ } else {
+ return new Item[0];
+ }
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStonecutterBlock.java b/src/main/java/cn/nukkit/block/BlockStonecutterBlock.java
new file mode 100644
index 00000000000..c36868440ce
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStonecutterBlock.java
@@ -0,0 +1,64 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.Faceable;
+
+public class BlockStonecutterBlock extends BlockSolidMeta implements Faceable {
+
+ public BlockStonecutterBlock() {
+ this(0);
+ }
+
+ public BlockStonecutterBlock(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stonecutter Block";
+ }
+
+ @Override
+ public int getId() {
+ return STONECUTTER_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 17.5;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public double getMaxY() {
+ return y + 0.5625;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ return super.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ @Override
+ public BlockFace getBlockFace() {
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStrippedCrimsonStem.java b/src/main/java/cn/nukkit/block/BlockStrippedCrimsonStem.java
new file mode 100644
index 00000000000..4c75f67c1a0
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStrippedCrimsonStem.java
@@ -0,0 +1,39 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStrippedCrimsonStem extends BlockStemStripped {
+
+ public BlockStrippedCrimsonStem() {
+ this(0);
+ }
+
+ public BlockStrippedCrimsonStem(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Crimson Stem";
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_CRIMSON_STEM;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CRIMSON_STEM_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockStrippedWarpedStem.java b/src/main/java/cn/nukkit/block/BlockStrippedWarpedStem.java
new file mode 100644
index 00000000000..25f559f2b7a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStrippedWarpedStem.java
@@ -0,0 +1,39 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStrippedWarpedStem extends BlockStemStripped {
+
+ public BlockStrippedWarpedStem() {
+ this(0);
+ }
+
+ public BlockStrippedWarpedStem(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_WARPED_STEM;
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Warped Stem";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockStructureBlock.java b/src/main/java/cn/nukkit/block/BlockStructureBlock.java
new file mode 100644
index 00000000000..7bda561e5f9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockStructureBlock.java
@@ -0,0 +1,47 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockStructureBlock extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return STRUCTURE_BLOCK;
+ }
+
+ @Override
+ public double getHardness() {
+ return -1;
+ }
+
+ @Override
+ public double getResistance() {
+ return 18000000;
+ }
+
+ @Override
+ public String getName() {
+ return "Structure Block";
+ }
+
+ @Override
+ public boolean isBreakable(Item item) {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.LIGHT_GRAY_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSugarcane.java b/src/main/java/cn/nukkit/block/BlockSugarcane.java
index a5c0eea8abb..ef2cef0f56a 100644
--- a/src/main/java/cn/nukkit/block/BlockSugarcane.java
+++ b/src/main/java/cn/nukkit/block/BlockSugarcane.java
@@ -4,11 +4,10 @@
import cn.nukkit.Server;
import cn.nukkit.event.block.BlockGrowEvent;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSugarcane;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.level.Level;
import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.Vector3;
import cn.nukkit.utils.BlockColor;
/**
@@ -26,7 +25,7 @@ public BlockSugarcane(int meta) {
@Override
public String getName() {
- return "Sugarcane";
+ return "Sugar Cane";
}
@Override
@@ -36,7 +35,7 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemSugarcane();
+ return Item.get(Item.SUGARCANE);
}
@Override
@@ -46,7 +45,7 @@ public boolean canBeActivated() {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.getId() == Item.DYE && item.getDamage() == 0x0F) { //Bonemeal
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
int count = 1;
for (int i = 1; i <= 2; i++) {
@@ -64,7 +63,7 @@ public boolean onActivate(Item item, Player player) {
for (int i = 1; i <= toGrow; i++) {
Block block = this.up(i);
if (block.getId() == 0) {
- BlockGrowEvent ev = new BlockGrowEvent(block, Block.get(BlockID.SUGARCANE_BLOCK));
+ BlockGrowEvent ev = new BlockGrowEvent(block, Block.get(SUGARCANE_BLOCK));
Server.getInstance().getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
@@ -77,14 +76,13 @@ public boolean onActivate(Item item, Player player) {
}
if (success) {
- if (player != null && (player.gamemode & 0x01) == 0) {
+ if (player != null && !player.isCreative()) {
item.count--;
}
this.level.addParticle(new BoneMealParticle(this));
}
}
-
return true;
}
return false;
@@ -102,23 +100,22 @@ public int onUpdate(int type) {
if (this.down().getId() != SUGARCANE_BLOCK) {
if (this.getDamage() == 0x0F) {
for (int y = 1; y < 3; ++y) {
- Block b = this.getLevel().getBlock(new Vector3(this.x, this.y + y, this.z));
+ Block b = this.getLevel().getBlock((int) this.x, (int) this.y + y, (int) this.z);
if (b.getId() == AIR) {
BlockGrowEvent ev = new BlockGrowEvent(b, Block.get(BlockID.SUGARCANE_BLOCK));
Server.getInstance().getPluginManager().callEvent(ev);
-
+
if (!ev.isCancelled()) {
- this.getLevel().setBlock(b, Block.get(BlockID.SUGARCANE_BLOCK), false);
+ this.getLevel().setBlock(b, ev.getNewState(), false);
}
break;
}
}
this.setDamage(0);
- this.getLevel().setBlock(this, this, false);
} else {
this.setDamage(this.getDamage() + 1);
- this.getLevel().setBlock(this, this, false);
}
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, true, false); // No need to send this to client
return Level.BLOCK_UPDATE_RANDOM;
}
}
@@ -133,15 +130,15 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
Block down = this.down();
int id = down.getId();
if (id == SUGARCANE_BLOCK) {
- this.getLevel().setBlock(block, Block.get(BlockID.SUGARCANE_BLOCK), true);
+ this.getLevel().setBlock(block, Block.get(SUGARCANE_BLOCK), true);
return true;
} else if (id == GRASS || id == DIRT || id == SAND || id == PODZOL || id == MYCELIUM) {
Block block0 = down.north();
Block block1 = down.south();
Block block2 = down.west();
Block block3 = down.east();
- if ((block0 instanceof BlockWater) || (block1 instanceof BlockWater) || (block2 instanceof BlockWater) || (block3 instanceof BlockWater)) {
- this.getLevel().setBlock(block, Block.get(BlockID.SUGARCANE_BLOCK), true);
+ if (block0 instanceof BlockWater || block1 instanceof BlockWater || block2 instanceof BlockWater || block3 instanceof BlockWater || block0 instanceof BlockIceFrosted || block1 instanceof BlockIceFrosted || block2 instanceof BlockIceFrosted || block3 instanceof BlockIceFrosted) {
+ this.getLevel().setBlock(block, Block.get(SUGARCANE_BLOCK), true);
return true;
}
}
@@ -152,4 +149,9 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
public BlockColor getColor() {
return BlockColor.FOLIAGE_BLOCK_COLOR;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockSweetBerryBush.java b/src/main/java/cn/nukkit/block/BlockSweetBerryBush.java
new file mode 100644
index 00000000000..25b8cecd20d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSweetBerryBush.java
@@ -0,0 +1,206 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.item.EntityItem;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.event.block.BlockHarvestEvent;
+import cn.nukkit.event.entity.EntityDamageByBlockEvent;
+import cn.nukkit.event.entity.EntityDamageEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.math.MathHelper;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.DyeColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockSweetBerryBush extends BlockFlowable {
+
+ public BlockSweetBerryBush() {
+ this(0);
+ }
+
+ public BlockSweetBerryBush(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Sweet Berry Bush";
+ }
+
+ @Override
+ public int getId() {
+ return SWEET_BERRY_BUSH;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 30;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 60;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.SWEET_BERRIES);
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (this.getDamage() > 0 && !(entity instanceof EntityItem)) {
+ entity.resetFallDistance();
+ if (!entity.isSneaking() && ThreadLocalRandom.current().nextInt(20) == 0) {
+ if (entity.attack(new EntityDamageByBlockEvent(this, entity, EntityDamageEvent.DamageCause.CONTACT, 1))) {
+ this.level.addLevelSoundEvent(entity, LevelSoundEventPacket.SOUND_BLOCK_SWEET_BERRY_BUSH_HURT);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return this.getDamage() > 0;
+ }
+
+ protected AxisAlignedBB recalculateBoundingBox() {
+ if (this.getDamage() > 0) {
+ return this;
+ }
+ return null;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ int age = MathHelper.clamp(getDamage(), 0, 3);
+
+ int amount = 1;
+ if (age > 1) {
+ amount = 1 + ThreadLocalRandom.current().nextInt(2);
+ if (age == 3) {
+ amount++;
+ }
+ }
+
+ return new Item[]{Item.get(ItemID.SWEET_BERRIES, 0, amount)};
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ int age = MathHelper.clamp(this.getDamage(), 0, 3);
+
+ if (age < 3 && item.getId() == ItemID.DYE && item.getDamage() == DyeColor.WHITE.getDyeData()) {
+ BlockSweetBerryBush block = (BlockSweetBerryBush) this.clone();
+ block.setDamage(block.getDamage() + 1);
+ if (block.getDamage() > 3) {
+ block.setDamage(3);
+ }
+
+ BlockGrowEvent ev = new BlockGrowEvent(this, block);
+ this.getLevel().getServer().getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return false;
+ }
+
+ this.getLevel().setBlock(this, ev.getNewState(), false, true);
+ this.level.addParticle(new BoneMealParticle(this));
+
+ if (player != null && (player.gamemode & 0x01) == 0) {
+ item.count--;
+ }
+ return true;
+ }
+
+ if (age < 2) {
+ return true;
+ }
+
+ int amount = 1 + ThreadLocalRandom.current().nextInt(2);
+ if (age == 3) {
+ amount++;
+ }
+
+ BlockHarvestEvent event = new BlockHarvestEvent(this, Block.get(SWEET_BERRY_BUSH, 1), new Item[]{Item.get(ItemID.SWEET_BERRIES, 0, amount)});
+ this.getLevel().getServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(this, event.getNewState(), true, true);
+ Item[] drops = event.getDrops();
+ if (drops != null) {
+ Position dropPos = add(0.5, 0.5, 0.5);
+ for (Item drop : drops) {
+ if (drop != null) {
+ this.getLevel().dropItem(dropPos, drop);
+ }
+ }
+ }
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_SWEET_BERRY_BUSH_PICK);
+ }
+ return true;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!isSupportValid(this.down())) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+ } else if (type == Level.BLOCK_UPDATE_RANDOM) {
+ if (this.getDamage() < 3 && ThreadLocalRandom.current().nextInt(5) == 0) {
+ BlockGrowEvent event = new BlockGrowEvent(this, Block.get(this.getId(), this.getDamage() + 1));
+ if (!event.isCancelled()) {
+ this.getLevel().setBlock(this, event.getNewState(), true, true);
+ }
+ }
+ return type;
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (target.getId() == SWEET_BERRY_BUSH || block.getId() != AIR) {
+ return false;
+ }
+ if (isSupportValid(this.down())) {
+ this.getLevel().setBlock(block, this, true);
+ return true;
+ }
+ return false;
+ }
+
+
+ public static boolean isSupportValid(Block block) {
+ switch (block.getId()) {
+ case GRASS:
+ case DIRT:
+ case PODZOL:
+ case MYCELIUM:
+ case FARMLAND:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.FOLIAGE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTNT.java b/src/main/java/cn/nukkit/block/BlockTNT.java
index 462093738dc..f00f69d6952 100644
--- a/src/main/java/cn/nukkit/block/BlockTNT.java
+++ b/src/main/java/cn/nukkit/block/BlockTNT.java
@@ -2,16 +2,19 @@
import cn.nukkit.Player;
import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.item.EntityPrimedTNT;
+import cn.nukkit.entity.projectile.EntityArrow;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemTool;
import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.level.Level;
-import cn.nukkit.math.NukkitRandom;
+import cn.nukkit.level.Sound;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.DoubleTag;
import cn.nukkit.nbt.tag.FloatTag;
import cn.nukkit.nbt.tag.ListTag;
-import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/12/8 by xtypr.
@@ -19,9 +22,6 @@
*/
public class BlockTNT extends BlockSolid {
- public BlockTNT() {
- }
-
@Override
public String getName() {
return "TNT";
@@ -67,7 +67,7 @@ public void prime(int fuse) {
public void prime(int fuse, Entity source) {
this.getLevel().setBlock(this, Block.get(BlockID.AIR), true);
- double mot = (new NukkitRandom()).nextSignedFloat() * Math.PI * 2;
+ double mot = Utils.nukkitRandom.nextSignedFloat() * 6.283185307179586;
CompoundTag nbt = new CompoundTag()
.putList(new ListTag("Pos")
.add(new DoubleTag("", this.x + 0.5))
@@ -80,21 +80,17 @@ public void prime(int fuse, Entity source) {
.putList(new ListTag("Rotation")
.add(new FloatTag("", 0))
.add(new FloatTag("", 0)))
- .putShort("Fuse", fuse);
- Entity tnt = Entity.createEntity("PrimedTnt",
- this.getLevel().getChunk(this.getFloorX() >> 4, this.getFloorZ() >> 4),
+ .putByte("Fuse", fuse);
+ Entity tnt = new EntityPrimedTNT(
+ this.getLevel().getChunk(this.getChunkX(), this.getChunkZ()),
nbt, source
);
- if(tnt == null) {
- return;
- }
tnt.spawnToAll();
- this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_TNT);
}
@Override
public int onUpdate(int type) {
- if ((type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) && this.level.isBlockPowered(this.getLocation())) {
+ if ((type == Level.BLOCK_UPDATE_NORMAL || type == Level.BLOCK_UPDATE_REDSTONE) && this.level.isBlockPowered(this)) {
this.prime();
}
@@ -109,13 +105,15 @@ public boolean onActivate(Item item, Player player) {
return true;
} else if (item.getId() == Item.FIRE_CHARGE) {
if (!player.isCreative()) item.count--;
+ this.level.addSound(this, Sound.MOB_GHAST_FIREBALL);
this.prime(80, player);
return true;
- } else if (item.hasEnchantment(Enchantment.ID_FIRE_ASPECT)) {
+ } else if (item instanceof ItemTool && item.hasEnchantment(Enchantment.ID_FIRE_ASPECT)) {
item.useOn(this);
this.prime(80, player);
return true;
}
+
return false;
}
@@ -123,4 +121,17 @@ public boolean onActivate(Item item, Player player) {
public BlockColor getColor() {
return BlockColor.TNT_BLOCK_COLOR;
}
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (entity instanceof EntityArrow && entity.isOnFire()) {
+ entity.close();
+ this.prime();
+ }
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockTallGrass.java b/src/main/java/cn/nukkit/block/BlockTallGrass.java
index bdeebd2f16a..76cf82b3077 100644
--- a/src/main/java/cn/nukkit/block/BlockTallGrass.java
+++ b/src/main/java/cn/nukkit/block/BlockTallGrass.java
@@ -2,17 +2,16 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSeedsWheat;
+import cn.nukkit.item.ItemDye;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
import cn.nukkit.level.particle.BoneMealParticle;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
-
-import java.util.concurrent.ThreadLocalRandom;
+import cn.nukkit.utils.Utils;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockTallGrass extends BlockFlowable {
@@ -32,7 +31,7 @@ public int getId() {
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Grass",
"Grass",
"Fern",
@@ -85,7 +84,7 @@ public int onUpdate(int type) {
@Override
public boolean onActivate(Item item, Player player) {
- if (item.getId() == Item.DYE && item.getDamage() == 0x0f) {
+ if (item.getId() == Item.DYE && item.getDamage() == ItemDye.BONE_MEAL) {
Block up = this.up();
if (up.getId() == AIR) {
@@ -105,7 +104,7 @@ public boolean onActivate(Item item, Player player) {
}
if (meta != -1) {
- if (player != null && (player.gamemode & 0x01) == 0) {
+ if (player != null && !player.isCreative()) {
item.count--;
}
@@ -121,26 +120,18 @@ public boolean onActivate(Item item, Player player) {
return false;
}
+
@Override
public Item[] getDrops(Item item) {
- boolean dropSeeds = ThreadLocalRandom.current().nextInt(10) == 0;
if (item.isShears()) {
- //todo enchantment
- if (dropSeeds) {
- return new Item[]{
- new ItemSeedsWheat(),
- Item.get(Item.TALL_GRASS, this.getDamage(), 1)
- };
- } else {
- return new Item[]{
- Item.get(Item.TALL_GRASS, this.getDamage(), 1)
- };
- }
+ return new Item[]{
+ Item.get(Item.TALL_GRASS, this.getDamage(), 1)
+ };
}
- if (dropSeeds) {
+ if (Utils.random.nextInt(10) == 0) {
return new Item[]{
- new ItemSeedsWheat()
+ Item.get(Item.WHEAT_SEEDS)
};
} else {
return new Item[0];
@@ -156,4 +147,9 @@ public int getToolType() {
public BlockColor getColor() {
return BlockColor.FOLIAGE_BLOCK_COLOR;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockTarget.java b/src/main/java/cn/nukkit/block/BlockTarget.java
new file mode 100644
index 00000000000..287c2144bca
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTarget.java
@@ -0,0 +1,109 @@
+package cn.nukkit.block;
+
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.projectile.EntityArrow;
+import cn.nukkit.entity.projectile.EntityProjectile;
+import cn.nukkit.entity.projectile.EntityThrownTrident;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTarget extends BlockSolidMeta {
+
+ public BlockTarget() {
+ this(0);
+ }
+
+ public BlockTarget(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Target";
+ }
+
+ @Override
+ public int getId() {
+ return TARGET;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 5;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 15;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.5;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WHITE_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[]{new ItemBlock(Block.get(TARGET))};
+ }
+
+ @Override
+ public boolean isPowerSource() {
+ return true;
+ }
+
+ @Override
+ public int getWeakPower(BlockFace face) {
+ return this.getDamage() > 0 ? 10 : 0;
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (entity instanceof EntityProjectile) {
+ this.setDamage(1);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, true, false); // No need to send this to client
+ this.level.updateAroundRedstone(this, null);
+
+ if (entity instanceof EntityArrow || entity instanceof EntityThrownTrident) {
+ this.level.scheduleUpdate(this, 20);
+ } else {
+ this.level.scheduleUpdate(this, 8);
+ }
+ }
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_SCHEDULED) {
+ this.setDamage(0);
+ this.level.setBlock((int) this.x, (int) this.y, (int) this.z, BlockLayer.NORMAL, this, false, true, false); // No need to send this to client
+ this.level.updateAroundRedstone(this, null);
+ return Level.BLOCK_UPDATE_SCHEDULED;
+ }
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTerracotta.java b/src/main/java/cn/nukkit/block/BlockTerracotta.java
index 58f163a0379..d7aa085192d 100644
--- a/src/main/java/cn/nukkit/block/BlockTerracotta.java
+++ b/src/main/java/cn/nukkit/block/BlockTerracotta.java
@@ -3,6 +3,7 @@
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.DyeColor;
import cn.nukkit.utils.TerracottaColor;
/**
@@ -10,6 +11,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockTerracotta extends BlockSolidMeta {
+
public BlockTerracotta() {
this(0);
}
@@ -18,6 +20,10 @@ public BlockTerracotta(int meta) {
super(0);
}
+ public BlockTerracotta(DyeColor dyeColor) {
+ this(dyeColor.getWoolData());
+ }
+
public BlockTerracotta(TerracottaColor dyeColor) {
this(dyeColor.getTerracottaData());
}
@@ -49,7 +55,7 @@ public double getResistance() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
diff --git a/src/main/java/cn/nukkit/block/BlockTerracottaGlazed.java b/src/main/java/cn/nukkit/block/BlockTerracottaGlazed.java
index 5edfde12f6d..65e22f1f337 100644
--- a/src/main/java/cn/nukkit/block/BlockTerracottaGlazed.java
+++ b/src/main/java/cn/nukkit/block/BlockTerracottaGlazed.java
@@ -2,14 +2,16 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.utils.Faceable;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.DyeColor;
/**
* Created by CreeperFace on 2.6.2017.
*/
-public abstract class BlockTerracottaGlazed extends BlockSolidMeta implements Faceable {
+public abstract class BlockTerracottaGlazed extends BlockSolidMeta {
public BlockTerracottaGlazed() {
this(0);
@@ -41,8 +43,7 @@ public Item[] getDrops(Item item) {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- int[] faces = {2, 5, 3, 4};
- this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
return this.getLevel().setBlock(block, this, true, true);
}
@@ -52,7 +53,16 @@ public boolean canHarvestWithHand() {
}
@Override
- public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ public BlockColor getColor() {
+ return DyeColor.getByDyeData(getDyeColor().getDyeData()).getColor();
+ }
+
+ public DyeColor getDyeColor() {
+ return DyeColor.BLACK;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockTerracottaGlazedLime.java b/src/main/java/cn/nukkit/block/BlockTerracottaGlazedLime.java
index 3e7f4ec62b1..2c7f72b0bad 100644
--- a/src/main/java/cn/nukkit/block/BlockTerracottaGlazedLime.java
+++ b/src/main/java/cn/nukkit/block/BlockTerracottaGlazedLime.java
@@ -25,5 +25,7 @@ public String getName() {
return "Lime Glazed Terracotta";
}
- public DyeColor getDyeColor() { return DyeColor.LIME; }
+ public DyeColor getDyeColor() {
+ return DyeColor.LIME;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockTerracottaStained.java b/src/main/java/cn/nukkit/block/BlockTerracottaStained.java
index fa538493acb..96df1ce9ef8 100644
--- a/src/main/java/cn/nukkit/block/BlockTerracottaStained.java
+++ b/src/main/java/cn/nukkit/block/BlockTerracottaStained.java
@@ -51,7 +51,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{toItem()};
} else {
return new Item[0];
@@ -66,5 +66,4 @@ public BlockColor getColor() {
public DyeColor getDyeColor() {
return DyeColor.getByWoolData(getDamage());
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockThin.java b/src/main/java/cn/nukkit/block/BlockThin.java
index 9b309160dcf..2997f42dbc5 100644
--- a/src/main/java/cn/nukkit/block/BlockThin.java
+++ b/src/main/java/cn/nukkit/block/BlockThin.java
@@ -19,38 +19,47 @@ public boolean isSolid() {
}
protected AxisAlignedBB recalculateBoundingBox() {
- final double offNW = 7.0 / 16.0;
- final double offSE = 9.0 / 16.0;
- final double onNW = 0.0;
- final double onSE = 1.0;
- double w = offNW;
- double e = offSE;
- double n = offNW;
- double s = offSE;
+ double f = 0.4375;
+ double f1 = 0.5625;
+ double f2 = 0.4375;
+ double f3 = 0.5625;
try {
- boolean north = this.canConnect(this.north());
- boolean south = this.canConnect(this.south());
- boolean west = this.canConnect(this.west());
- boolean east = this.canConnect(this.east());
- w = west ? onNW : offNW;
- e = east ? onSE : offSE;
- n = north ? onNW : offNW;
- s = south ? onSE : offSE;
- } catch (LevelException ignore) {
- //null sucks
- }
+ boolean flag = this.canConnect(this.north());
+ boolean flag1 = this.canConnect(this.south());
+ boolean flag2 = this.canConnect(this.west());
+ boolean flag3 = this.canConnect(this.east());
+ if ((!flag2 || !flag3) && (flag2 || flag3 || flag || flag1)) {
+ if (flag2) {
+ f = 0;
+ } else if (flag3) {
+ f1 = 1;
+ }
+ } else {
+ f = 0;
+ f1 = 1;
+ }
+ if ((!flag || !flag1) && (flag2 || flag3 || flag || flag1)) {
+ if (flag) {
+ f2 = 0;
+ } else if (flag1) {
+ f3 = 1;
+ }
+ } else {
+ f2 = 0;
+ f3 = 1;
+ }
+ } catch (LevelException ignore) {}
return new SimpleAxisAlignedBB(
- this.x + w,
+ this.x + f,
this.y,
- this.z + n,
- this.x + e,
+ this.z + f2,
+ this.x + f1,
this.y + 1,
- this.z + s
+ this.z + f3
);
}
public boolean canConnect(Block block) {
return block.isSolid() || block.getId() == this.getId() || block.getId() == GLASS_PANE || block.getId() == GLASS;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockTilesDeepslate.java b/src/main/java/cn/nukkit/block/BlockTilesDeepslate.java
new file mode 100644
index 00000000000..8b71ccd91a3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTilesDeepslate.java
@@ -0,0 +1,52 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTilesDeepslate extends BlockSolid {
+
+ public BlockTilesDeepslate() {
+ // Does nothing
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_TILES;
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Tiles";
+ }
+
+ @Override
+ public double getHardness() {
+ return 3.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTilesDeepslateCracked.java b/src/main/java/cn/nukkit/block/BlockTilesDeepslateCracked.java
new file mode 100644
index 00000000000..504757997b9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTilesDeepslateCracked.java
@@ -0,0 +1,18 @@
+package cn.nukkit.block;
+
+public class BlockTilesDeepslateCracked extends BlockTilesDeepslate {
+
+ public BlockTilesDeepslateCracked() {
+ // Does nothing
+ }
+
+ @Override
+ public int getId() {
+ return CRACKED_DEEPSLATE_TILES;
+ }
+
+ @Override
+ public String getName() {
+ return "Cracked Deepslate Tiles";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTorch.java b/src/main/java/cn/nukkit/block/BlockTorch.java
index f9f1b1316d3..4886ee313b3 100644
--- a/src/main/java/cn/nukkit/block/BlockTorch.java
+++ b/src/main/java/cn/nukkit/block/BlockTorch.java
@@ -14,7 +14,7 @@
*/
public class BlockTorch extends BlockFlowable implements Faceable {
- private static final int[] faces = new int[]{
+ private static final short[] faces = {
0, //0, never used
5, //1
4, //2
@@ -23,7 +23,7 @@ public class BlockTorch extends BlockFlowable implements Faceable {
1, //5
};
- private static final int[] faces2 = new int[]{
+ private static final short[] faces2 = {
0, //0
4, //1
5, //2
@@ -64,7 +64,7 @@ public int onUpdate(int type) {
Block block = this.getSide(BlockFace.fromIndex(faces2[side]));
int id = block.getId();
- if ((block.isTransparent() && !(side == 0 && (below instanceof BlockFence || below.getId() == COBBLE_WALL))) && id != GLASS && id != STAINED_GLASS) {
+ if ((block.isTransparent() && !(side == 0 && (below instanceof BlockFence || below.getId() == COBBLE_WALL))) && id != GLASS && id != STAINED_GLASS && id != HARD_STAINED_GLASS) {
this.getLevel().useBreakOn(this);
return Level.BLOCK_UPDATE_NORMAL;
}
@@ -75,16 +75,20 @@ 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 (block instanceof BlockWater) {
+ return false;
+ }
+
int side = faces[face.getIndex()];
int bid = this.getSide(BlockFace.fromIndex(faces2[side])).getId();
- if ((!target.isTransparent() || bid == GLASS || bid == STAINED_GLASS) && face != BlockFace.DOWN) {
+ if ((!target.isTransparent() || bid == GLASS || bid == STAINED_GLASS || bid == HARD_STAINED_GLASS) && face != BlockFace.DOWN) {
this.setDamage(side);
this.getLevel().setBlock(block, this, true, true);
return true;
}
Block below = this.down();
- if (!below.isTransparent() || below instanceof BlockFence || below.getId() == COBBLE_WALL || below.getId() == GLASS || below.getId() == STAINED_GLASS) {
+ if (!below.isTransparent() || below instanceof BlockFence || below.getId() == COBBLE_WALL || below.getId() == GLASS || below.getId() == STAINED_GLASS || below.getId() == HARD_STAINED_GLASS) {
this.setDamage(0);
this.getLevel().setBlock(block, this, true, true);
return true;
@@ -94,7 +98,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
@@ -121,4 +125,9 @@ public BlockFace getBlockFace(int meta) {
return BlockFace.UP;
}
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockTransparent.java b/src/main/java/cn/nukkit/block/BlockTransparent.java
index 3ed36d2ca51..0499a649686 100644
--- a/src/main/java/cn/nukkit/block/BlockTransparent.java
+++ b/src/main/java/cn/nukkit/block/BlockTransparent.java
@@ -3,7 +3,7 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockTransparent extends Block {
@@ -17,5 +17,4 @@ public boolean isTransparent() {
public BlockColor getColor() {
return BlockColor.TRANSPARENT_BLOCK_COLOR;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockTransparentMeta.java b/src/main/java/cn/nukkit/block/BlockTransparentMeta.java
index 0ddf58d7a67..bc96d517fcb 100644
--- a/src/main/java/cn/nukkit/block/BlockTransparentMeta.java
+++ b/src/main/java/cn/nukkit/block/BlockTransparentMeta.java
@@ -3,7 +3,7 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockTransparentMeta extends BlockMeta {
@@ -25,5 +25,4 @@ public boolean isTransparent() {
public BlockColor getColor() {
return BlockColor.TRANSPARENT_BLOCK_COLOR;
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockTrapdoor.java b/src/main/java/cn/nukkit/block/BlockTrapdoor.java
index bc6884b0d9b..a96e7f944ba 100644
--- a/src/main/java/cn/nukkit/block/BlockTrapdoor.java
+++ b/src/main/java/cn/nukkit/block/BlockTrapdoor.java
@@ -7,10 +7,10 @@
import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.level.Level;
+import cn.nukkit.level.Sound;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
import cn.nukkit.math.SimpleAxisAlignedBB;
-import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.BlockColor;
import cn.nukkit.utils.Faceable;
@@ -22,6 +22,8 @@ public class BlockTrapdoor extends BlockTransparentMeta implements Faceable {
public static final int TRAPDOOR_OPEN_BIT = 0x08;
public static final int TRAPDOOR_TOP_BIT = 0x04;
+ private static final int[] faces = {2, 1, 3, 0};
+
public BlockTrapdoor() {
this(0);
}
@@ -167,11 +169,16 @@ public double getMaxZ() {
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_REDSTONE) {
- if ((!isOpen() && this.level.isBlockPowered(this.getLocation())) || (isOpen() && !this.level.isBlockPowered(this.getLocation()))) {
+ boolean powered = this.level.isBlockPowered(this);
+ if ((!isOpen() && powered) || (isOpen() && !powered)) {
this.level.getServer().getPluginManager().callEvent(new BlockRedstoneEvent(this, isOpen() ? 15 : 0, isOpen() ? 0 : 15));
this.setDamage(this.getDamage() ^ TRAPDOOR_OPEN_BIT);
this.level.setBlock(this, this, true);
- this.level.addLevelEvent(this.add(0.5, 0.5, 0.5), LevelEventPacket.EVENT_SOUND_DOOR);
+ if (this.isOpen()) {
+ this.level.addSound(this, Sound.RANDOM_DOOR_OPEN);
+ } else {
+ this.level.addSound(this, Sound.RANDOM_DOOR_CLOSE);
+ }
return type;
}
}
@@ -193,40 +200,41 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
top = face != BlockFace.UP;
}
- int[] faces = {2, 1, 3, 0};
- int faceBit = faces[facing.getHorizontalIndex()];
- meta |= faceBit;
+ meta |= faces[facing.getHorizontalIndex()];
if (top) {
meta |= TRAPDOOR_TOP_BIT;
}
+
this.setDamage(meta);
- this.getLevel().setBlock(block, this, true, true);
+
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public boolean onActivate(Item item, Player player) {
- if(toggle(player)) {
- this.level.addLevelEvent(this.add(0.5, 0.5, 0.5), LevelEventPacket.EVENT_SOUND_DOOR);
- return true;
- }
- return false;
+ return toggle(player);
}
public boolean toggle(Player player) {
DoorToggleEvent ev = new DoorToggleEvent(this, player);
- getLevel().getServer().getPluginManager().callEvent(ev);
- if(ev.isCancelled()) {
+ level.getServer().getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
return false;
}
this.setDamage(this.getDamage() ^ TRAPDOOR_OPEN_BIT);
- getLevel().setBlock(this, this, true);
+ level.setBlock(this, this, true, true);
+ if (this.isOpen()) {
+ this.level.addSound(this, Sound.RANDOM_DOOR_OPEN);
+ } else {
+ this.level.addSound(this, Sound.RANDOM_DOOR_CLOSE);
+ }
return true;
}
@@ -245,6 +253,11 @@ public boolean isTop() {
@Override
public BlockFace getBlockFace() {
- return BlockFace.fromHorizontalIndex(this.getDamage() & 0x07);
+ return BlockFace.fromHorizontalIndex(this.getDamage() & 0x7);
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return this.isOpen();
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockTrapdoorAcacia.java b/src/main/java/cn/nukkit/block/BlockTrapdoorAcacia.java
new file mode 100644
index 00000000000..ddaa2123418
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTrapdoorAcacia.java
@@ -0,0 +1,41 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTrapdoorAcacia extends BlockTrapdoor {
+
+ public BlockTrapdoorAcacia() {
+ this(0);
+ }
+
+ public BlockTrapdoorAcacia(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Acacia Trapdoor";
+ }
+
+ @Override
+ public int getId() {
+ return ACACIA_TRAPDOOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTrapdoorBirch.java b/src/main/java/cn/nukkit/block/BlockTrapdoorBirch.java
new file mode 100644
index 00000000000..0aced89bb68
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTrapdoorBirch.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTrapdoorBirch extends BlockTrapdoor {
+
+ public BlockTrapdoorBirch() {
+ this(0);
+ }
+
+ public BlockTrapdoorBirch(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Birch Trapdoor";
+ }
+
+ @Override
+ public int getId() {
+ return BIRCH_TRAPDOOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SAND_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTrapdoorDarkOak.java b/src/main/java/cn/nukkit/block/BlockTrapdoorDarkOak.java
new file mode 100644
index 00000000000..b0fe6ef8e10
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTrapdoorDarkOak.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTrapdoorDarkOak extends BlockTrapdoor {
+
+ public BlockTrapdoorDarkOak() {
+ this(0);
+ }
+
+ public BlockTrapdoorDarkOak(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Dark Oak Trapdoor";
+ }
+
+ @Override
+ public int getId() {
+ return DARK_OAK_TRAPDOOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BROWN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTrapdoorJungle.java b/src/main/java/cn/nukkit/block/BlockTrapdoorJungle.java
new file mode 100644
index 00000000000..93c0c5464f6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTrapdoorJungle.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTrapdoorJungle extends BlockTrapdoor {
+
+ public BlockTrapdoorJungle() {
+ this(0);
+ }
+
+ public BlockTrapdoorJungle(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Jungle Trapdoor";
+ }
+
+ @Override
+ public int getId() {
+ return JUNGLE_TRAPDOOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DIRT_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTrapdoorSpruce.java b/src/main/java/cn/nukkit/block/BlockTrapdoorSpruce.java
new file mode 100644
index 00000000000..81ee2e6dc9d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTrapdoorSpruce.java
@@ -0,0 +1,36 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTrapdoorSpruce extends BlockTrapdoor {
+
+ public BlockTrapdoorSpruce() {
+ this(0);
+ }
+
+ public BlockTrapdoorSpruce(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Spruce Trapdoor";
+ }
+
+ @Override
+ public int getId() {
+ return SPRUCE_TRAPDOOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SPRUCE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTrappedChest.java b/src/main/java/cn/nukkit/block/BlockTrappedChest.java
index 6b4916c3699..aba41a49f0f 100644
--- a/src/main/java/cn/nukkit/block/BlockTrappedChest.java
+++ b/src/main/java/cn/nukkit/block/BlockTrappedChest.java
@@ -34,10 +34,8 @@ public String getName() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- int[] faces = {2, 5, 3, 4};
-
BlockEntityChest chest = null;
- this.setDamage(faces[player != null ? player.getDirection().getHorizontalIndex() : 0]);
+ this.setDamage(Block.faces2534[player != null ? player.getDirection().getHorizontalIndex() : 0]);
for (BlockFace side : Plane.HORIZONTAL) {
if ((this.getDamage() == 4 || this.getDamage() == 5) && (side == BlockFace.WEST || side == BlockFace.EAST)) {
@@ -55,7 +53,8 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
+
CompoundTag nbt = new CompoundTag("")
.putList(new ListTag<>("Items"))
.putString("id", BlockEntity.CHEST)
@@ -74,11 +73,7 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
}
}
- BlockEntityChest blockEntity = (BlockEntityChest) BlockEntity.createBlockEntity(BlockEntity.CHEST, this.getLevel().getChunk((int) (this.x) >> 4, (int) (this.z) >> 4), nbt);
-
- if (blockEntity == null) {
- return false;
- }
+ BlockEntityChest blockEntity = (BlockEntityChest) BlockEntity.createBlockEntity(BlockEntity.CHEST, this.getChunk(), nbt);
if (chest != null) {
chest.pairWith(blockEntity);
diff --git a/src/main/java/cn/nukkit/block/BlockTripWire.java b/src/main/java/cn/nukkit/block/BlockTripWire.java
index 8d63ebf0564..e0a28186cd3 100644
--- a/src/main/java/cn/nukkit/block/BlockTripWire.java
+++ b/src/main/java/cn/nukkit/block/BlockTripWire.java
@@ -3,7 +3,6 @@
import cn.nukkit.Player;
import cn.nukkit.entity.Entity;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemString;
import cn.nukkit.level.Level;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
@@ -31,21 +30,6 @@ public String getName() {
return "Tripwire";
}
- @Override
- public boolean canPassThrough() {
- return true;
- }
-
- @Override
- public double getResistance() {
- return 0;
- }
-
- @Override
- public double getHardness() {
- return 0;
- }
-
@Override
public AxisAlignedBB getBoundingBox() {
return null;
@@ -53,7 +37,7 @@ public AxisAlignedBB getBoundingBox() {
@Override
public Item toItem() {
- return new ItemString();
+ return Item.get(Item.STRING);
}
public boolean isPowered() {
@@ -115,7 +99,7 @@ public void updateHook(boolean scheduleUpdate) {
hook.calculateState(false, true, i, this);
}
- /*if(scheduleUpdate) {
+ /*if (scheduleUpdate) {
this.level.scheduleUpdate(hook, 10);
}*/
break;
@@ -136,7 +120,8 @@ public int onUpdate(int type) {
}
boolean found = false;
- for (Entity entity : this.level.getCollidingEntities(this.getCollisionBoundingBox())) {
+ Entity[] e = this.level.getCollidingEntities(this.getCollisionBoundingBox());
+ for (Entity entity : e) {
if (!entity.doesTriggerPressurePlate()) {
continue;
}
@@ -190,4 +175,19 @@ public double getMaxY() {
protected AxisAlignedBB recalculateCollisionBoundingBox() {
return this;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockTripWireHook.java b/src/main/java/cn/nukkit/block/BlockTripWireHook.java
index c286e1be24c..42c8bb4dd79 100644
--- a/src/main/java/cn/nukkit/block/BlockTripWireHook.java
+++ b/src/main/java/cn/nukkit/block/BlockTripWireHook.java
@@ -82,7 +82,7 @@ public boolean onBreak(Item item) {
if (powered) {
this.level.updateAroundRedstone(this, null);
- this.level.updateAroundRedstone(this.getLocation().getSide(getFacing().getOpposite()), null);
+ this.level.updateAroundRedstone(this.getSideVec(getFacing().getOpposite()), null);
}
return true;
@@ -90,7 +90,6 @@ public boolean onBreak(Item item) {
public void calculateState(boolean onBreak, boolean updateAround, int pos, Block block) {
BlockFace facing = getFacing();
- Vector3 v = this.getLocation();
boolean attached = isAttached();
boolean powered = isPowered();
boolean canConnect = !onBreak;
@@ -99,8 +98,7 @@ public void calculateState(boolean onBreak, boolean updateAround, int pos, Block
Block[] blocks = new Block[42];
for (int i = 1; i < 42; ++i) {
- Vector3 vector = v.getSide(facing, i);
- Block b = this.level.getBlock(vector);
+ Block b = this.getSide(facing, i);
if (b instanceof BlockTripWireHook) {
if (((BlockTripWireHook) b).getFacing() == facing.getOpposite()) {
@@ -133,36 +131,36 @@ public void calculateState(boolean onBreak, boolean updateAround, int pos, Block
canConnect = canConnect & distance > 1;
nextPowered = nextPowered & canConnect;
- BlockTripWireHook hook = (BlockTripWireHook) Block.get(BlockID.TRIPWIRE_HOOK);
+ BlockTripWireHook hook = (BlockTripWireHook) Block.get(TRIPWIRE_HOOK);
hook.setAttached(canConnect);
hook.setPowered(nextPowered);
if (distance > 0) {
- Vector3 vec = v.getSide(facing, distance);
+ Vector3 vec = this.getSideVec(facing, distance);
BlockFace face = facing.getOpposite();
hook.setFace(face);
this.level.setBlock(vec, hook, true, false);
this.level.updateAroundRedstone(vec, null);
- this.level.updateAroundRedstone(vec.getSide(face.getOpposite()), null);
+ this.level.updateAroundRedstone(vec.getSideVec(face.getOpposite()), null);
this.addSound(vec, canConnect, nextPowered, attached, powered);
}
- this.addSound(v, canConnect, nextPowered, attached, powered);
+ this.addSound(this, canConnect, nextPowered, attached, powered);
if (!onBreak) {
hook.setFace(facing);
- this.level.setBlock(v, hook, true, false);
+ this.level.setBlock(this, hook, true, false);
if (updateAround) {
- this.level.updateAroundRedstone(v, null);
- this.level.updateAroundRedstone(v.getSide(facing.getOpposite()), null);
+ this.level.updateAroundRedstone(this, null);
+ this.level.updateAroundRedstone(this.getSideVec(facing.getOpposite()), null);
}
}
if (attached != canConnect) {
for (int i = 1; i < distance; i++) {
- Vector3 vc = v.getSide(facing, i);
+ Vector3 vc = this.getSideVec(facing, i);
block = blocks[i];
if (block != null && this.level.getBlockIdAt(vc.getFloorX(), vc.getFloorY(), vc.getFloorZ()) != Block.AIR) {
@@ -232,6 +230,21 @@ public int getStrongPower(BlockFace side) {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockTuff.java b/src/main/java/cn/nukkit/block/BlockTuff.java
new file mode 100644
index 00000000000..c2491ae0925
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTuff.java
@@ -0,0 +1,51 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockTuff extends BlockSolid {
+
+ public BlockTuff() {
+ // Does Nothing
+ }
+
+ @Override
+ public String getName() {
+ return "Tuff";
+ }
+
+ @Override
+ public int getId() {
+ return TUFF;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 6;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_PICKAXE;
+ }
+
+ @Override
+ public int getToolTier() {
+ return ItemTool.TIER_WOODEN;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.GRAY_TERRACOTA_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTurtleEgg.java b/src/main/java/cn/nukkit/block/BlockTurtleEgg.java
new file mode 100644
index 00000000000..5394b752141
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTurtleEgg.java
@@ -0,0 +1,111 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.BlockFace;
+
+public class BlockTurtleEgg extends BlockTransparentMeta {
+
+ public BlockTurtleEgg() {
+ this(0);
+ }
+
+ public BlockTurtleEgg(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Turtle Egg";
+ }
+
+ @Override
+ public int getId() {
+ return TURTLE_EGG;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.5;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.5;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public double getMinX() {
+ return this.x + 0.2;
+ }
+
+ @Override
+ public double getMinZ() {
+ return this.z + 0.2;
+ }
+
+ @Override
+ public double getMaxX() {
+ return this.x + 0.8;
+ }
+
+ @Override
+ public double getMaxY() {
+ return this.y + 0.45;
+ }
+
+ @Override
+ public double getMaxZ() {
+ return this.z + 0.8;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (!canStayOnFullSolid(this.down())) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (target instanceof BlockTurtleEgg && this.getDamage() < 3) {
+ this.setDamage(this.getDamage() + 1);
+ return this.getLevel().setBlock(target, this, true, true);
+ }
+ if (!canStayOnFullSolid(this.down())) {
+ return false;
+ }
+ this.getLevel().setBlock(this, this, true, true);
+ return true;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockTypes.java b/src/main/java/cn/nukkit/block/BlockTypes.java
new file mode 100644
index 00000000000..e8a637860db
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTypes.java
@@ -0,0 +1,677 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.material.BlockType;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import lombok.Data;
+
+public class BlockTypes {
+ private static final Int2ObjectMap types = new Int2ObjectOpenHashMap<>();
+ private static final Object2ObjectMap identifiers = new Object2ObjectOpenHashMap<>();
+
+ public static final BlockType AIR = register("minecraft:air", BlockID.AIR);
+ public static final BlockType STONE = register("minecraft:stone", BlockID.STONE);
+ public static final BlockType GRASS = register("minecraft:grass", BlockID.GRASS);
+ public static final BlockType DIRT = register("minecraft:dirt", BlockID.DIRT);
+ public static final BlockType COBBLESTONE = register("minecraft:cobblestone", BlockID.COBBLESTONE);
+ public static final BlockType COBBLE = register("minecraft:cobblestone", BlockID.COBBLE);
+ public static final BlockType PLANK = register("minecraft:planks", BlockID.PLANK);
+ public static final BlockType PLANKS = register("minecraft:planks", BlockID.PLANKS);
+ public static final BlockType WOODEN_PLANK = register("minecraft:planks", BlockID.WOODEN_PLANK);
+ public static final BlockType WOODEN_PLANKS = register("minecraft:planks", BlockID.WOODEN_PLANKS);
+ public static final BlockType SAPLING = register("minecraft:sapling", BlockID.SAPLING);
+ public static final BlockType SAPLINGS = register("minecraft:sapling", BlockID.SAPLINGS);
+ public static final BlockType BEDROCK = register("minecraft:bedrock", BlockID.BEDROCK);
+ public static final BlockType WATER = register("minecraft:flowing_water", BlockID.WATER);
+ public static final BlockType STILL_WATER = register("minecraft:water", BlockID.STILL_WATER);
+ public static final BlockType LAVA = register("minecraft:flowing_lava", BlockID.LAVA);
+ public static final BlockType STILL_LAVA = register("minecraft:lava", BlockID.STILL_LAVA);
+ public static final BlockType SAND = register("minecraft:sand", BlockID.SAND);
+ public static final BlockType GRAVEL = register("minecraft:gravel", BlockID.GRAVEL);
+ public static final BlockType GOLD_ORE = register("minecraft:gold_ore", BlockID.GOLD_ORE);
+ public static final BlockType IRON_ORE = register("minecraft:iron_ore", BlockID.IRON_ORE);
+ public static final BlockType COAL_ORE = register("minecraft:coal_ore", BlockID.COAL_ORE);
+ public static final BlockType LOG = register("minecraft:log", BlockID.LOG);
+ public static final BlockType WOOD = register("minecraft:log", BlockID.WOOD);
+ public static final BlockType TRUNK = register("minecraft:log", BlockID.TRUNK);
+ public static final BlockType LEAVES = register("minecraft:leaves", BlockID.LEAVES);
+ public static final BlockType LEAVE = register("minecraft:leaves", BlockID.LEAVE);
+ public static final BlockType SPONGE = register("minecraft:sponge", BlockID.SPONGE);
+ public static final BlockType GLASS = register("minecraft:glass", BlockID.GLASS);
+ public static final BlockType LAPIS_ORE = register("minecraft:lapis_ore", BlockID.LAPIS_ORE);
+ public static final BlockType LAPIS_BLOCK = register("minecraft:lapis_block", BlockID.LAPIS_BLOCK);
+ public static final BlockType DISPENSER = register("minecraft:dispenser", BlockID.DISPENSER);
+ public static final BlockType SANDSTONE = register("minecraft:sandstone", BlockID.SANDSTONE);
+ public static final BlockType NOTEBLOCK = register("minecraft:noteblock", BlockID.NOTEBLOCK);
+ public static final BlockType BED_BLOCK = register("minecraft:item.bed", BlockID.BED_BLOCK);
+ public static final BlockType POWERED_RAIL = register("minecraft:golden_rail", BlockID.POWERED_RAIL);
+ public static final BlockType DETECTOR_RAIL = register("minecraft:detector_rail", BlockID.DETECTOR_RAIL);
+ public static final BlockType STICKY_PISTON = register("minecraft:sticky_piston", BlockID.STICKY_PISTON);
+ public static final BlockType COBWEB = register("minecraft:web", BlockID.COBWEB);
+ public static final BlockType WEB = register("minecraft:web", BlockID.WEB);
+ public static final BlockType TALL_GRASS = register("minecraft:tallgrass", BlockID.TALL_GRASS);
+ public static final BlockType BUSH = register("minecraft:deadbush", BlockID.BUSH);
+ public static final BlockType DEAD_BUSH = register("minecraft:deadbush", BlockID.DEAD_BUSH);
+ public static final BlockType DEADBUSH = register("minecraft:deadbush", BlockID.DEADBUSH);
+ public static final BlockType PISTON = register("minecraft:piston", BlockID.PISTON);
+ public static final BlockType PISTON_HEAD = register("minecraft:piston_arm_collision", BlockID.PISTON_HEAD);
+ public static final BlockType WOOL = register("minecraft:wool", BlockID.WOOL);
+ public static final BlockType WHITE_WOOL = register("minecraft:wool", BlockID.WHITE_WOOL);
+ public static final BlockType DANDELION = register("minecraft:yellow_flower", BlockID.DANDELION);
+ public static final BlockType POPPY = register("minecraft:red_flower", BlockID.POPPY);
+ public static final BlockType ROSE = register("minecraft:red_flower", BlockID.ROSE);
+ public static final BlockType FLOWER = register("minecraft:red_flower", BlockID.FLOWER);
+ public static final BlockType RED_FLOWER = register("minecraft:red_flower", BlockID.RED_FLOWER);
+ public static final BlockType BROWN_MUSHROOM = register("minecraft:brown_mushroom", BlockID.BROWN_MUSHROOM);
+ public static final BlockType RED_MUSHROOM = register("minecraft:red_mushroom", BlockID.RED_MUSHROOM);
+ public static final BlockType GOLD_BLOCK = register("minecraft:gold_block", BlockID.GOLD_BLOCK);
+ public static final BlockType IRON_BLOCK = register("minecraft:iron_block", BlockID.IRON_BLOCK);
+ public static final BlockType DOUBLE_SLAB = register("minecraft:double_stone_block_slab", BlockID.DOUBLE_SLAB);
+ public static final BlockType DOUBLE_STONE_SLAB = register("minecraft:double_stone_block_slab", BlockID.DOUBLE_STONE_SLAB);
+ public static final BlockType DOUBLE_SLABS = register("minecraft:double_stone_block_slab", BlockID.DOUBLE_SLABS);
+ public static final BlockType SLAB = register("minecraft:stone_block_slab", BlockID.SLAB);
+ public static final BlockType STONE_SLAB = register("minecraft:stone_block_slab", BlockID.STONE_SLAB);
+ public static final BlockType SLABS = register("minecraft:stone_block_slab", BlockID.SLABS);
+ public static final BlockType BRICKS = register("minecraft:brick_block", BlockID.BRICKS);
+ public static final BlockType BRICKS_BLOCK = register("minecraft:brick_block", BlockID.BRICKS_BLOCK);
+ public static final BlockType TNT = register("minecraft:tnt", BlockID.TNT);
+ public static final BlockType BOOKSHELF = register("minecraft:bookshelf", BlockID.BOOKSHELF);
+ public static final BlockType MOSS_STONE = register("minecraft:mossy_cobblestone", BlockID.MOSS_STONE);
+ public static final BlockType MOSSY_STONE = register("minecraft:mossy_cobblestone", BlockID.MOSSY_STONE);
+ public static final BlockType OBSIDIAN = register("minecraft:obsidian", BlockID.OBSIDIAN);
+ public static final BlockType TORCH = register("minecraft:torch", BlockID.TORCH);
+ public static final BlockType FIRE = register("minecraft:fire", BlockID.FIRE);
+ public static final BlockType MONSTER_SPAWNER = register("minecraft:mob_spawner", BlockID.MONSTER_SPAWNER);
+ public static final BlockType WOOD_STAIRS = register("minecraft:oak_stairs", BlockID.WOOD_STAIRS);
+ public static final BlockType WOODEN_STAIRS = register("minecraft:oak_stairs", BlockID.WOODEN_STAIRS);
+ public static final BlockType OAK_WOOD_STAIRS = register("minecraft:oak_stairs", BlockID.OAK_WOOD_STAIRS);
+ public static final BlockType OAK_WOODEN_STAIRS = register("minecraft:oak_stairs", BlockID.OAK_WOODEN_STAIRS);
+ public static final BlockType CHEST = register("minecraft:chest", BlockID.CHEST);
+ public static final BlockType REDSTONE_WIRE = register("minecraft:redstone_wire", BlockID.REDSTONE_WIRE);
+ public static final BlockType DIAMOND_ORE = register("minecraft:diamond_ore", BlockID.DIAMOND_ORE);
+ public static final BlockType DIAMOND_BLOCK = register("minecraft:diamond_block", BlockID.DIAMOND_BLOCK);
+ public static final BlockType CRAFTING_TABLE = register("minecraft:crafting_table", BlockID.CRAFTING_TABLE);
+ public static final BlockType WORKBENCH = register("minecraft:crafting_table", BlockID.WORKBENCH);
+ public static final BlockType WHEAT_BLOCK = register("minecraft:item.wheat", BlockID.WHEAT_BLOCK);
+ public static final BlockType FARMLAND = register("minecraft:farmland", BlockID.FARMLAND);
+ public static final BlockType FURNACE = register("minecraft:furnace", BlockID.FURNACE);
+ public static final BlockType BURNING_FURNACE = register("minecraft:lit_furnace", BlockID.BURNING_FURNACE);
+ public static final BlockType LIT_FURNACE = register("minecraft:lit_furnace", BlockID.LIT_FURNACE);
+ public static final BlockType SIGN_POST = register("minecraft:standing_sign", BlockID.SIGN_POST);
+ public static final BlockType DOOR_BLOCK = register("minecraft:item.wooden_door", BlockID.DOOR_BLOCK);
+ public static final BlockType WOODEN_DOOR_BLOCK = register("minecraft:item.wooden_door", BlockID.WOODEN_DOOR_BLOCK);
+ public static final BlockType WOOD_DOOR_BLOCK = register("minecraft:item.wooden_door", BlockID.WOOD_DOOR_BLOCK);
+ public static final BlockType LADDER = register("minecraft:ladder", BlockID.LADDER);
+ public static final BlockType RAIL = register("minecraft:rail", BlockID.RAIL);
+ public static final BlockType COBBLE_STAIRS = register("minecraft:stone_stairs", BlockID.COBBLE_STAIRS);
+ public static final BlockType COBBLESTONE_STAIRS = register("minecraft:stone_stairs", BlockID.COBBLESTONE_STAIRS);
+ public static final BlockType WALL_SIGN = register("minecraft:wall_sign", BlockID.WALL_SIGN);
+ public static final BlockType LEVER = register("minecraft:lever", BlockID.LEVER);
+ public static final BlockType STONE_PRESSURE_PLATE = register("minecraft:stone_pressure_plate", BlockID.STONE_PRESSURE_PLATE);
+ public static final BlockType IRON_DOOR_BLOCK = register("minecraft:item.iron_door", BlockID.IRON_DOOR_BLOCK);
+ public static final BlockType WOODEN_PRESSURE_PLATE = register("minecraft:wooden_pressure_plate", BlockID.WOODEN_PRESSURE_PLATE);
+ public static final BlockType REDSTONE_ORE = register("minecraft:redstone_ore", BlockID.REDSTONE_ORE);
+ public static final BlockType GLOWING_REDSTONE_ORE = register("minecraft:lit_redstone_ore", BlockID.GLOWING_REDSTONE_ORE);
+ public static final BlockType LIT_REDSTONE_ORE = register("minecraft:lit_redstone_ore", BlockID.LIT_REDSTONE_ORE);
+ public static final BlockType UNLIT_REDSTONE_TORCH = register("minecraft:unlit_redstone_torch", BlockID.UNLIT_REDSTONE_TORCH);
+ public static final BlockType REDSTONE_TORCH = register("minecraft:redstone_torch", BlockID.REDSTONE_TORCH);
+ public static final BlockType STONE_BUTTON = register("minecraft:stone_button", BlockID.STONE_BUTTON);
+ public static final BlockType SNOW = register("minecraft:snow_layer", BlockID.SNOW);
+ public static final BlockType SNOW_LAYER = register("minecraft:snow_layer", BlockID.SNOW_LAYER);
+ public static final BlockType ICE = register("minecraft:ice", BlockID.ICE);
+ public static final BlockType SNOW_BLOCK = register("minecraft:snow", BlockID.SNOW_BLOCK);
+ public static final BlockType CACTUS = register("minecraft:cactus", BlockID.CACTUS);
+ public static final BlockType CLAY_BLOCK = register("minecraft:clay", BlockID.CLAY_BLOCK);
+ public static final BlockType REEDS = register("minecraft:item.reeds", BlockID.REEDS);
+ public static final BlockType SUGARCANE_BLOCK = register("minecraft:item.reeds", BlockID.SUGARCANE_BLOCK);
+ public static final BlockType JUKEBOX = register("minecraft:jukebox", BlockID.JUKEBOX);
+ public static final BlockType FENCE = register("minecraft:fence", BlockID.FENCE);
+ public static final BlockType PUMPKIN = register("minecraft:pumpkin", BlockID.PUMPKIN);
+ public static final BlockType NETHERRACK = register("minecraft:netherrack", BlockID.NETHERRACK);
+ public static final BlockType SOUL_SAND = register("minecraft:soul_sand", BlockID.SOUL_SAND);
+ public static final BlockType GLOWSTONE = register("minecraft:glowstone", BlockID.GLOWSTONE);
+ public static final BlockType GLOWSTONE_BLOCK = register("minecraft:glowstone", BlockID.GLOWSTONE_BLOCK);
+ public static final BlockType NETHER_PORTAL = register("minecraft:portal", BlockID.NETHER_PORTAL);
+ public static final BlockType LIT_PUMPKIN = register("minecraft:lit_pumpkin", BlockID.LIT_PUMPKIN);
+ public static final BlockType JACK_O_LANTERN = register("minecraft:lit_pumpkin", BlockID.JACK_O_LANTERN);
+ public static final BlockType CAKE_BLOCK = register("minecraft:item.cake", BlockID.CAKE_BLOCK);
+ public static final BlockType UNPOWERED_REPEATER = register("minecraft:unpowered_repeater", BlockID.UNPOWERED_REPEATER);
+ public static final BlockType POWERED_REPEATER = register("minecraft:powered_repeater", BlockID.POWERED_REPEATER);
+ public static final BlockType INVISIBLE_BEDROCK = register("minecraft:invisible_bedrock", BlockID.INVISIBLE_BEDROCK);
+ public static final BlockType TRAPDOOR = register("minecraft:trapdoor", BlockID.TRAPDOOR);
+ public static final BlockType MONSTER_EGG = register("minecraft:monster_egg", BlockID.MONSTER_EGG);
+ public static final BlockType STONE_BRICKS = register("minecraft:stonebrick", BlockID.STONE_BRICKS);
+ public static final BlockType STONE_BRICK = register("minecraft:stonebrick", BlockID.STONE_BRICK);
+ public static final BlockType BROWN_MUSHROOM_BLOCK = register("minecraft:brown_mushroom_block", BlockID.BROWN_MUSHROOM_BLOCK);
+ public static final BlockType RED_MUSHROOM_BLOCK = register("minecraft:red_mushroom_block", BlockID.RED_MUSHROOM_BLOCK);
+ public static final BlockType IRON_BAR = register("minecraft:iron_bars", BlockID.IRON_BAR);
+ public static final BlockType IRON_BARS = register("minecraft:iron_bars", BlockID.IRON_BARS);
+ public static final BlockType GLASS_PANE = register("minecraft:glass_pane", BlockID.GLASS_PANE);
+ public static final BlockType GLASS_PANEL = register("minecraft:glass_pane", BlockID.GLASS_PANEL);
+ public static final BlockType MELON_BLOCK = register("minecraft:melon_block", BlockID.MELON_BLOCK);
+ public static final BlockType PUMPKIN_STEM = register("minecraft:pumpkin_stem", BlockID.PUMPKIN_STEM);
+ public static final BlockType MELON_STEM = register("minecraft:melon_stem", BlockID.MELON_STEM);
+ public static final BlockType VINE = register("minecraft:vine", BlockID.VINE);
+ public static final BlockType VINES = register("minecraft:vine", BlockID.VINES);
+ public static final BlockType FENCE_GATE = register("minecraft:fence_gate", BlockID.FENCE_GATE);
+ public static final BlockType FENCE_GATE_OAK = register("minecraft:fence_gate", BlockID.FENCE_GATE_OAK);
+ public static final BlockType BRICK_STAIRS = register("minecraft:brick_stairs", BlockID.BRICK_STAIRS);
+ public static final BlockType STONE_BRICK_STAIRS = register("minecraft:stone_brick_stairs", BlockID.STONE_BRICK_STAIRS);
+ public static final BlockType MYCELIUM = register("minecraft:mycelium", BlockID.MYCELIUM);
+ public static final BlockType WATER_LILY = register("minecraft:waterlily", BlockID.WATER_LILY);
+ public static final BlockType WATERLILY = register("minecraft:waterlily", BlockID.WATERLILY);
+ public static final BlockType LILY_PAD = register("minecraft:waterlily", BlockID.LILY_PAD);
+ public static final BlockType NETHER_BRICKS = register("minecraft:nether_brick", BlockID.NETHER_BRICKS);
+ public static final BlockType NETHER_BRICK_BLOCK = register("minecraft:nether_brick", BlockID.NETHER_BRICK_BLOCK);
+ public static final BlockType NETHER_BRICK_FENCE = register("minecraft:nether_brick_fence", BlockID.NETHER_BRICK_FENCE);
+ public static final BlockType NETHER_BRICKS_STAIRS = register("minecraft:nether_brick_stairs", BlockID.NETHER_BRICKS_STAIRS);
+ public static final BlockType NETHER_WART_BLOCK = register("minecraft:item.nether_wart", BlockID.NETHER_WART_BLOCK);
+ public static final BlockType ENCHANTING_TABLE = register("minecraft:enchanting_table", BlockID.ENCHANTING_TABLE);
+ public static final BlockType ENCHANT_TABLE = register("minecraft:enchanting_table", BlockID.ENCHANT_TABLE);
+ public static final BlockType ENCHANTMENT_TABLE = register("minecraft:enchanting_table", BlockID.ENCHANTMENT_TABLE);
+ public static final BlockType BREWING_STAND_BLOCK = register("minecraft:brewingstandblock", BlockID.BREWING_STAND_BLOCK);
+ public static final BlockType BREWING_BLOCK = register("minecraft:brewingstandblock", BlockID.BREWING_BLOCK);
+ public static final BlockType CAULDRON_BLOCK = register("minecraft:item.cauldron", BlockID.CAULDRON_BLOCK);
+ public static final BlockType END_PORTAL = register("minecraft:end_portal", BlockID.END_PORTAL);
+ public static final BlockType END_PORTAL_FRAME = register("minecraft:end_portal_frame", BlockID.END_PORTAL_FRAME);
+ public static final BlockType END_STONE = register("minecraft:end_stone", BlockID.END_STONE);
+ public static final BlockType DRAGON_EGG = register("minecraft:dragon_egg", BlockID.DRAGON_EGG);
+ public static final BlockType REDSTONE_LAMP = register("minecraft:redstone_lamp", BlockID.REDSTONE_LAMP);
+ public static final BlockType LIT_REDSTONE_LAMP = register("minecraft:lit_redstone_lamp", BlockID.LIT_REDSTONE_LAMP);
+ public static final BlockType DROPPER = register("minecraft:dropper", BlockID.DROPPER);
+ public static final BlockType ACTIVATOR_RAIL = register("minecraft:activator_rail", BlockID.ACTIVATOR_RAIL);
+ public static final BlockType COCOA = register("minecraft:cocoa", BlockID.COCOA);
+ public static final BlockType COCOA_BLOCK = register("minecraft:cocoa", BlockID.COCOA_BLOCK);
+ public static final BlockType SANDSTONE_STAIRS = register("minecraft:sandstone_stairs", BlockID.SANDSTONE_STAIRS);
+ public static final BlockType EMERALD_ORE = register("minecraft:emerald_ore", BlockID.EMERALD_ORE);
+ public static final BlockType ENDER_CHEST = register("minecraft:ender_chest", BlockID.ENDER_CHEST);
+ public static final BlockType TRIPWIRE_HOOK = register("minecraft:tripwire_hook", BlockID.TRIPWIRE_HOOK);
+ public static final BlockType TRIPWIRE = register("minecraft:trip_wire", BlockID.TRIPWIRE);
+ public static final BlockType EMERALD_BLOCK = register("minecraft:emerald_block", BlockID.EMERALD_BLOCK);
+ public static final BlockType SPRUCE_WOOD_STAIRS = register("minecraft:spruce_stairs", BlockID.SPRUCE_WOOD_STAIRS);
+ public static final BlockType SPRUCE_WOODEN_STAIRS = register("minecraft:spruce_stairs", BlockID.SPRUCE_WOODEN_STAIRS);
+ public static final BlockType BIRCH_WOOD_STAIRS = register("minecraft:birch_stairs", BlockID.BIRCH_WOOD_STAIRS);
+ public static final BlockType BIRCH_WOODEN_STAIRS = register("minecraft:birch_stairs", BlockID.BIRCH_WOODEN_STAIRS);
+ public static final BlockType JUNGLE_WOOD_STAIRS = register("minecraft:jungle_stairs", BlockID.JUNGLE_WOOD_STAIRS);
+ public static final BlockType JUNGLE_WOODEN_STAIRS = register("minecraft:jungle_stairs", BlockID.JUNGLE_WOODEN_STAIRS);
+ public static final BlockType COMMAND_BLOCK = register("minecraft:command_block", BlockID.COMMAND_BLOCK);
+ public static final BlockType BEACON = register("minecraft:beacon", BlockID.BEACON);
+ public static final BlockType COBBLE_WALL = register("minecraft:cobblestone_wall", BlockID.COBBLE_WALL);
+ public static final BlockType STONE_WALL = register("minecraft:cobblestone_wall", BlockID.STONE_WALL);
+ public static final BlockType COBBLESTONE_WALL = register("minecraft:cobblestone_wall", BlockID.COBBLESTONE_WALL);
+ public static final BlockType FLOWER_POT_BLOCK = register("minecraft:item.flower_pot", BlockID.FLOWER_POT_BLOCK);
+ public static final BlockType CARROT_BLOCK = register("minecraft:carrots", BlockID.CARROT_BLOCK);
+ public static final BlockType POTATO_BLOCK = register("minecraft:potatoes", BlockID.POTATO_BLOCK);
+ public static final BlockType WOODEN_BUTTON = register("minecraft:wooden_button", BlockID.WOODEN_BUTTON);
+ public static final BlockType SKULL_BLOCK = register("minecraft:item.skull", BlockID.SKULL_BLOCK);
+ public static final BlockType ANVIL = register("minecraft:anvil", BlockID.ANVIL);
+ public static final BlockType TRAPPED_CHEST = register("minecraft:trapped_chest", BlockID.TRAPPED_CHEST);
+ public static final BlockType LIGHT_WEIGHTED_PRESSURE_PLATE = register("minecraft:light_weighted_pressure_plate", BlockID.LIGHT_WEIGHTED_PRESSURE_PLATE);
+ public static final BlockType HEAVY_WEIGHTED_PRESSURE_PLATE = register("minecraft:heavy_weighted_pressure_plate", BlockID.HEAVY_WEIGHTED_PRESSURE_PLATE);
+ public static final BlockType UNPOWERED_COMPARATOR = register("minecraft:unpowered_comparator", BlockID.UNPOWERED_COMPARATOR);
+ public static final BlockType POWERED_COMPARATOR = register("minecraft:powered_comparator", BlockID.POWERED_COMPARATOR);
+ public static final BlockType DAYLIGHT_DETECTOR = register("minecraft:daylight_detector", BlockID.DAYLIGHT_DETECTOR);
+ public static final BlockType REDSTONE_BLOCK = register("minecraft:redstone_block", BlockID.REDSTONE_BLOCK);
+ public static final BlockType QUARTZ_ORE = register("minecraft:quartz_ore", BlockID.QUARTZ_ORE);
+ public static final BlockType HOPPER_BLOCK = register("minecraft:item.hopper", BlockID.HOPPER_BLOCK);
+ public static final BlockType QUARTZ_BLOCK = register("minecraft:quartz_block", BlockID.QUARTZ_BLOCK);
+ public static final BlockType QUARTZ_STAIRS = register("minecraft:quartz_stairs", BlockID.QUARTZ_STAIRS);
+ public static final BlockType DOUBLE_WOOD_SLAB = register("minecraft:double_wooden_slab", BlockID.DOUBLE_WOOD_SLAB);
+ public static final BlockType DOUBLE_WOODEN_SLAB = register("minecraft:double_wooden_slab", BlockID.DOUBLE_WOODEN_SLAB);
+ public static final BlockType DOUBLE_WOOD_SLABS = register("minecraft:double_wooden_slab", BlockID.DOUBLE_WOOD_SLABS);
+ public static final BlockType DOUBLE_WOODEN_SLABS = register("minecraft:double_wooden_slab", BlockID.DOUBLE_WOODEN_SLABS);
+ public static final BlockType WOOD_SLAB = register("minecraft:wooden_slab", BlockID.WOOD_SLAB);
+ public static final BlockType WOODEN_SLAB = register("minecraft:wooden_slab", BlockID.WOODEN_SLAB);
+ public static final BlockType WOOD_SLABS = register("minecraft:wooden_slab", BlockID.WOOD_SLABS);
+ public static final BlockType WOODEN_SLABS = register("minecraft:wooden_slab", BlockID.WOODEN_SLABS);
+ public static final BlockType STAINED_TERRACOTTA = register("minecraft:stained_hardened_clay", BlockID.STAINED_TERRACOTTA);
+ public static final BlockType STAINED_HARDENED_CLAY = register("minecraft:stained_hardened_clay", BlockID.STAINED_HARDENED_CLAY);
+ public static final BlockType STAINED_GLASS_PANE = register("minecraft:stained_glass_pane", BlockID.STAINED_GLASS_PANE);
+ public static final BlockType LEAVES2 = register("minecraft:leaves2", BlockID.LEAVES2);
+ public static final BlockType LEAVE2 = register("minecraft:leaves2", BlockID.LEAVE2);
+ public static final BlockType WOOD2 = register("minecraft:log2", BlockID.WOOD2);
+ public static final BlockType TRUNK2 = register("minecraft:log2", BlockID.TRUNK2);
+ public static final BlockType LOG2 = register("minecraft:log2", BlockID.LOG2);
+ public static final BlockType ACACIA_WOOD_STAIRS = register("minecraft:acacia_stairs", BlockID.ACACIA_WOOD_STAIRS);
+ public static final BlockType ACACIA_WOODEN_STAIRS = register("minecraft:acacia_stairs", BlockID.ACACIA_WOODEN_STAIRS);
+ public static final BlockType DARK_OAK_WOOD_STAIRS = register("minecraft:dark_oak_stairs", BlockID.DARK_OAK_WOOD_STAIRS);
+ public static final BlockType DARK_OAK_WOODEN_STAIRS = register("minecraft:dark_oak_stairs", BlockID.DARK_OAK_WOODEN_STAIRS);
+ public static final BlockType SLIME_BLOCK = register("minecraft:slime", BlockID.SLIME_BLOCK);
+ public static final BlockType SLIME = register("minecraft:slime", BlockID.SLIME);
+ public static final BlockType GLOW_STICK = register("minecraft:glow_stick", BlockID.GLOW_STICK);
+ public static final BlockType IRON_TRAPDOOR = register("minecraft:iron_trapdoor", BlockID.IRON_TRAPDOOR);
+ public static final BlockType PRISMARINE = register("minecraft:prismarine", BlockID.PRISMARINE);
+ public static final BlockType SEA_LANTERN = register("minecraft:sea_lantern", BlockID.SEA_LANTERN);
+ public static final BlockType HAY_BALE = register("minecraft:hay_block", BlockID.HAY_BALE);
+ public static final BlockType HAY_BLOCK = register("minecraft:hay_block", BlockID.HAY_BLOCK);
+ public static final BlockType CARPET = register("minecraft:carpet", BlockID.CARPET);
+ public static final BlockType TERRACOTTA = register("minecraft:hardened_clay", BlockID.TERRACOTTA);
+ public static final BlockType HARDENED_CLAY = register("minecraft:hardened_clay", BlockID.HARDENED_CLAY);
+ public static final BlockType COAL_BLOCK = register("minecraft:coal_block", BlockID.COAL_BLOCK);
+ public static final BlockType PACKED_ICE = register("minecraft:packed_ice", BlockID.PACKED_ICE);
+ public static final BlockType DOUBLE_PLANT = register("minecraft:double_plant", BlockID.DOUBLE_PLANT);
+ public static final BlockType STANDING_BANNER = register("minecraft:standing_banner", BlockID.STANDING_BANNER);
+ public static final BlockType WALL_BANNER = register("minecraft:wall_banner", BlockID.WALL_BANNER);
+ public static final BlockType DAYLIGHT_DETECTOR_INVERTED = register("minecraft:daylight_detector_inverted", BlockID.DAYLIGHT_DETECTOR_INVERTED);
+ public static final BlockType RED_SANDSTONE = register("minecraft:red_sandstone", BlockID.RED_SANDSTONE);
+ public static final BlockType RED_SANDSTONE_STAIRS = register("minecraft:red_sandstone_stairs", BlockID.RED_SANDSTONE_STAIRS);
+ public static final BlockType DOUBLE_RED_SANDSTONE_SLAB = register("minecraft:double_stone_block_slab2", BlockID.DOUBLE_RED_SANDSTONE_SLAB);
+ public static final BlockType RED_SANDSTONE_SLAB = register("minecraft:stone_block_slab2", BlockID.RED_SANDSTONE_SLAB);
+ public static final BlockType FENCE_GATE_SPRUCE = register("minecraft:spruce_fence_gate", BlockID.FENCE_GATE_SPRUCE);
+ public static final BlockType FENCE_GATE_BIRCH = register("minecraft:birch_fence_gate", BlockID.FENCE_GATE_BIRCH);
+ public static final BlockType FENCE_GATE_JUNGLE = register("minecraft:jungle_fence_gate", BlockID.FENCE_GATE_JUNGLE);
+ public static final BlockType FENCE_GATE_DARK_OAK = register("minecraft:dark_oak_fence_gate", BlockID.FENCE_GATE_DARK_OAK);
+ public static final BlockType FENCE_GATE_ACACIA = register("minecraft:acacia_fence_gate", BlockID.FENCE_GATE_ACACIA);
+ public static final BlockType REPEATING_COMMAND_BLOCK = register("minecraft:repeating_command_block", BlockID.REPEATING_COMMAND_BLOCK);
+ public static final BlockType CHAIN_COMMAND_BLOCK = register("minecraft:chain_command_block", BlockID.CHAIN_COMMAND_BLOCK);
+ public static final BlockType HARD_GLASS_PANE = register("minecraft:hard_glass_pane", BlockID.HARD_GLASS_PANE);
+ public static final BlockType HARD_STAINED_GLASS_PANE = register("minecraft:hard_stained_glass_pane", BlockID.HARD_STAINED_GLASS_PANE);
+ public static final BlockType CHEMICAL_HEAT = register("minecraft:chemical_heat", BlockID.CHEMICAL_HEAT);
+ public static final BlockType SPRUCE_DOOR_BLOCK = register("minecraft:item.spruce_door", BlockID.SPRUCE_DOOR_BLOCK);
+ public static final BlockType BIRCH_DOOR_BLOCK = register("minecraft:item.birch_door", BlockID.BIRCH_DOOR_BLOCK);
+ public static final BlockType JUNGLE_DOOR_BLOCK = register("minecraft:item.jungle_door", BlockID.JUNGLE_DOOR_BLOCK);
+ public static final BlockType ACACIA_DOOR_BLOCK = register("minecraft:item.acacia_door", BlockID.ACACIA_DOOR_BLOCK);
+ public static final BlockType DARK_OAK_DOOR_BLOCK = register("minecraft:item.dark_oak_door", BlockID.DARK_OAK_DOOR_BLOCK);
+ public static final BlockType GRASS_PATH = register("minecraft:grass_path", BlockID.GRASS_PATH);
+ public static final BlockType ITEM_FRAME_BLOCK = register("minecraft:item.frame", BlockID.ITEM_FRAME_BLOCK);
+ public static final BlockType CHORUS_FLOWER = register("minecraft:chorus_flower", BlockID.CHORUS_FLOWER);
+ public static final BlockType PURPUR_BLOCK = register("minecraft:purpur_block", BlockID.PURPUR_BLOCK);
+ public static final BlockType COLORED_TORCH_RG = register("minecraft:colored_torch_rg", BlockID.COLORED_TORCH_RG);
+ public static final BlockType PURPUR_STAIRS = register("minecraft:purpur_stairs", BlockID.PURPUR_STAIRS);
+ public static final BlockType COLORED_TORCH_BP = register("minecraft:colored_torch_bp", BlockID.COLORED_TORCH_BP);
+ public static final BlockType UNDYED_SHULKER_BOX = register("minecraft:undyed_shulker_box", BlockID.UNDYED_SHULKER_BOX);
+ public static final BlockType END_BRICKS = register("minecraft:end_bricks", BlockID.END_BRICKS);
+ public static final BlockType FROSTED_ICE = register("minecraft:frosted_ice", BlockID.FROSTED_ICE);
+ public static final BlockType ICE_FROSTED = register("minecraft:frosted_ice", BlockID.ICE_FROSTED);
+ public static final BlockType END_ROD = register("minecraft:end_rod", BlockID.END_ROD);
+ public static final BlockType END_GATEWAY = register("minecraft:end_gateway", BlockID.END_GATEWAY);
+ public static final BlockType ALLOW = register("minecraft:allow", BlockID.ALLOW);
+ public static final BlockType DENY = register("minecraft:deny", BlockID.DENY);
+ public static final BlockType BORDER_BLOCK = register("minecraft:border_block", BlockID.BORDER_BLOCK);
+ public static final BlockType MAGMA = register("minecraft:magma", BlockID.MAGMA);
+ public static final BlockType BLOCK_NETHER_WART_BLOCK = register("minecraft:nether_wart_block", BlockID.BLOCK_NETHER_WART_BLOCK);
+ public static final BlockType RED_NETHER_BRICK = register("minecraft:red_nether_brick", BlockID.RED_NETHER_BRICK);
+ public static final BlockType BONE_BLOCK = register("minecraft:bone_block", BlockID.BONE_BLOCK);
+ public static final BlockType SHULKER_BOX = register("minecraft:shulker_box", BlockID.SHULKER_BOX);
+ public static final BlockType PURPLE_GLAZED_TERRACOTTA = register("minecraft:purple_glazed_terracotta", BlockID.PURPLE_GLAZED_TERRACOTTA);
+ public static final BlockType WHITE_GLAZED_TERRACOTTA = register("minecraft:white_glazed_terracotta", BlockID.WHITE_GLAZED_TERRACOTTA);
+ public static final BlockType ORANGE_GLAZED_TERRACOTTA = register("minecraft:orange_glazed_terracotta", BlockID.ORANGE_GLAZED_TERRACOTTA);
+ public static final BlockType MAGENTA_GLAZED_TERRACOTTA = register("minecraft:magenta_glazed_terracotta", BlockID.MAGENTA_GLAZED_TERRACOTTA);
+ public static final BlockType LIGHT_BLUE_GLAZED_TERRACOTTA = register("minecraft:light_blue_glazed_terracotta", BlockID.LIGHT_BLUE_GLAZED_TERRACOTTA);
+ public static final BlockType YELLOW_GLAZED_TERRACOTTA = register("minecraft:yellow_glazed_terracotta", BlockID.YELLOW_GLAZED_TERRACOTTA);
+ public static final BlockType LIME_GLAZED_TERRACOTTA = register("minecraft:lime_glazed_terracotta", BlockID.LIME_GLAZED_TERRACOTTA);
+ public static final BlockType PINK_GLAZED_TERRACOTTA = register("minecraft:pink_glazed_terracotta", BlockID.PINK_GLAZED_TERRACOTTA);
+ public static final BlockType GRAY_GLAZED_TERRACOTTA = register("minecraft:gray_glazed_terracotta", BlockID.GRAY_GLAZED_TERRACOTTA);
+ public static final BlockType SILVER_GLAZED_TERRACOTTA = register("minecraft:silver_glazed_terracotta", BlockID.SILVER_GLAZED_TERRACOTTA);
+ public static final BlockType CYAN_GLAZED_TERRACOTTA = register("minecraft:cyan_glazed_terracotta", BlockID.CYAN_GLAZED_TERRACOTTA);
+ public static final BlockType BLUE_GLAZED_TERRACOTTA = register("minecraft:blue_glazed_terracotta", BlockID.BLUE_GLAZED_TERRACOTTA);
+ public static final BlockType BROWN_GLAZED_TERRACOTTA = register("minecraft:brown_glazed_terracotta", BlockID.BROWN_GLAZED_TERRACOTTA);
+ public static final BlockType GREEN_GLAZED_TERRACOTTA = register("minecraft:green_glazed_terracotta", BlockID.GREEN_GLAZED_TERRACOTTA);
+ public static final BlockType RED_GLAZED_TERRACOTTA = register("minecraft:red_glazed_terracotta", BlockID.RED_GLAZED_TERRACOTTA);
+ public static final BlockType BLACK_GLAZED_TERRACOTTA = register("minecraft:black_glazed_terracotta", BlockID.BLACK_GLAZED_TERRACOTTA);
+ public static final BlockType CONCRETE = register("minecraft:concrete", BlockID.CONCRETE);
+ public static final BlockType CONCRETE_POWDER = register("minecraft:concrete_powder", BlockID.CONCRETE_POWDER);
+ public static final BlockType CHEMISTRY_TABLE = register("minecraft:chemistry_table", BlockID.CHEMISTRY_TABLE);
+ public static final BlockType UNDERWATER_TORCH = register("minecraft:underwater_torch", BlockID.UNDERWATER_TORCH);
+ public static final BlockType CHORUS_PLANT = register("minecraft:chorus_plant", BlockID.CHORUS_PLANT);
+ public static final BlockType STAINED_GLASS = register("minecraft:stained_glass", BlockID.STAINED_GLASS);
+ public static final BlockType CAMERA_BLOCK = register("minecraft:item.camera", BlockID.CAMERA_BLOCK);
+ public static final BlockType PODZOL = register("minecraft:podzol", BlockID.PODZOL);
+ public static final BlockType BEETROOT_BLOCK = register("minecraft:item.beetroot", BlockID.BEETROOT_BLOCK);
+ public static final BlockType STONECUTTER = register("minecraft:stonecutter", BlockID.STONECUTTER);
+ public static final BlockType GLOWING_OBSIDIAN = register("minecraft:glowingobsidian", BlockID.GLOWING_OBSIDIAN);
+ public static final BlockType NETHER_REACTOR = register("minecraft:netherreactor", BlockID.NETHER_REACTOR);
+ public static final BlockType INFO_UPDATE = register("minecraft:info_update", BlockID.INFO_UPDATE);
+ public static final BlockType INFO_UPDATE2 = register("minecraft:info_update2", BlockID.INFO_UPDATE2);
+ public static final BlockType PISTON_EXTENSION = register("minecraft:moving_block", BlockID.PISTON_EXTENSION);
+ public static final BlockType MOVING_BLOCK = register("minecraft:moving_block", BlockID.MOVING_BLOCK);
+ public static final BlockType OBSERVER = register("minecraft:observer", BlockID.OBSERVER);
+ public static final BlockType STRUCTURE_BLOCK = register("minecraft:structure_block", BlockID.STRUCTURE_BLOCK);
+ public static final BlockType HARD_GLASS = register("minecraft:hard_glass", BlockID.HARD_GLASS);
+ public static final BlockType HARD_STAINED_GLASS = register("minecraft:hard_stained_glass", BlockID.HARD_STAINED_GLASS);
+ public static final BlockType RESERVED6 = register("minecraft:reserved6", BlockID.RESERVED6);
+ public static final BlockType PRISMARINE_STAIRS = register("minecraft:prismarine_stairs", BlockID.PRISMARINE_STAIRS);
+ public static final BlockType DARK_PRISMARINE_STAIRS = register("minecraft:dark_prismarine_stairs", BlockID.DARK_PRISMARINE_STAIRS);
+ public static final BlockType PRISMARINE_BRICKS_STAIRS = register("minecraft:prismarine_bricks_stairs", BlockID.PRISMARINE_BRICKS_STAIRS);
+ public static final BlockType STRIPPED_SPRUCE_LOG = register("minecraft:stripped_spruce_log", BlockID.STRIPPED_SPRUCE_LOG);
+ public static final BlockType STRIPPED_BIRCH_LOG = register("minecraft:stripped_birch_log", BlockID.STRIPPED_BIRCH_LOG);
+ public static final BlockType STRIPPED_JUNGLE_LOG = register("minecraft:stripped_jungle_log", BlockID.STRIPPED_JUNGLE_LOG);
+ public static final BlockType STRIPPED_ACACIA_LOG = register("minecraft:stripped_acacia_log", BlockID.STRIPPED_ACACIA_LOG);
+ public static final BlockType STRIPPED_DARK_OAK_LOG = register("minecraft:stripped_dark_oak_log", BlockID.STRIPPED_DARK_OAK_LOG);
+ public static final BlockType STRIPPED_OAK_LOG = register("minecraft:stripped_oak_log", BlockID.STRIPPED_OAK_LOG);
+ public static final BlockType BLUE_ICE = register("minecraft:blue_ice", BlockID.BLUE_ICE);
+ public static final BlockType SEAGRASS = register("minecraft:seagrass", BlockID.SEAGRASS);
+ public static final BlockType CORAL = register("minecraft:coral", BlockID.CORAL);
+ public static final BlockType CORAL_BLOCK = register("minecraft:coral_block", BlockID.CORAL_BLOCK);
+ public static final BlockType CORAL_FAN = register("minecraft:coral_fan", BlockID.CORAL_FAN);
+ public static final BlockType CORAL_FAN_DEAD = register("minecraft:coral_fan_dead", BlockID.CORAL_FAN_DEAD);
+ public static final BlockType CORAL_FAN_HANG = register("minecraft:coral_fan_hang", BlockID.CORAL_FAN_HANG);
+ public static final BlockType CORAL_FAN_HANG2 = register("minecraft:coral_fan_hang2", BlockID.CORAL_FAN_HANG2);
+ public static final BlockType CORAL_FAN_HANG3 = register("minecraft:coral_fan_hang3", BlockID.CORAL_FAN_HANG3);
+ public static final BlockType BLOCK_KELP = register("minecraft:item.kelp", BlockID.BLOCK_KELP);
+ public static final BlockType DRIED_KELP_BLOCK = register("minecraft:dried_kelp_block", BlockID.DRIED_KELP_BLOCK);
+ public static final BlockType ACACIA_BUTTON = register("minecraft:acacia_button", BlockID.ACACIA_BUTTON);
+ public static final BlockType BIRCH_BUTTON = register("minecraft:birch_button", BlockID.BIRCH_BUTTON);
+ public static final BlockType DARK_OAK_BUTTON = register("minecraft:dark_oak_button", BlockID.DARK_OAK_BUTTON);
+ public static final BlockType JUNGLE_BUTTON = register("minecraft:jungle_button", BlockID.JUNGLE_BUTTON);
+ public static final BlockType SPRUCE_BUTTON = register("minecraft:spruce_button", BlockID.SPRUCE_BUTTON);
+ public static final BlockType ACACIA_TRAPDOOR = register("minecraft:acacia_trapdoor", BlockID.ACACIA_TRAPDOOR);
+ public static final BlockType BIRCH_TRAPDOOR = register("minecraft:birch_trapdoor", BlockID.BIRCH_TRAPDOOR);
+ public static final BlockType DARK_OAK_TRAPDOOR = register("minecraft:dark_oak_trapdoor", BlockID.DARK_OAK_TRAPDOOR);
+ public static final BlockType JUNGLE_TRAPDOOR = register("minecraft:jungle_trapdoor", BlockID.JUNGLE_TRAPDOOR);
+ public static final BlockType SPRUCE_TRAPDOOR = register("minecraft:spruce_trapdoor", BlockID.SPRUCE_TRAPDOOR);
+ public static final BlockType ACACIA_PRESSURE_PLATE = register("minecraft:acacia_pressure_plate", BlockID.ACACIA_PRESSURE_PLATE);
+ public static final BlockType BIRCH_PRESSURE_PLATE = register("minecraft:birch_pressure_plate", BlockID.BIRCH_PRESSURE_PLATE);
+ public static final BlockType DARK_OAK_PRESSURE_PLATE = register("minecraft:dark_oak_pressure_plate", BlockID.DARK_OAK_PRESSURE_PLATE);
+ public static final BlockType JUNGLE_PRESSURE_PLATE = register("minecraft:jungle_pressure_plate", BlockID.JUNGLE_PRESSURE_PLATE);
+ public static final BlockType SPRUCE_PRESSURE_PLATE = register("minecraft:spruce_pressure_plate", BlockID.SPRUCE_PRESSURE_PLATE);
+ public static final BlockType CARVED_PUMPKIN = register("minecraft:carved_pumpkin", BlockID.CARVED_PUMPKIN);
+ public static final BlockType SEA_PICKLE = register("minecraft:sea_pickle", BlockID.SEA_PICKLE);
+ public static final BlockType CONDUIT = register("minecraft:conduit", BlockID.CONDUIT);
+ public static final BlockType TURTLE_EGG = register("minecraft:turtle_egg", BlockID.TURTLE_EGG);
+ public static final BlockType BUBBLE_COLUMN = register("minecraft:bubble_column", BlockID.BUBBLE_COLUMN);
+ public static final BlockType BARRIER = register("minecraft:barrier", BlockID.BARRIER);
+ public static final BlockType STONE_SLAB3 = register("minecraft:stone_block_slab3", BlockID.STONE_SLAB3);
+ public static final BlockType BAMBOO = register("minecraft:bamboo", BlockID.BAMBOO);
+ public static final BlockType BAMBOO_SAPLING = register("minecraft:bamboo_sapling", BlockID.BAMBOO_SAPLING);
+ public static final BlockType SCAFFOLDING = register("minecraft:scaffolding", BlockID.SCAFFOLDING);
+ public static final BlockType STONE_SLAB4 = register("minecraft:stone_block_slab4", BlockID.STONE_SLAB4);
+ public static final BlockType DOUBLE_STONE_SLAB3 = register("minecraft:double_stone_block_slab3", BlockID.DOUBLE_STONE_SLAB3);
+ public static final BlockType DOUBLE_STONE_SLAB4 = register("minecraft:double_stone_block_slab4", BlockID.DOUBLE_STONE_SLAB4);
+ public static final BlockType GRANITE_STAIRS = register("minecraft:granite_stairs", BlockID.GRANITE_STAIRS);
+ public static final BlockType DIORITE_STAIRS = register("minecraft:diorite_stairs", BlockID.DIORITE_STAIRS);
+ public static final BlockType ANDESITE_STAIRS = register("minecraft:andesite_stairs", BlockID.ANDESITE_STAIRS);
+ public static final BlockType POLISHED_GRANITE_STAIRS = register("minecraft:polished_granite_stairs", BlockID.POLISHED_GRANITE_STAIRS);
+ public static final BlockType POLISHED_DIORITE_STAIRS = register("minecraft:polished_diorite_stairs", BlockID.POLISHED_DIORITE_STAIRS);
+ public static final BlockType POLISHED_ANDESITE_STAIRS = register("minecraft:polished_andesite_stairs", BlockID.POLISHED_ANDESITE_STAIRS);
+ public static final BlockType MOSSY_STONE_BRICK_STAIRS = register("minecraft:mossy_stone_brick_stairs", BlockID.MOSSY_STONE_BRICK_STAIRS);
+ public static final BlockType SMOOTH_RED_SANDSTONE_STAIRS = register("minecraft:smooth_red_sandstone_stairs", BlockID.SMOOTH_RED_SANDSTONE_STAIRS);
+ public static final BlockType SMOOTH_SANDSTONE_STAIRS = register("minecraft:smooth_sandstone_stairs", BlockID.SMOOTH_SANDSTONE_STAIRS);
+ public static final BlockType END_BRICK_STAIRS = register("minecraft:end_brick_stairs", BlockID.END_BRICK_STAIRS);
+ public static final BlockType MOSSY_COBBLESTONE_STAIRS = register("minecraft:mossy_cobblestone_stairs", BlockID.MOSSY_COBBLESTONE_STAIRS);
+ public static final BlockType NORMAL_STONE_STAIRS = register("minecraft:normal_stone_stairs", BlockID.NORMAL_STONE_STAIRS);
+ public static final BlockType SPRUCE_STANDING_SIGN = register("minecraft:spruce_standing_sign", BlockID.SPRUCE_STANDING_SIGN);
+ public static final BlockType SPRUCE_WALL_SIGN = register("minecraft:spruce_wall_sign", BlockID.SPRUCE_WALL_SIGN);
+ public static final BlockType SMOOTH_STONE = register("minecraft:smooth_stone", BlockID.SMOOTH_STONE);
+ public static final BlockType RED_NETHER_BRICK_STAIRS = register("minecraft:red_nether_brick_stairs", BlockID.RED_NETHER_BRICK_STAIRS);
+ public static final BlockType SMOOTH_QUARTZ_STAIRS = register("minecraft:smooth_quartz_stairs", BlockID.SMOOTH_QUARTZ_STAIRS);
+ public static final BlockType BIRCH_STANDING_SIGN = register("minecraft:birch_standing_sign", BlockID.BIRCH_STANDING_SIGN);
+ public static final BlockType BIRCH_WALL_SIGN = register("minecraft:birch_wall_sign", BlockID.BIRCH_WALL_SIGN);
+ public static final BlockType JUNGLE_STANDING_SIGN = register("minecraft:jungle_standing_sign", BlockID.JUNGLE_STANDING_SIGN);
+ public static final BlockType JUNGLE_WALL_SIGN = register("minecraft:jungle_wall_sign", BlockID.JUNGLE_WALL_SIGN);
+ public static final BlockType ACACIA_STANDING_SIGN = register("minecraft:acacia_standing_sign", BlockID.ACACIA_STANDING_SIGN);
+ public static final BlockType ACACIA_WALL_SIGN = register("minecraft:acacia_wall_sign", BlockID.ACACIA_WALL_SIGN);
+ public static final BlockType DARK_OAK_STANDING_SIGN = register("minecraft:darkoak_standing_sign", BlockID.DARK_OAK_STANDING_SIGN);
+ public static final BlockType DARK_OAK_WALL_SIGN = register("minecraft:darkoak_wall_sign", BlockID.DARK_OAK_WALL_SIGN);
+ public static final BlockType LECTERN = register("minecraft:lectern", BlockID.LECTERN);
+ public static final BlockType GRINDSTONE = register("minecraft:grindstone", BlockID.GRINDSTONE);
+ public static final BlockType BLAST_FURNACE = register("minecraft:blast_furnace", BlockID.BLAST_FURNACE);
+ public static final BlockType STONECUTTER_BLOCK = register("minecraft:stonecutter_block", BlockID.STONECUTTER_BLOCK);
+ public static final BlockType SMOKER = register("minecraft:smoker", BlockID.SMOKER);
+ public static final BlockType LIT_SMOKER = register("minecraft:lit_smoker", BlockID.LIT_SMOKER);
+ public static final BlockType CARTOGRAPHY_TABLE = register("minecraft:cartography_table", BlockID.CARTOGRAPHY_TABLE);
+ public static final BlockType FLETCHING_TABLE = register("minecraft:fletching_table", BlockID.FLETCHING_TABLE);
+ public static final BlockType SMITHING_TABLE = register("minecraft:smithing_table", BlockID.SMITHING_TABLE);
+ public static final BlockType BARREL = register("minecraft:barrel", BlockID.BARREL);
+ public static final BlockType LOOM = register("minecraft:loom", BlockID.LOOM);
+ public static final BlockType BELL = register("minecraft:bell", BlockID.BELL);
+ public static final BlockType SWEET_BERRY_BUSH = register("minecraft:sweet_berry_bush", BlockID.SWEET_BERRY_BUSH);
+ public static final BlockType LANTERN = register("minecraft:lantern", BlockID.LANTERN);
+ public static final BlockType CAMPFIRE_BLOCK = register("minecraft:item.campfire", BlockID.CAMPFIRE_BLOCK);
+ public static final BlockType LAVA_CAULDRON = register("minecraft:lava_cauldron", BlockID.LAVA_CAULDRON);
+ public static final BlockType JIGSAW = register("minecraft:jigsaw", BlockID.JIGSAW);
+ public static final BlockType WOOD_BARK = register("minecraft:wood", BlockID.WOOD_BARK);
+ public static final BlockType COMPOSTER = register("minecraft:composter", BlockID.COMPOSTER);
+ public static final BlockType LIT_BLAST_FURNACE = register("minecraft:lit_blast_furnace", BlockID.LIT_BLAST_FURNACE);
+ public static final BlockType LIGHT_BLOCK = register("minecraft:light_block", BlockID.LIGHT_BLOCK);
+ public static final BlockType WITHER_ROSE = register("minecraft:wither_rose", BlockID.WITHER_ROSE);
+ public static final BlockType PISTON_HEAD_STICKY = register("minecraft:sticky_piston_arm_collision", BlockID.PISTON_HEAD_STICKY);
+ public static final BlockType BEE_NEST = register("minecraft:bee_nest", BlockID.BEE_NEST);
+ public static final BlockType BEEHIVE = register("minecraft:beehive", BlockID.BEEHIVE);
+ public static final BlockType HONEY_BLOCK = register("minecraft:honey_block", BlockID.HONEY_BLOCK);
+ public static final BlockType HONEYCOMB_BLOCK = register("minecraft:honeycomb_block", BlockID.HONEYCOMB_BLOCK);
+ public static final BlockType LODESTONE = register("minecraft:lodestone", BlockID.LODESTONE);
+ public static final BlockType CRIMSON_ROOTS = register("minecraft:crimson_roots", BlockID.CRIMSON_ROOTS);
+ public static final BlockType WARPED_ROOTS = register("minecraft:warped_roots", BlockID.WARPED_ROOTS);
+ public static final BlockType CRIMSON_STEM = register("minecraft:crimson_stem", BlockID.CRIMSON_STEM);
+ public static final BlockType WARPED_STEM = register("minecraft:warped_stem", BlockID.WARPED_STEM);
+ public static final BlockType WARPED_WART_BLOCK = register("minecraft:warped_wart_block", BlockID.WARPED_WART_BLOCK);
+ public static final BlockType CRIMSON_FUNGUS = register("minecraft:crimson_fungus", BlockID.CRIMSON_FUNGUS);
+ public static final BlockType WARPED_FUNGUS = register("minecraft:warped_fungus", BlockID.WARPED_FUNGUS);
+ public static final BlockType SHROOMLIGHT = register("minecraft:shroomlight", BlockID.SHROOMLIGHT);
+ public static final BlockType WEEPING_VINES = register("minecraft:weeping_vines", BlockID.WEEPING_VINES);
+ public static final BlockType CRIMSON_NYLIUM = register("minecraft:crimson_nylium", BlockID.CRIMSON_NYLIUM);
+ public static final BlockType WARPED_NYLIUM = register("minecraft:warped_nylium", BlockID.WARPED_NYLIUM);
+ public static final BlockType BASALT = register("minecraft:basalt", BlockID.BASALT);
+ public static final BlockType POLISHED_BASALT = register("minecraft:polished_basalt", BlockID.POLISHED_BASALT);
+ public static final BlockType SOUL_SOIL = register("minecraft:soul_soil", BlockID.SOUL_SOIL);
+ public static final BlockType SOUL_FIRE = register("minecraft:soul_fire", BlockID.SOUL_FIRE);
+ public static final BlockType NETHER_SPROUTS_BLOCK = register("minecraft:item.nether_sprouts", BlockID.NETHER_SPROUTS_BLOCK);
+ public static final BlockType TARGET = register("minecraft:target", BlockID.TARGET);
+ public static final BlockType STRIPPED_CRIMSON_STEM = register("minecraft:stripped_crimson_stem", BlockID.STRIPPED_CRIMSON_STEM);
+ public static final BlockType STRIPPED_WARPED_STEM = register("minecraft:stripped_warped_stem", BlockID.STRIPPED_WARPED_STEM);
+ public static final BlockType CRIMSON_PLANKS = register("minecraft:crimson_planks", BlockID.CRIMSON_PLANKS);
+ public static final BlockType WARPED_PLANKS = register("minecraft:warped_planks", BlockID.WARPED_PLANKS);
+ public static final BlockType CRIMSON_DOOR_BLOCK = register("minecraft:item.crimson_door", BlockID.CRIMSON_DOOR_BLOCK);
+ public static final BlockType WARPED_DOOR_BLOCK = register("minecraft:item.warped_door", BlockID.WARPED_DOOR_BLOCK);
+ public static final BlockType CRIMSON_TRAPDOOR = register("minecraft:crimson_trapdoor", BlockID.CRIMSON_TRAPDOOR);
+ public static final BlockType WARPED_TRAPDOOR = register("minecraft:warped_trapdoor", BlockID.WARPED_TRAPDOOR);
+ public static final BlockType CRIMSON_STANDING_SIGN = register("minecraft:crimson_standing_sign", BlockID.CRIMSON_STANDING_SIGN);
+ public static final BlockType WARPED_STANDING_SIGN = register("minecraft:warped_standing_sign", BlockID.WARPED_STANDING_SIGN);
+ public static final BlockType CRIMSON_WALL_SIGN = register("minecraft:crimson_wall_sign", BlockID.CRIMSON_WALL_SIGN);
+ public static final BlockType WARPED_WALL_SIGN = register("minecraft:warped_wall_sign", BlockID.WARPED_WALL_SIGN);
+ public static final BlockType CRIMSON_STAIRS = register("minecraft:crimson_stairs", BlockID.CRIMSON_STAIRS);
+ public static final BlockType WARPED_STAIRS = register("minecraft:warped_stairs", BlockID.WARPED_STAIRS);
+ public static final BlockType CRIMSON_FENCE = register("minecraft:crimson_fence", BlockID.CRIMSON_FENCE);
+ public static final BlockType WARPED_FENCE = register("minecraft:warped_fence", BlockID.WARPED_FENCE);
+ public static final BlockType CRIMSON_FENCE_GATE = register("minecraft:crimson_fence_gate", BlockID.CRIMSON_FENCE_GATE);
+ public static final BlockType WARPED_FENCE_GATE = register("minecraft:warped_fence_gate", BlockID.WARPED_FENCE_GATE);
+ public static final BlockType CRIMSON_BUTTON = register("minecraft:crimson_button", BlockID.CRIMSON_BUTTON);
+ public static final BlockType WARPED_BUTTON = register("minecraft:warped_button", BlockID.WARPED_BUTTON);
+ public static final BlockType CRIMSON_PRESSURE_PLATE = register("minecraft:crimson_pressure_plate", BlockID.CRIMSON_PRESSURE_PLATE);
+ public static final BlockType WARPED_PRESSURE_PLATE = register("minecraft:warped_pressure_plate", BlockID.WARPED_PRESSURE_PLATE);
+ public static final BlockType CRIMSON_SLAB = register("minecraft:crimson_slab", BlockID.CRIMSON_SLAB);
+ public static final BlockType WARPED_SLAB = register("minecraft:warped_slab", BlockID.WARPED_SLAB);
+ public static final BlockType CRIMSON_DOUBLE_SLAB = register("minecraft:crimson_double_slab", BlockID.CRIMSON_DOUBLE_SLAB);
+ public static final BlockType WARPED_DOUBLE_SLAB = register("minecraft:warped_double_slab", BlockID.WARPED_DOUBLE_SLAB);
+ public static final BlockType SOUL_TORCH = register("minecraft:soul_torch", BlockID.SOUL_TORCH);
+ public static final BlockType SOUL_LANTERN = register("minecraft:soul_lantern", BlockID.SOUL_LANTERN);
+ public static final BlockType NETHERITE_BLOCK = register("minecraft:netherite_block", BlockID.NETHERITE_BLOCK);
+ public static final BlockType ANCIENT_DEBRIS = register("minecraft:ancient_debris", BlockID.ANCIENT_DEBRIS);
+ public static final BlockType RESPAWN_ANCHOR = register("minecraft:respawn_anchor", BlockID.RESPAWN_ANCHOR);
+ public static final BlockType BLACKSTONE = register("minecraft:blackstone", BlockID.BLACKSTONE);
+ public static final BlockType POLISHED_BLACKSTONE_BRICKS = register("minecraft:polished_blackstone_bricks", BlockID.POLISHED_BLACKSTONE_BRICKS);
+ public static final BlockType POLISHED_BLACKSTONE_BRICK_STAIRS = register("minecraft:polished_blackstone_brick_stairs", BlockID.POLISHED_BLACKSTONE_BRICK_STAIRS);
+ public static final BlockType BLACKSTONE_STAIRS = register("minecraft:blackstone_stairs", BlockID.BLACKSTONE_STAIRS);
+ public static final BlockType BLACKSTONE_WALL = register("minecraft:blackstone_wall", BlockID.BLACKSTONE_WALL);
+ public static final BlockType POLISHED_BLACKSTONE_BRICK_WALL = register("minecraft:polished_blackstone_brick_wall", BlockID.POLISHED_BLACKSTONE_BRICK_WALL);
+ public static final BlockType CHISELED_POLISHED_BLACKSTONE = register("minecraft:chiseled_polished_blackstone", BlockID.CHISELED_POLISHED_BLACKSTONE);
+ public static final BlockType CRACKED_POLISHED_BLACKSTONE_BRICKS = register("minecraft:cracked_polished_blackstone_bricks", BlockID.CRACKED_POLISHED_BLACKSTONE_BRICKS);
+ public static final BlockType GILDED_BLACKSTONE = register("minecraft:gilded_blackstone", BlockID.GILDED_BLACKSTONE);
+ public static final BlockType BLACKSTONE_SLAB = register("minecraft:blackstone_slab", BlockID.BLACKSTONE_SLAB);
+ public static final BlockType BLACKSTONE_DOUBLE_SLAB = register("minecraft:blackstone_double_slab", BlockID.BLACKSTONE_DOUBLE_SLAB);
+ public static final BlockType POLISHED_BLACKSTONE_BRICK_SLAB = register("minecraft:polished_blackstone_brick_slab", BlockID.POLISHED_BLACKSTONE_BRICK_SLAB);
+ public static final BlockType POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB = register("minecraft:polished_blackstone_brick_double_slab", BlockID.POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB);
+ public static final BlockType CHAIN_BLOCK = register("minecraft:item.chain", BlockID.CHAIN_BLOCK);
+ public static final BlockType TWISTING_VINES = register("minecraft:twisting_vines", BlockID.TWISTING_VINES);
+ public static final BlockType NETHER_GOLD_ORE = register("minecraft:nether_gold_ore", BlockID.NETHER_GOLD_ORE);
+ public static final BlockType CRYING_OBSIDIAN = register("minecraft:crying_obsidian", BlockID.CRYING_OBSIDIAN);
+ public static final BlockType SOUL_CAMPFIRE_BLOCK = register("minecraft:item.soul_campfire", BlockID.SOUL_CAMPFIRE_BLOCK);
+ public static final BlockType POLISHED_BLACKSTONE = register("minecraft:polished_blackstone", BlockID.POLISHED_BLACKSTONE);
+ public static final BlockType POLISHED_BLACKSTONE_STAIRS = register("minecraft:polished_blackstone_stairs", BlockID.POLISHED_BLACKSTONE_STAIRS);
+ public static final BlockType POLISHED_BLACKSTONE_SLAB = register("minecraft:polished_blackstone_slab", BlockID.POLISHED_BLACKSTONE_SLAB);
+ public static final BlockType POLISHED_BLACKSTONE_DOUBLE_SLAB = register("minecraft:polished_blackstone_double_slab", BlockID.POLISHED_BLACKSTONE_DOUBLE_SLAB);
+ public static final BlockType POLISHED_BLACKSTONE_PRESSURE_PLATE = register("minecraft:polished_blackstone_pressure_plate", BlockID.POLISHED_BLACKSTONE_PRESSURE_PLATE);
+ public static final BlockType POLISHED_BLACKSTONE_BUTTON = register("minecraft:polished_blackstone_button", BlockID.POLISHED_BLACKSTONE_BUTTON);
+ public static final BlockType POLISHED_BLACKSTONE_WALL = register("minecraft:polished_blackstone_wall", BlockID.POLISHED_BLACKSTONE_WALL);
+ public static final BlockType WARPED_HYPHAE = register("minecraft:warped_hyphae", BlockID.WARPED_HYPHAE);
+ public static final BlockType CRIMSON_HYPHAE = register("minecraft:crimson_hyphae", BlockID.CRIMSON_HYPHAE);
+ public static final BlockType STRIPPED_CRIMSON_HYPHAE = register("minecraft:stripped_crimson_hyphae", BlockID.STRIPPED_CRIMSON_HYPHAE);
+ public static final BlockType STRIPPED_WARPED_HYPHAE = register("minecraft:stripped_warped_hyphae", BlockID.STRIPPED_WARPED_HYPHAE);
+ public static final BlockType CHISELED_NETHER_BRICKS = register("minecraft:chiseled_nether_bricks", BlockID.CHISELED_NETHER_BRICKS);
+ public static final BlockType CRACKED_NETHER_BRICKS = register("minecraft:cracked_nether_bricks", BlockID.CRACKED_NETHER_BRICKS);
+ public static final BlockType QUARTZ_BRICKS = register("minecraft:quartz_bricks", BlockID.QUARTZ_BRICKS);
+ public static final BlockType POWDER_SNOW = register("minecraft:powder_snow", BlockID.POWDER_SNOW);
+ public static final BlockType SCULK_SENSOR = register("minecraft:sculk_sensor", BlockID.SCULK_SENSOR);
+ public static final BlockType POINTED_DRIPSTONE = register("minecraft:pointed_dripstone", BlockID.POINTED_DRIPSTONE);
+ public static final BlockType COPPER_ORE = register("minecraft:copper_ore", BlockID.COPPER_ORE);
+ public static final BlockType LIGHTNING_ROD = register("minecraft:lightning_rod", BlockID.LIGHTNING_ROD);
+ public static final BlockType DRIPSTONE_BLOCK = register("minecraft:dripstone_block", BlockID.DRIPSTONE_BLOCK);
+ public static final BlockType ROOTED_DIRT = register("minecraft:dirt_with_roots", BlockID.ROOTED_DIRT);
+ public static final BlockType HANGING_ROOTS = register("minecraft:hanging_roots", BlockID.HANGING_ROOTS);
+ public static final BlockType MOSS_BLOCK = register("minecraft:moss_block", BlockID.MOSS_BLOCK);
+ public static final BlockType SPORE_BLOSSOM = register("minecraft:spore_blossom", BlockID.SPORE_BLOSSOM);
+ public static final BlockType CAVE_VINES = register("minecraft:cave_vines", BlockID.CAVE_VINES);
+ public static final BlockType BIG_DRIPLEAF = register("minecraft:big_dripleaf", BlockID.BIG_DRIPLEAF);
+ public static final BlockType AZALEA_LEAVES = register("minecraft:azalea_leaves", BlockID.AZALEA_LEAVES);
+ public static final BlockType AZALEA_LEAVES_FLOWERED = register("minecraft:azalea_leaves_flowered", BlockID.AZALEA_LEAVES_FLOWERED);
+ public static final BlockType CALCITE = register("minecraft:calcite", BlockID.CALCITE);
+ public static final BlockType AMETHYST_BLOCK = register("minecraft:amethyst_block", BlockID.AMETHYST_BLOCK);
+ public static final BlockType BUDDING_AMETHYST = register("minecraft:budding_amethyst", BlockID.BUDDING_AMETHYST);
+ public static final BlockType AMETHYST_CLUSTER = register("minecraft:amethyst_cluster", BlockID.AMETHYST_CLUSTER);
+ public static final BlockType LARGE_AMETHYST_BUD = register("minecraft:large_amethyst_bud", BlockID.LARGE_AMETHYST_BUD);
+ public static final BlockType MEDIUM_AMETHYST_BUD = register("minecraft:medium_amethyst_bud", BlockID.MEDIUM_AMETHYST_BUD);
+ public static final BlockType SMALL_AMETHYST_BUD = register("minecraft:small_amethyst_bud", BlockID.SMALL_AMETHYST_BUD);
+ public static final BlockType TUFF = register("minecraft:tuff", BlockID.TUFF);
+ public static final BlockType TINTED_GLASS = register("minecraft:tinted_glass", BlockID.TINTED_GLASS);
+ public static final BlockType MOSS_CARPET = register("minecraft:moss_carpet", BlockID.MOSS_CARPET);
+ public static final BlockType SMALL_DRIPLEAF = register("minecraft:small_dripleaf_block", BlockID.SMALL_DRIPLEAF);
+ public static final BlockType AZALEA = register("minecraft:azalea", BlockID.AZALEA);
+ public static final BlockType FLOWERING_AZALEA = register("minecraft:flowering_azalea", BlockID.FLOWERING_AZALEA);
+ public static final BlockType GLOW_FRAME = register("minecraft:item.glow_frame", BlockID.GLOW_FRAME);
+ public static final BlockType COPPER_BLOCK = register("minecraft:copper_block", BlockID.COPPER_BLOCK);
+ public static final BlockType EXPOSED_COPPER = register("minecraft:exposed_copper", BlockID.EXPOSED_COPPER);
+ public static final BlockType WEATHERED_COPPER = register("minecraft:weathered_copper", BlockID.WEATHERED_COPPER);
+ public static final BlockType OXIDIZED_COPPER = register("minecraft:oxidized_copper", BlockID.OXIDIZED_COPPER);
+ public static final BlockType WAXED_COPPER = register("minecraft:waxed_copper", BlockID.WAXED_COPPER);
+ public static final BlockType WAXED_EXPOSED_COPPER = register("minecraft:waxed_exposed_copper", BlockID.WAXED_EXPOSED_COPPER);
+ public static final BlockType WAXED_WEATHERED_COPPER = register("minecraft:waxed_weathered_copper", BlockID.WAXED_WEATHERED_COPPER);
+ public static final BlockType CUT_COPPER = register("minecraft:cut_copper", BlockID.CUT_COPPER);
+ public static final BlockType EXPOSED_CUT_COPPER = register("minecraft:exposed_cut_copper", BlockID.EXPOSED_CUT_COPPER);
+ public static final BlockType WEATHERED_CUT_COPPER = register("minecraft:weathered_cut_copper", BlockID.WEATHERED_CUT_COPPER);
+ public static final BlockType OXIDIZED_CUT_COPPER = register("minecraft:oxidized_cut_copper", BlockID.OXIDIZED_CUT_COPPER);
+ public static final BlockType WAXED_CUT_COPPER = register("minecraft:waxed_cut_copper", BlockID.WAXED_CUT_COPPER);
+ public static final BlockType WAXED_EXPOSED_CUT_COPPER = register("minecraft:waxed_exposed_cut_copper", BlockID.WAXED_EXPOSED_CUT_COPPER);
+ public static final BlockType WAXED_WEATHERED_CUT_COPPER = register("minecraft:waxed_weathered_cut_copper", BlockID.WAXED_WEATHERED_CUT_COPPER);
+ public static final BlockType CUT_COPPER_STAIRS = register("minecraft:cut_copper_stairs", BlockID.CUT_COPPER_STAIRS);
+ public static final BlockType EXPOSED_CUT_COPPER_STAIRS = register("minecraft:exposed_cut_copper_stairs", BlockID.EXPOSED_CUT_COPPER_STAIRS);
+ public static final BlockType WEATHERED_CUT_COPPER_STAIRS = register("minecraft:weathered_cut_copper_stairs", BlockID.WEATHERED_CUT_COPPER_STAIRS);
+ public static final BlockType OXIDIZED_CUT_COPPER_STAIRS = register("minecraft:oxidized_cut_copper_stairs", BlockID.OXIDIZED_CUT_COPPER_STAIRS);
+ public static final BlockType WAXED_CUT_COPPER_STAIRS = register("minecraft:waxed_cut_copper_stairs", BlockID.WAXED_CUT_COPPER_STAIRS);
+ public static final BlockType WAXED_EXPOSED_CUT_COPPER_STAIRS = register("minecraft:waxed_exposed_cut_copper_stairs", BlockID.WAXED_EXPOSED_CUT_COPPER_STAIRS);
+ public static final BlockType WAXED_WEATHERED_CUT_COPPER_STAIRS = register("minecraft:waxed_weathered_cut_copper_stairs", BlockID.WAXED_WEATHERED_CUT_COPPER_STAIRS);
+ public static final BlockType CUT_COPPER_SLAB = register("minecraft:cut_copper_slab", BlockID.CUT_COPPER_SLAB);
+ public static final BlockType EXPOSED_CUT_COPPER_SLAB = register("minecraft:exposed_cut_copper_slab", BlockID.EXPOSED_CUT_COPPER_SLAB);
+ public static final BlockType WEATHERED_CUT_COPPER_SLAB = register("minecraft:weathered_cut_copper_slab", BlockID.WEATHERED_CUT_COPPER_SLAB);
+ public static final BlockType OXIDIZED_CUT_COPPER_SLAB = register("minecraft:oxidized_cut_copper_slab", BlockID.OXIDIZED_CUT_COPPER_SLAB);
+ public static final BlockType WAXED_CUT_COPPER_SLAB = register("minecraft:waxed_cut_copper_slab", BlockID.WAXED_CUT_COPPER_SLAB);
+ public static final BlockType WAXED_EXPOSED_CUT_COPPER_SLAB = register("minecraft:waxed_exposed_cut_copper_slab", BlockID.WAXED_EXPOSED_CUT_COPPER_SLAB);
+ public static final BlockType WAXED_WEATHERED_CUT_COPPER_SLAB = register("minecraft:waxed_weathered_cut_copper_slab", BlockID.WAXED_WEATHERED_CUT_COPPER_SLAB);
+ public static final BlockType DOUBLE_CUT_COPPER_SLAB = register("minecraft:double_cut_copper_slab", BlockID.DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType EXPOSED_DOUBLE_CUT_COPPER_SLAB = register("minecraft:exposed_double_cut_copper_slab", BlockID.EXPOSED_DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType WEATHERED_DOUBLE_CUT_COPPER_SLAB = register("minecraft:weathered_double_cut_copper_slab", BlockID.WEATHERED_DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType OXIDIZED_DOUBLE_CUT_COPPER_SLAB = register("minecraft:oxidized_double_cut_copper_slab", BlockID.OXIDIZED_DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType WAXED_DOUBLE_CUT_COPPER_SLAB = register("minecraft:waxed_double_cut_copper_slab", BlockID.WAXED_DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB = register("minecraft:waxed_exposed_double_cut_copper_slab", BlockID.WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB = register("minecraft:waxed_weathered_double_cut_copper_slab", BlockID.WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType CAVE_VINES_BODY_WITH_BERRIES = register("minecraft:cave_vines_body_with_berries", BlockID.CAVE_VINES_BODY_WITH_BERRIES);
+ public static final BlockType CAVE_VINES_HEAD_WITH_BERRIES = register("minecraft:cave_vines_head_with_berries", BlockID.CAVE_VINES_HEAD_WITH_BERRIES);
+ public static final BlockType SMOOTH_BASALT = register("minecraft:smooth_basalt", BlockID.SMOOTH_BASALT);
+ public static final BlockType DEEPSLATE = register("minecraft:deepslate", BlockID.DEEPSLATE);
+ public static final BlockType COBBLED_DEEPSLATE = register("minecraft:cobbled_deepslate", BlockID.COBBLED_DEEPSLATE);
+ public static final BlockType COBBLED_DEEPSLATE_SLAB = register("minecraft:cobbled_deepslate_slab", BlockID.COBBLED_DEEPSLATE_SLAB);
+ public static final BlockType COBBLED_DEEPSLATE_STAIRS = register("minecraft:cobbled_deepslate_stairs", BlockID.COBBLED_DEEPSLATE_STAIRS);
+ public static final BlockType COBBLED_DEEPSLATE_WALL = register("minecraft:cobbled_deepslate_wall", BlockID.COBBLED_DEEPSLATE_WALL);
+ public static final BlockType POLISHED_DEEPSLATE = register("minecraft:polished_deepslate", BlockID.POLISHED_DEEPSLATE);
+ public static final BlockType POLISHED_DEEPSLATE_SLAB = register("minecraft:polished_deepslate_slab", BlockID.POLISHED_DEEPSLATE_SLAB);
+ public static final BlockType POLISHED_DEEPSLATE_STAIRS = register("minecraft:polished_deepslate_stairs", BlockID.POLISHED_DEEPSLATE_STAIRS);
+ public static final BlockType POLISHED_DEEPSLATE_WALL = register("minecraft:polished_deepslate_wall", BlockID.POLISHED_DEEPSLATE_WALL);
+ public static final BlockType DEEPSLATE_TILES = register("minecraft:deepslate_tiles", BlockID.DEEPSLATE_TILES);
+ public static final BlockType DEEPSLATE_TILE_SLAB = register("minecraft:deepslate_tile_slab", BlockID.DEEPSLATE_TILE_SLAB);
+ public static final BlockType DEEPSLATE_TILE_STAIRS = register("minecraft:deepslate_tile_stairs", BlockID.DEEPSLATE_TILE_STAIRS);
+ public static final BlockType DEEPSLATE_TILE_WALL = register("minecraft:deepslate_tile_wall", BlockID.DEEPSLATE_TILE_WALL);
+ public static final BlockType DEEPSLATE_BRICKS = register("minecraft:deepslate_bricks", BlockID.DEEPSLATE_BRICKS);
+ public static final BlockType DEEPSLATE_BRICK_SLAB = register("minecraft:deepslate_brick_slab", BlockID.DEEPSLATE_BRICK_SLAB);
+ public static final BlockType DEEPSLATE_BRICK_STAIRS = register("minecraft:deepslate_brick_stairs", BlockID.DEEPSLATE_BRICK_STAIRS);
+ public static final BlockType DEEPSLATE_BRICK_WALL = register("minecraft:deepslate_brick_wall", BlockID.DEEPSLATE_BRICK_WALL);
+ public static final BlockType CHISELED_DEEPSLATE = register("minecraft:chiseled_deepslate", BlockID.CHISELED_DEEPSLATE);
+ public static final BlockType COBBLED_DEEPSLATE_DOUBLE_SLAB = register("minecraft:cobbled_deepslate_double_slab", BlockID.COBBLED_DEEPSLATE_DOUBLE_SLAB);
+ public static final BlockType POLISHED_DEEPSLATE_DOUBLE_SLAB = register("minecraft:polished_deepslate_double_slab", BlockID.POLISHED_DEEPSLATE_DOUBLE_SLAB);
+ public static final BlockType DEEPSLATE_TILE_DOUBLE_SLAB = register("minecraft:deepslate_tile_double_slab", BlockID.DEEPSLATE_TILE_DOUBLE_SLAB);
+ public static final BlockType DEEPSLATE_BRICK_DOUBLE_SLAB = register("minecraft:deepslate_brick_double_slab", BlockID.DEEPSLATE_BRICK_DOUBLE_SLAB);
+ public static final BlockType DEEPSLATE_LAPIS_ORE = register("minecraft:deepslate_lapis_ore", BlockID.DEEPSLATE_LAPIS_ORE);
+ public static final BlockType DEEPSLATE_IRON_ORE = register("minecraft:deepslate_iron_ore", BlockID.DEEPSLATE_IRON_ORE);
+ public static final BlockType DEEPSLATE_GOLD_ORE = register("minecraft:deepslate_gold_ore", BlockID.DEEPSLATE_GOLD_ORE);
+ public static final BlockType DEEPSLATE_REDSTONE_ORE = register("minecraft:deepslate_redstone_ore", BlockID.DEEPSLATE_REDSTONE_ORE);
+ public static final BlockType LIT_DEEPSLATE_REDSTONE_ORE = register("minecraft:lit_deepslate_redstone_ore", BlockID.LIT_DEEPSLATE_REDSTONE_ORE);
+ public static final BlockType DEEPSLATE_DIAMOND_ORE = register("minecraft:deepslate_diamond_ore", BlockID.DEEPSLATE_DIAMOND_ORE);
+ public static final BlockType DEEPSLATE_COAL_ORE = register("minecraft:deepslate_coal_ore", BlockID.DEEPSLATE_COAL_ORE);
+ public static final BlockType DEEPSLATE_EMERALD_ORE = register("minecraft:deepslate_emerald_ore", BlockID.DEEPSLATE_EMERALD_ORE);
+ public static final BlockType DEEPSLATE_COPPER_ORE = register("minecraft:deepslate_copper_ore", BlockID.DEEPSLATE_COPPER_ORE);
+ public static final BlockType CRACKED_DEEPSLATE_TILES = register("minecraft:cracked_deepslate_tiles", BlockID.CRACKED_DEEPSLATE_TILES);
+ public static final BlockType CRACKED_DEEPSLATE_BRICKS = register("minecraft:cracked_deepslate_bricks", BlockID.CRACKED_DEEPSLATE_BRICKS);
+ public static final BlockType GLOW_LICHEN = register("minecraft:glow_lichen", BlockID.GLOW_LICHEN);
+ public static final BlockType CANDLE = register("minecraft:candle", BlockID.CANDLE);
+ public static final BlockType WHITE_CANDLE = register("minecraft:white_candle", BlockID.WHITE_CANDLE);
+ public static final BlockType ORANGE_CANDLE = register("minecraft:orange_candle", BlockID.ORANGE_CANDLE);
+ public static final BlockType MAGENTA_CANDLE = register("minecraft:magenta_candle", BlockID.MAGENTA_CANDLE);
+ public static final BlockType LIGHT_BLUE_CANDLE = register("minecraft:light_blue_candle", BlockID.LIGHT_BLUE_CANDLE);
+ public static final BlockType YELLOW_CANDLE = register("minecraft:yellow_candle", BlockID.YELLOW_CANDLE);
+ public static final BlockType LIME_CANDLE = register("minecraft:lime_candle", BlockID.LIME_CANDLE);
+ public static final BlockType PINK_CANDLE = register("minecraft:pink_candle", BlockID.PINK_CANDLE);
+ public static final BlockType GRAY_CANDLE = register("minecraft:gray_candle", BlockID.GRAY_CANDLE);
+ public static final BlockType LIGHT_GRAY_CANDLE = register("minecraft:light_gray_candle", BlockID.LIGHT_GRAY_CANDLE);
+ public static final BlockType CYAN_CANDLE = register("minecraft:cyan_candle", BlockID.CYAN_CANDLE);
+ public static final BlockType PURPLE_CANDLE = register("minecraft:purple_candle", BlockID.PURPLE_CANDLE);
+ public static final BlockType BLUE_CANDLE = register("minecraft:blue_candle", BlockID.BLUE_CANDLE);
+ public static final BlockType BROWN_CANDLE = register("minecraft:brown_candle", BlockID.BROWN_CANDLE);
+ public static final BlockType GREEN_CANDLE = register("minecraft:green_candle", BlockID.GREEN_CANDLE);
+ public static final BlockType RED_CANDLE = register("minecraft:red_candle", BlockID.RED_CANDLE);
+ public static final BlockType BLACK_CANDLE = register("minecraft:black_candle", BlockID.BLACK_CANDLE);
+ public static final BlockType WAXED_OXIDIZED_COPPER = register("minecraft:waxed_oxidized_copper", BlockID.WAXED_OXIDIZED_COPPER);
+ public static final BlockType WAXED_OXIDIZED_CUT_COPPER = register("minecraft:waxed_oxidized_cut_copper", BlockID.WAXED_OXIDIZED_CUT_COPPER);
+ public static final BlockType WAXED_OXIDIZED_CUT_COPPER_STAIRS = register("minecraft:waxed_oxidized_cut_copper_stairs", BlockID.WAXED_OXIDIZED_CUT_COPPER_STAIRS);
+ public static final BlockType WAXED_OXIDIZED_CUT_COPPER_SLAB = register("minecraft:waxed_oxidized_cut_copper_slab", BlockID.WAXED_OXIDIZED_CUT_COPPER_SLAB);
+ public static final BlockType WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB = register("minecraft:waxed_oxidized_double_cut_copper_slab", BlockID.WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB);
+ public static final BlockType RAW_IRON_BLOCK = register("minecraft:raw_iron_block", BlockID.RAW_IRON_BLOCK);
+ public static final BlockType RAW_COPPER_BLOCK = register("minecraft:raw_copper_block", BlockID.RAW_COPPER_BLOCK);
+ public static final BlockType RAW_GOLD_BLOCK = register("minecraft:raw_gold_block", BlockID.RAW_GOLD_BLOCK);
+ public static final BlockType INFESTED_DEEPSLATE = register("minecraft:infested_deepslate", BlockID.INFESTED_DEEPSLATE);
+ public static final BlockType MUD = register("minecraft:mud", BlockID.MUD);
+ public static final BlockType MUD_BRICKS = register("minecraft:mud_bricks", BlockID.MUD_BRICKS);
+ public static final BlockType PACKED_MUD = register("minecraft:packed_mud", BlockID.PACKED_MUD);
+ public static final BlockType MUD_BRICK_SLAB = register("minecraft:mud_brick_slab", BlockID.MUD_BRICK_SLAB);
+ public static final BlockType MUD_BRICK_DOUBLE_SLAB = register("minecraft:mud_brick_double_slab", BlockID.MUD_BRICK_DOUBLE_SLAB);
+ public static final BlockType MUD_BRICK_STAIRS = register("minecraft:mud_brick_stairs", BlockID.MUD_BRICK_STAIRS);
+ public static final BlockType MUD_BRICK_WALL = register("minecraft:mud_brick_wall", BlockID.MUD_BRICK_WALL);
+
+
+ private static BlockType register(String identifier, int legacyId) {
+ return register(new BlockTypeImpl(identifier, legacyId));
+ }
+
+ private static BlockType register(BlockType blockType) {
+ BlockType old = types.putIfAbsent(blockType.getLegacyId(), blockType);
+ /*if (old != null) { // TODO: there are alternate names for some items
+ throw new IllegalArgumentException("Block type with id " + itemType.getLegacyId() + " already exists: " + old);
+ }*/
+ identifiers.putIfAbsent(blockType.getIdentifier(), blockType); // TODO: using identifiers.put() would be better
+ return old == null ? blockType : old;
+ }
+
+ public static BlockType getFromLegacy(int legacyId) {
+ return types.get(legacyId);
+ }
+
+ public static BlockType get(String identifier) {
+ return identifiers.get(identifier);
+ }
+
+ @Data
+ private static class BlockTypeImpl implements BlockType {
+ private final String identifier;
+ private final int legacyId;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java b/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java
index 15f33862f1d..510cefa29a1 100644
--- a/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java
+++ b/src/main/java/cn/nukkit/block/BlockUndyedShulkerBox.java
@@ -1,34 +1,15 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
package cn.nukkit.block;
-import cn.nukkit.Player;
import cn.nukkit.blockentity.BlockEntity;
-import cn.nukkit.blockentity.BlockEntityShulkerBox;
-import cn.nukkit.inventory.ShulkerBoxInventory;
-import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBlock;
-import cn.nukkit.item.ItemTool;
-import cn.nukkit.math.BlockFace;
-import cn.nukkit.nbt.NBTIO;
-import cn.nukkit.nbt.tag.CompoundTag;
-import cn.nukkit.nbt.tag.ListTag;
-import cn.nukkit.nbt.tag.Tag;
+import cn.nukkit.inventory.ContainerInventory;
+import cn.nukkit.inventory.InventoryHolder;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.DyeColor;
-import java.util.Map;
-
-/**
- *
- * @author Reece Mackie
- */
-public class BlockUndyedShulkerBox extends BlockTransparent {
+public class BlockUndyedShulkerBox extends BlockShulkerBox {
public BlockUndyedShulkerBox() {
- super();
+ super(0);
}
@Override
@@ -42,123 +23,32 @@ public String getName() {
}
@Override
- public double getHardness() {
- return 2;
- }
-
- @Override
- public double getResistance() {
- return 10;
- }
-
- @Override
- public boolean canBeActivated() {
- return true;
+ public BlockColor getColor() {
+ return BlockColor.PURPLE_BLOCK_COLOR;
}
@Override
- public int getToolType() {
- return ItemTool.TYPE_PICKAXE;
+ public DyeColor getDyeColor() {
+ return null;
}
@Override
- public Item toItem() {
- ItemBlock item = new ItemBlock(this, this.getDamage(), 1);
-
- BlockEntityShulkerBox t = (BlockEntityShulkerBox) this.getLevel().getBlockEntity(this);
-
- if (t != null) {
- ShulkerBoxInventory i = t.getRealInventory();
-
- if (!i.isEmpty()) {
- CompoundTag nbt = item.getNamedTag();
- if (nbt == null)
- nbt = new CompoundTag("");
-
- ListTag items = new ListTag<>();
-
- for (int it = 0; it < i.getSize(); it++) {
- if (i.getItem(it).getId() != Item.AIR) {
- CompoundTag d = NBTIO.putItemHelper(i.getItem(it), it);
- items.add(d);
- }
- }
-
- nbt.put("Items", items);
-
- item.setCompoundTag(nbt);
- }
-
- if (t.hasName()) {
- item.setCustomName(t.getName());
- }
- }
-
- return item;
+ public void setDamage(int meta) {
}
@Override
- public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- this.getLevel().setBlock(block, this, true);
- CompoundTag nbt = BlockEntity.getDefaultCompound(this, BlockEntity.SHULKER_BOX)
- .putByte("facing", face.getIndex());
-
- if (item.hasCustomName()) {
- nbt.putString("CustomName", item.getCustomName());
- }
-
- CompoundTag t = item.getNamedTag();
-
- // This code gets executed when the player has broken the shulker box and placed it back (©Kevims 2020)
- if (t != null && t.contains("Items")) {
- nbt.putList(t.getList("Items"));
- }
-
- // This code gets executed when the player has copied the shulker box in creative mode (©Kevims 2020)
- if (item.hasCustomBlockData()) {
- Map customData = item.getCustomBlockData().getTags();
- for (Map.Entry tag : customData.entrySet()) {
- nbt.put(tag.getKey(), tag.getValue());
- }
- }
-
- BlockEntityShulkerBox box = (BlockEntityShulkerBox) BlockEntity.createBlockEntity(BlockEntity.SHULKER_BOX, this.getLevel().getChunk(this.getFloorX() >> 4, this.getFloorZ() >> 4), nbt);
- return box != null;
- }
-
- @Override
- public boolean canHarvestWithHand() {
- return false;
+ public boolean hasComparatorInputOverride() {
+ return true;
}
@Override
- public boolean onActivate(Item item, Player player) {
- if (player != null) {
- BlockEntity t = this.getLevel().getBlockEntity(this);
- BlockEntityShulkerBox box;
- if (t instanceof BlockEntityShulkerBox) {
- box = (BlockEntityShulkerBox) t;
- } else {
- CompoundTag nbt = BlockEntity.getDefaultCompound(this, BlockEntity.SHULKER_BOX);
- box = (BlockEntityShulkerBox) BlockEntity.createBlockEntity(BlockEntity.SHULKER_BOX, this.getLevel().getChunk(this.getFloorX() >> 4, this.getFloorZ() >> 4), nbt);
- if (box == null) {
- return false;
- }
- }
-
- Block block = this.getSide(BlockFace.fromIndex(box.namedTag.getByte("facing")));
- if (!(block instanceof BlockAir) && !(block instanceof BlockLiquid) && !(block instanceof BlockFlowable)) {
- return true;
- }
+ public int getComparatorInputOverride() {
+ BlockEntity be = this.getLevel().getBlockEntity(this);
- player.addWindow(box.getInventory());
+ if (!(be instanceof InventoryHolder)) {
+ return 0;
}
- return true;
- }
-
- @Override
- public BlockColor getColor() {
- return BlockColor.PURPLE_BLOCK_COLOR;
+ return ContainerInventory.calculateRedstone(((InventoryHolder) be).getInventory());
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockUnknown.java b/src/main/java/cn/nukkit/block/BlockUnknown.java
index 4a89311d83b..514e145976b 100644
--- a/src/main/java/cn/nukkit/block/BlockUnknown.java
+++ b/src/main/java/cn/nukkit/block/BlockUnknown.java
@@ -1,7 +1,7 @@
package cn.nukkit.block;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockUnknown extends BlockMeta {
@@ -22,6 +22,11 @@ public int getId() {
return id;
}
+ @Override
+ public double getHardness() {
+ return 0.1;
+ }
+
@Override
public String getName() {
return "Unknown";
diff --git a/src/main/java/cn/nukkit/block/BlockVine.java b/src/main/java/cn/nukkit/block/BlockVine.java
index bc097954203..72f49fb7bfb 100644
--- a/src/main/java/cn/nukkit/block/BlockVine.java
+++ b/src/main/java/cn/nukkit/block/BlockVine.java
@@ -13,9 +13,7 @@
import cn.nukkit.math.SimpleAxisAlignedBB;
import cn.nukkit.utils.BlockColor;
-import java.util.EnumSet;
import java.util.Random;
-import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -74,7 +72,6 @@ public boolean canBeClimbed() {
@Override
public void onEntityCollide(Entity entity) {
entity.resetFallDistance();
- entity.onGround = true;
}
@Override
@@ -140,7 +137,7 @@ protected AxisAlignedBB recalculateBoundingBox() {
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (block.getId() != VINE && target.isSolid() && face.getHorizontalIndex() != -1) {
this.setDamage(getMetaFromFace(face.getOpposite()));
- this.getLevel().setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@@ -160,25 +157,24 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
+ int meta = this.getDamage();
Block up = this.up();
- Set upFaces = up instanceof BlockVine ? ((BlockVine) up).getFaces() : null;
- Set faces = this.getFaces();
for (BlockFace face : BlockFace.Plane.HORIZONTAL) {
- if (!this.getSide(face).isSolid() && (upFaces == null || !upFaces.contains(face))) {
- faces.remove(face);
+ int faceMeta = getMetaFromFace(face);
+ if (!this.getSide(face).isSolid() && (up.getId() != VINE || (up.getDamage() & faceMeta) != faceMeta)) {
+ meta &= ~faceMeta;
}
}
- if (faces.isEmpty() && !up.isSolid()) {
+ if (meta == 0 && !up.isSolid()) {
this.getLevel().useBreakOn(this, null, null, true);
return Level.BLOCK_UPDATE_NORMAL;
}
- int meta = getMetaFromFaces(faces);
if (meta != this.getDamage()) {
this.level.setBlock(this, Block.get(VINE, meta), true);
return Level.BLOCK_UPDATE_NORMAL;
@@ -191,7 +187,7 @@ public int onUpdate(int type) {
int faceMeta = getMetaFromFace(face);
int meta = this.getDamage();
- if (this.y < 255 && face == BlockFace.UP && block.getId() == AIR) {
+ if (this.y < this.level.getMaxBlockY() && face == BlockFace.UP && block.getId() == AIR) {
if (this.canSpread()) {
for (BlockFace horizontalFace : BlockFace.Plane.HORIZONTAL) {
if (random.nextBoolean() || !this.getSide(horizontalFace).getSide(face).isSolid()) {
@@ -228,7 +224,7 @@ public int onUpdate(int type) {
putVine(this, meta, null);
}
}
- } else if (this.y > 0) {
+ } else if (this.y > this.level.getMinBlockY()) {
Block below = this.down();
int id = below.getId();
if (id == AIR || id == VINE) {
@@ -255,7 +251,7 @@ private boolean canSpread() {
for (int x = blockX - 4; x <= blockX + 4; x++) {
for (int z = blockZ - 4; z <= blockZ + 4; z++) {
for (int y = blockY - 1; y <= blockY + 1; y++) {
- if (this.level.getBlock(x, y, z).getId() == VINE) {
+ if (this.level.getBlockIdAt(x, y, z) == VINE) {
if (++count >= 5) return false;
}
}
@@ -294,37 +290,6 @@ private void putVineOnHorizontalFace(Block block, int meta, Block source) {
}
}
- private Set getFaces() {
- Set faces = EnumSet.noneOf(BlockFace.class);
-
- int meta = this.getDamage();
- if ((meta & 1) > 0) {
- faces.add(BlockFace.SOUTH);
- }
- if ((meta & 2) > 0) {
- faces.add(BlockFace.WEST);
- }
- if ((meta & 4) > 0) {
- faces.add(BlockFace.NORTH);
- }
- if ((meta & 8) > 0) {
- faces.add(BlockFace.EAST);
- }
-
- return faces;
- }
-
- private static int getMetaFromFaces(Set faces) {
- int meta = 0;
-
- for (BlockFace face : faces) {
- meta |= getMetaFromFace(face);
-
- }
-
- return meta;
- }
-
private static int getMetaFromFace(BlockFace face) {
switch (face) {
case SOUTH:
@@ -353,4 +318,19 @@ public BlockColor getColor() {
public boolean canSilkTouch() {
return true;
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockVinesNether.java b/src/main/java/cn/nukkit/block/BlockVinesNether.java
new file mode 100644
index 00000000000..faaf54f1963
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockVinesNether.java
@@ -0,0 +1,398 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.event.block.BlockGrowEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemDye;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.item.enchantment.Enchantment;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.math.BlockFace;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Implements the main logic of all nether vines.
+ * @author joserobjr
+ */
+public abstract class BlockVinesNether extends BlockTransparentMeta {
+
+ public BlockVinesNether() {
+ this(0);
+ }
+
+ public BlockVinesNether(int meta) {
+ super(meta);
+ }
+
+ /**
+ * The direction that the vine will grow, vertical direction is expected but future implementations
+ * may also add horizontal directions.
+ * @return Normally, up or down.
+ */
+ public abstract BlockFace getGrowthDirection();
+
+ /**
+ * The current age of this block.
+ */
+ public abstract int getVineAge();
+
+ /**
+ * Changes the age of this block.
+ * @param vineAge The new age
+ */
+ public abstract void setVineAge(int vineAge);
+
+ /**
+ * The maximum accepted age of this block.
+ * @return Positive, inclusive value.
+ */
+ public abstract int getMaxVineAge();
+
+ /**
+ * Changes the current vine age to a random new random age.
+ *
+ * @param pseudorandom If the the randomization should be pseudorandom.
+ */
+ public void randomizeVineAge(boolean pseudorandom) {
+ if (pseudorandom) {
+ setVineAge(ThreadLocalRandom.current().nextInt(getMaxVineAge()));
+ return;
+ }
+
+ double chance = 1.0D;
+ int age;
+
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ for(age = 0; random.nextDouble() < chance; ++age) {
+ chance *= 0.826D;
+ }
+
+ setVineAge(age);
+ }
+
+ @Override
+ public boolean place(@Nonnull Item item, @Nonnull Block block, @Nonnull Block target, @Nonnull BlockFace face, double fx, double fy, double fz, @Nullable Player player) {
+ Block support = getSide(getGrowthDirection().getOpposite());
+ if (!isSupportValid(support)) {
+ return false;
+ }
+
+ if (support.getId() == getId()) {
+ setVineAge(Math.min(getMaxVineAge(), ((BlockVinesNether) support).getVineAge() + 1));
+ } else {
+ randomizeVineAge(true);
+ }
+
+ return super.place(item, block, target, face, fx, fy, fz, player);
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ switch (type) {
+ case Level.BLOCK_UPDATE_RANDOM:
+ int maxVineAge = getMaxVineAge();
+ if (getVineAge() < maxVineAge && ThreadLocalRandom.current().nextInt(10) == 0
+ && findVineAge(true).orElse(maxVineAge) < maxVineAge) {
+ grow();
+ }
+ return Level.BLOCK_UPDATE_RANDOM;
+ case Level.BLOCK_UPDATE_NORMAL:
+ if (!this.isSupportValid()) {
+ this.getLevel().useBreakOn(this);
+ }
+ return Level.BLOCK_UPDATE_NORMAL;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Grow a single vine if possible. Calls {@link BlockGrowEvent} passing the positioned new state and the source block.
+ * @return If the vine grew successfully.
+ */
+ public boolean grow() {
+ Block pos = getSide(getGrowthDirection());
+ if (pos.getId() != AIR || pos.y < 0 || 255 < pos.y) {
+ return false;
+ }
+
+ BlockVinesNether growing = clone();
+ growing.x = pos.x;
+ growing.y = pos.y;
+ growing.z = pos.z;
+ growing.setVineAge(Math.min(getVineAge() + 1, getMaxVineAge()));
+
+ BlockGrowEvent ev = new BlockGrowEvent(this, growing);
+ Server.getInstance().getPluginManager().callEvent(ev);
+
+ if (ev.isCancelled()) {
+ return false;
+ }
+
+ if (level.setBlock(pos, growing)) {
+ increaseRootAge();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Grow a random amount of vines.
+ * Calls {@link BlockGrowEvent} passing the positioned new state and the source block for each new vine being added
+ * to the world, if one of the events gets cancelled the growth gets interrupted.
+ * @return How many vines grew
+ */
+ public int growMultiple() {
+ BlockFace growthDirection = getGrowthDirection();
+ int age = getVineAge() + 1;
+ int maxAge = getMaxVineAge();
+ BlockVinesNether growing = clone();
+ growing.randomizeVineAge(false);
+ int blocksToGrow = growing.getVineAge();
+
+ int grew = 0;
+ for (int distance = 1; distance <= blocksToGrow; distance++) {
+ Block pos = getSide(growthDirection, distance);
+ if (pos.getId() != AIR || pos.y < 0 || 255 < pos.y) {
+ break;
+ }
+
+ growing.setVineAge(Math.min(age++, maxAge));
+ growing.x = pos.x;
+ growing.y = pos.y;
+ growing.z = pos.z;
+
+ BlockGrowEvent ev = new BlockGrowEvent(this, growing.clone());
+ Server.getInstance().getPluginManager().callEvent(ev);
+
+ if (ev.isCancelled()) {
+ break;
+ }
+
+ if (!level.setBlock(pos, ev.getNewState())) {
+ break;
+ }
+
+ grew++;
+ }
+
+ if (grew > 0) {
+ increaseRootAge();
+ }
+
+ return grew;
+ }
+
+ /**
+ * Attempt to get the age of the root or the head of the vine.
+ * @param base True to get the age of the base (oldest block), false to get the age of the head (newest block)
+ * @return Empty if the target could not be reached. The age of the target if it was found.
+ */
+ @Nonnull
+ public OptionalInt findVineAge(boolean base) {
+ return findVineBlock(base)
+ .map(vine-> OptionalInt.of(vine.getVineAge()))
+ .orElse(OptionalInt.empty());
+ }
+
+ /**
+ * Attempt to find the root or the head of the vine transversing the growth direction for up to 256 blocks.
+ * @param base True to find the base (oldest block), false to find the head (newest block)
+ * @return Empty if the target could not be reached or the block there isn't an instance of {@link BlockVinesNether}.
+ * The positioned block of the target if it was found.
+ */
+ @Nonnull
+ public Optional findVineBlock(boolean base) {
+ return findVine(base)
+ .map(Position::getLevelBlock)
+ .filter(BlockVinesNether.class::isInstance)
+ .map(BlockVinesNether.class::cast);
+ }
+
+ /**
+ * Attempt to find the root or the head of the vine transversing the growth direction for up to 256 blocks.
+ * @param base True to find the base (oldest block), false to find the head (newest block)
+ * @return Empty if the target could not be reached. The position of the target if it was found.
+ */
+ @Nonnull
+ public Optional findVine(boolean base) {
+ BlockFace supportFace = getGrowthDirection();
+ if (base) {
+ supportFace = supportFace.getOpposite();
+ }
+ Position result = getLocation();
+ int id = getId();
+ int limit = 256;
+ while (--limit > 0) {
+ Position next = result.getSide(supportFace);
+ if (next.getLevelBlock().getId() == id) {
+ result = next;
+ } else {
+ break;
+ }
+ }
+
+ return Optional.of(result);
+ }
+
+ public Optional increaseRootAge() {
+ Block base = findVine(true).map(Position::getLevelBlock).orElse(null);
+ if (!(base instanceof BlockVinesNether)) {
+ return Optional.empty();
+ }
+
+ BlockVinesNether baseVine = (BlockVinesNether) base;
+ int vineAge = baseVine.getVineAge();
+ if (vineAge < baseVine.getMaxVineAge()) {
+ baseVine.setVineAge(vineAge + 1);
+ if (getLevel().setBlock(baseVine, baseVine)) {
+ return Optional.of(true);
+ }
+ }
+
+ return Optional.of(false);
+ }
+
+ @Override
+ public boolean onActivate(@Nonnull Item item, @Nullable Player player) {
+ if (!(item.getId() == ItemID.DYE && item.getDamage() == ItemDye.BONE_MEAL)) {
+ return false;
+ }
+
+ this.getLevel().addParticle(new BoneMealParticle(this));
+ this.findVineBlock(false).ifPresent(BlockVinesNether::growMultiple);
+
+ if (player != null && !player.isCreative()) {
+ item.count--;
+ }
+ return true;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ // They have a 33% (3/9) chance to drop a single weeping vine when broken,
+ // increased to 55% (5/9) with Fortune I,
+ // 77% (7/9) with Fortune II,
+ // and 100% with Fortune III.
+ //
+ // They always drop a single weeping vine when broken with shears or a tool enchanted with Silk Touch.
+
+ int enchantmentLevel;
+ if (item.isShears() || (enchantmentLevel = item.getEnchantmentLevel(Enchantment.ID_FORTUNE_DIGGING)) >= 3) {
+ return new Item[]{ toItem() };
+ }
+
+ int chance = 3 + enchantmentLevel * 2;
+ if (ThreadLocalRandom.current().nextInt(9) < chance) {
+ return new Item[]{ toItem() };
+ }
+
+ return new Item[0];
+ }
+
+ protected boolean isSupportValid(@Nonnull Block support) {
+ return support.getId() == getId() || !support.isTransparent();
+ }
+
+ public boolean isSupportValid() {
+ return isSupportValid(getSide(getGrowthDirection().getOpposite()));
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ entity.resetFallDistance();
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0;
+ }
+
+ @Override
+ public boolean canBeClimbed() {
+ return true;
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return true;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ public double getMinX() {
+ return x+ (4/16.0);
+ }
+
+ @Override
+ public double getMinZ() {
+ return z+ (4/16.0);
+ }
+
+ @Override
+ public double getMaxX() {
+ return x+ (12/16.0);
+ }
+
+ @Override
+ public double getMaxZ() {
+ return z+ (12/16.0);
+ }
+
+ @Override
+ public double getMaxY() {
+ return y+ (15/16.0);
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean canSilkTouch() {
+ return true;
+ }
+ @Override
+ public BlockVinesNether clone() {
+ return (BlockVinesNether) super.clone();
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockVinesTwisting.java b/src/main/java/cn/nukkit/block/BlockVinesTwisting.java
new file mode 100644
index 00000000000..2404d0f542d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockVinesTwisting.java
@@ -0,0 +1,50 @@
+package cn.nukkit.block;
+
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockVinesTwisting extends BlockVinesNether {
+
+ public BlockVinesTwisting() {
+ this(0);
+ }
+
+ public BlockVinesTwisting(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Twisting Vines";
+ }
+
+ @Override
+ public int getId() {
+ return TWISTING_VINES;
+ }
+
+ @Override
+ public BlockFace getGrowthDirection() {
+ return BlockFace.UP;
+ }
+
+ @Override
+ public int getVineAge() {
+ return this.getDamage();
+ }
+
+ @Override
+ public void setVineAge(int vineAge) {
+ this.setDamage(vineAge & 0x19);
+ }
+
+ @Override
+ public int getMaxVineAge() {
+ return 25;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CYAN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWall.java b/src/main/java/cn/nukkit/block/BlockWall.java
index 64414a248a4..802c57e6cc9 100644
--- a/src/main/java/cn/nukkit/block/BlockWall.java
+++ b/src/main/java/cn/nukkit/block/BlockWall.java
@@ -6,14 +6,14 @@
import cn.nukkit.math.SimpleAxisAlignedBB;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockWall extends BlockTransparentMeta {
+
public static final int NONE_MOSSY_WALL = 0;
public static final int MOSSY_WALL = 1;
-
public BlockWall() {
this(0);
}
@@ -53,7 +53,6 @@ public String getName() {
@Override
protected AxisAlignedBB recalculateBoundingBox() {
-
boolean north = this.canConnect(this.getSide(BlockFace.NORTH));
boolean south = this.canConnect(this.getSide(BlockFace.SOUTH));
boolean west = this.canConnect(this.getSide(BlockFace.WEST));
@@ -95,4 +94,9 @@ public int getToolType() {
public boolean canHarvestWithHand() {
return false;
}
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockWallBanner.java b/src/main/java/cn/nukkit/block/BlockWallBanner.java
index a9f5f8a35db..4efe0ff6a6e 100644
--- a/src/main/java/cn/nukkit/block/BlockWallBanner.java
+++ b/src/main/java/cn/nukkit/block/BlockWallBanner.java
@@ -3,9 +3,6 @@
import cn.nukkit.level.Level;
import cn.nukkit.math.BlockFace;
-/**
- * Created by PetteriM1
- */
public class BlockWallBanner extends BlockBanner {
public BlockWallBanner() {
diff --git a/src/main/java/cn/nukkit/block/BlockWallBrickDeepslate.java b/src/main/java/cn/nukkit/block/BlockWallBrickDeepslate.java
new file mode 100644
index 00000000000..394580d90b8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWallBrickDeepslate.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockWallBrickDeepslate extends BlockWall {
+
+ public BlockWallBrickDeepslate() {
+ this(0);
+ }
+
+ public BlockWallBrickDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Brick Wall";
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_BRICK_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWallDeepslateCobbled.java b/src/main/java/cn/nukkit/block/BlockWallDeepslateCobbled.java
new file mode 100644
index 00000000000..99f04665d12
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWallDeepslateCobbled.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockWallDeepslateCobbled extends BlockWall {
+
+ public BlockWallDeepslateCobbled() {
+ this(0);
+ }
+
+ public BlockWallDeepslateCobbled(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Cobbled Deepslate Wall";
+ }
+
+ @Override
+ public int getId() {
+ return COBBLED_DEEPSLATE_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWallDeepslatePolished.java b/src/main/java/cn/nukkit/block/BlockWallDeepslatePolished.java
new file mode 100644
index 00000000000..3d2a245416d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWallDeepslatePolished.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockWallDeepslatePolished extends BlockWall {
+
+ public BlockWallDeepslatePolished() {
+ this(0);
+ }
+
+ public BlockWallDeepslatePolished(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Polished Deepslate Wall";
+ }
+
+ @Override
+ public int getId() {
+ return POLISHED_DEEPSLATE_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWallSign.java b/src/main/java/cn/nukkit/block/BlockWallSign.java
index d7cb421ea03..695352b0b60 100644
--- a/src/main/java/cn/nukkit/block/BlockWallSign.java
+++ b/src/main/java/cn/nukkit/block/BlockWallSign.java
@@ -9,6 +9,13 @@
*/
public class BlockWallSign extends BlockSignPost {
+ private static final int[] faces = {
+ 3,
+ 2,
+ 5,
+ 4,
+ };
+
public BlockWallSign() {
this(0);
}
@@ -29,12 +36,6 @@ public String getName() {
@Override
public int onUpdate(int type) {
- int[] faces = {
- 3,
- 2,
- 5,
- 4,
- };
if (type == Level.BLOCK_UPDATE_NORMAL) {
if (this.getDamage() >= 2 && this.getDamage() <= 5) {
if (this.getSide(BlockFace.fromIndex(faces[this.getDamage() - 2])).getId() == Item.AIR) {
diff --git a/src/main/java/cn/nukkit/block/BlockWallTileDeepslate.java b/src/main/java/cn/nukkit/block/BlockWallTileDeepslate.java
new file mode 100644
index 00000000000..74d84b78bb6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWallTileDeepslate.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockWallTileDeepslate extends BlockWall {
+
+ public BlockWallTileDeepslate() {
+ this(0);
+ }
+
+ public BlockWallTileDeepslate(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Deepslate Tile Wall";
+ }
+
+ @Override
+ public int getId() {
+ return DEEPSLATE_TILE_WALL;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedDoor.java b/src/main/java/cn/nukkit/block/BlockWarpedDoor.java
new file mode 100644
index 00000000000..a761896fcc1
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedDoor.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockWarpedDoor extends BlockDoor {
+
+ public BlockWarpedDoor() {
+ this(0);
+ }
+
+ public BlockWarpedDoor(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Door";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_DOOR_BLOCK;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.WARPED_DOOR);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedFungus.java b/src/main/java/cn/nukkit/block/BlockWarpedFungus.java
new file mode 100644
index 00000000000..ed95a37d7c9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedFungus.java
@@ -0,0 +1,99 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.generator.object.tree.ObjectWarpedTree;
+import cn.nukkit.level.particle.BoneMealParticle;
+import cn.nukkit.level.Position;
+import cn.nukkit.utils.BlockColor;
+import cn.nukkit.math.NukkitRandom;
+import cn.nukkit.math.Vector3;
+import cn.nukkit.utils.DyeColor;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockWarpedFungus extends BlockFungus {
+
+ public BlockWarpedFungus() {
+ // Does nothing
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_FUNGUS;
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Fungus";
+ }
+
+ @Override
+ protected boolean canGrowOn(Block support) {
+ return support.getId() == WARPED_NYLIUM;
+ }
+
+ @Override
+ public boolean grow(Player cause) {
+ // TODO:
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CYAN_BLOCK_COLOR;
+ }
+
+ @Override
+ public int onUpdate(int type) {
+ if (type == Level.BLOCK_UPDATE_NORMAL) {
+ if (this.down().isTransparent()) {
+ this.getLevel().useBreakOn(this);
+ return Level.BLOCK_UPDATE_NORMAL;
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ public boolean canPlaceOn(Block floor, Position pos) {
+ switch (floor.getId()) {
+ case BlockID.GRASS:
+ case BlockID.DIRT:
+ case BlockID.PODZOL:
+ case BlockID.FARMLAND:
+ case BlockID.CRIMSON_NYLIUM:
+ case BlockID.WARPED_NYLIUM:
+ case BlockID.MYCELIUM:
+ case BlockID.SOUL_SOIL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.getId() == Item.DYE && item.getDamage() == DyeColor.WHITE.getDyeData()) {
+ if (player != null && (player.gamemode & 0x01) == 0) {
+ item.count--;
+ }
+
+ if (ThreadLocalRandom.current().nextFloat() < 0.4 && this.level.getBlockIdAt((int) this.x, (int) this.y - 1, (int) this.z) == WARPED_NYLIUM) {
+ new ObjectWarpedTree().placeObject(this.level, (int) this.x, (int) this.y, (int) this.z, new NukkitRandom());
+ this.level.setBlock(new Vector3((int) this.x, (int) this.y - 1, (int) this.z), Block.get(NETHERRACK), false, true);
+ }
+
+ this.level.addParticle(new BoneMealParticle(this));
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedNylium.java b/src/main/java/cn/nukkit/block/BlockWarpedNylium.java
new file mode 100644
index 00000000000..8f087440bc5
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedNylium.java
@@ -0,0 +1,21 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWarpedNylium extends BlockNylium {
+
+ @Override
+ public String getName() {
+ return "Warped Nylium";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_NYLIUM;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_NYLIUM_BLOCK_COLOR;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedPlanks.java b/src/main/java/cn/nukkit/block/BlockWarpedPlanks.java
new file mode 100644
index 00000000000..be1be0f9d77
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedPlanks.java
@@ -0,0 +1,55 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWarpedPlanks extends BlockSolid {
+
+ public BlockWarpedPlanks() {
+ this(0);
+ }
+
+ public BlockWarpedPlanks(int meta) {
+ // super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_PLANKS;
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Planks";
+ }
+
+ @Override
+ public double getHardness() {
+ return 2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedRoots.java b/src/main/java/cn/nukkit/block/BlockWarpedRoots.java
new file mode 100644
index 00000000000..6b223c1921d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedRoots.java
@@ -0,0 +1,39 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWarpedRoots extends BlockRoots {
+
+ public BlockWarpedRoots() {
+ this(0);
+ }
+
+ public BlockWarpedRoots(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_ROOTS;
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Roots";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public boolean canBeReplaced() {
+ return true;
+ }
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedSign.java b/src/main/java/cn/nukkit/block/BlockWarpedSign.java
new file mode 100644
index 00000000000..84b5c9d4934
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedSign.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+
+public class BlockWarpedSign extends BlockSignPost {
+
+ public BlockWarpedSign() {
+ this(0);
+ }
+
+ public BlockWarpedSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Sign";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_STANDING_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(ItemID.WARPED_SIGN);
+ }
+
+ @Override
+ protected int getPostId() {
+ return WARPED_STANDING_SIGN;
+ }
+
+ @Override
+ protected int getWallId() {
+ return WARPED_WALL_SIGN;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedStairs.java b/src/main/java/cn/nukkit/block/BlockWarpedStairs.java
new file mode 100644
index 00000000000..50a3b0551b6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedStairs.java
@@ -0,0 +1,39 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWarpedStairs extends BlockStairsWood {
+
+ public BlockWarpedStairs() {
+ this(0);
+ }
+
+ public BlockWarpedStairs(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_STAIRS;
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Wood Stairs";
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.CYAN_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedStem.java b/src/main/java/cn/nukkit/block/BlockWarpedStem.java
new file mode 100644
index 00000000000..a94b86608ff
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedStem.java
@@ -0,0 +1,44 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWarpedStem extends BlockStem {
+
+ public BlockWarpedStem() {
+ this(0);
+ }
+
+ public BlockWarpedStem(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Stem";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_STEM;
+ }
+
+ @Override
+ public int getStrippedId() {
+ return STRIPPED_WARPED_STEM;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_STEM_BLOCK_COLOR;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 0;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 0;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedTrapdoor.java b/src/main/java/cn/nukkit/block/BlockWarpedTrapdoor.java
new file mode 100644
index 00000000000..e12c5fb9fa2
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedTrapdoor.java
@@ -0,0 +1,30 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockWarpedTrapdoor extends BlockTrapdoor {
+
+ public BlockWarpedTrapdoor() {
+ this(0);
+ }
+
+ public BlockWarpedTrapdoor(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Trapdoor";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_TRAPDOOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedWallSign.java b/src/main/java/cn/nukkit/block/BlockWarpedWallSign.java
new file mode 100644
index 00000000000..8619a4fe6e3
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedWallSign.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+
+public class BlockWarpedWallSign extends BlockWallSign {
+
+ public BlockWarpedWallSign() {
+ this(0);
+ }
+
+ public BlockWarpedWallSign(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Wall Sign";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_WALL_SIGN;
+ }
+
+ @Override
+ public Item toItem() {
+ return Item.get(Item.WARPED_SIGN);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWarpedWartBlock.java b/src/main/java/cn/nukkit/block/BlockWarpedWartBlock.java
new file mode 100644
index 00000000000..13a7c7f0c67
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWarpedWartBlock.java
@@ -0,0 +1,35 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWarpedWartBlock extends BlockNetherWartBlock {
+
+ public BlockWarpedWartBlock() {
+ super();
+ }
+
+ @Override
+ public String getName() {
+ return "Warped Wart Block";
+ }
+
+ @Override
+ public int getId() {
+ return WARPED_WART_BLOCK;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WARPED_WART_BLOCK_COLOR;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/BlockWater.java b/src/main/java/cn/nukkit/block/BlockWater.java
index c2a4e1fcc23..ae35555ee39 100644
--- a/src/main/java/cn/nukkit/block/BlockWater.java
+++ b/src/main/java/cn/nukkit/block/BlockWater.java
@@ -2,16 +2,28 @@
import cn.nukkit.Player;
import cn.nukkit.entity.Entity;
+import cn.nukkit.event.block.WaterFrostEvent;
import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.biome.Biome;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.level.format.anvil.Anvil;
import cn.nukkit.math.BlockFace;
import cn.nukkit.utils.BlockColor;
+import java.util.concurrent.ThreadLocalRandom;
+
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockWater extends BlockLiquid {
+ /**
+ * Used to cache biome check for freezing
+ * 1 = can't freeze, 2 = can freeze
+ */
+ private byte freezing;
public BlockWater() {
this(0);
@@ -46,7 +58,7 @@ public BlockColor getColor() {
@Override
public BlockLiquid getBlock(int meta) {
- return (BlockLiquid) Block.get(BlockID.WATER, meta);
+ return (BlockLiquid) Block.get(WATER, meta);
}
@Override
@@ -62,4 +74,30 @@ public void onEntityCollide(Entity entity) {
public int tickRate() {
return 5;
}
+
+ @Override
+ public int onUpdate(int type) {
+ if (freezing != 1 && type == Level.BLOCK_UPDATE_RANDOM && this.getDamage() == 0) {
+ FullChunk chunk = getChunk();
+ if (freezing < 1) {
+ freezing = Biome.getBiome(chunk.getBiomeId((int) this.x & 0x0f, (int) this.z & 0x0f)).isFreezing() ? (byte) 2 : (byte) 1;
+ }
+ if (freezing == 2) {
+ if (ThreadLocalRandom.current().nextInt(10) == 0 && chunk.getBlockLight((int) this.x & 0x0f, (int) this.y, (int) this.z & 0x0f) < 12 && chunk.getHighestBlockAt((int) this.x & 0x0f, (int) this.z & 0x0f, false) <= this.y) {
+ WaterFrostEvent ev = new WaterFrostEvent(this);
+ level.getServer().getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ level.setBlock(this, Block.get(Block.ICE), true, true);
+ }
+ }
+ }
+ return Level.BLOCK_UPDATE_RANDOM;
+ }
+ return super.onUpdate(type);
+ }
+
+ @Override
+ public boolean usesWaterLogging() {
+ return level == null || !(level.getProvider() instanceof Anvil);
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockWaterLily.java b/src/main/java/cn/nukkit/block/BlockWaterLily.java
index fb4095683fc..7a7e1ad855f 100644
--- a/src/main/java/cn/nukkit/block/BlockWaterLily.java
+++ b/src/main/java/cn/nukkit/block/BlockWaterLily.java
@@ -2,7 +2,6 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.Level;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
@@ -19,7 +18,6 @@ public BlockWaterLily() {
}
public BlockWaterLily(int meta) {
- // Lily pad can't have meta. Also stops the server from throwing an exception with the block palette.
super(0);
}
@@ -33,6 +31,11 @@ public int getId() {
return WATER_LILY;
}
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return this;
+ }
+
@Override
public double getMinX() {
return this.x + 0.0625;
@@ -58,11 +61,6 @@ public double getMaxZ() {
return this.z + 0.9375;
}
- @Override
- protected AxisAlignedBB recalculateBoundingBox() {
- return this;
- }
-
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
if (target instanceof BlockWater) {
@@ -86,11 +84,6 @@ public int onUpdate(int type) {
return 0;
}
- @Override
- public Item toItem() {
- return new ItemBlock(this, 0);
- }
-
@Override
public BlockColor getColor() {
return BlockColor.FOLIAGE_BLOCK_COLOR;
@@ -102,12 +95,7 @@ public boolean canPassThrough() {
}
@Override
- public int getFullId() {
- return this.getId() << 4;
- }
-
- @Override
- public void setDamage(int meta) {
-
+ public boolean breakWhenPushed() {
+ return true;
}
}
diff --git a/src/main/java/cn/nukkit/block/BlockWaterStill.java b/src/main/java/cn/nukkit/block/BlockWaterStill.java
index ba28df1d508..40822a32fe6 100644
--- a/src/main/java/cn/nukkit/block/BlockWaterStill.java
+++ b/src/main/java/cn/nukkit/block/BlockWaterStill.java
@@ -1,7 +1,7 @@
package cn.nukkit.block;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class BlockWaterStill extends BlockWater {
@@ -26,7 +26,6 @@ public String getName() {
@Override
public BlockLiquid getBlock(int meta) {
- return (BlockLiquid) Block.get(BlockID.STILL_WATER, meta);
+ return (BlockLiquid) Block.get(STILL_WATER, meta);
}
-
}
diff --git a/src/main/java/cn/nukkit/block/BlockWeepingVines.java b/src/main/java/cn/nukkit/block/BlockWeepingVines.java
new file mode 100644
index 00000000000..9735f1858eb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWeepingVines.java
@@ -0,0 +1,49 @@
+package cn.nukkit.block;
+
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWeepingVines extends BlockVinesNether {
+
+ public BlockWeepingVines() {
+ this(0);
+ }
+
+ public BlockWeepingVines(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Weeping Vines";
+ }
+
+ @Override
+ public int getId() {
+ return WEEPING_VINES;
+ }
+
+ @Override
+ public BlockFace getGrowthDirection() {
+ return BlockFace.DOWN;
+ }
+
+ @Override
+ public int getVineAge() {
+ return this.getDamage();
+ }
+
+ @Override
+ public void setVineAge(int vineAge) {
+ this.setDamage(vineAge & 0x19);
+ }
+
+ public int getMaxVineAge() {
+ return 25;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.NETHERRACK_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateHeavy.java b/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateHeavy.java
index c71f007a618..af93d51bafd 100644
--- a/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateHeavy.java
+++ b/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateHeavy.java
@@ -48,7 +48,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -59,7 +59,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateLight.java b/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateLight.java
index e718b675c81..2c397f3bff7 100644
--- a/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateLight.java
+++ b/src/main/java/cn/nukkit/block/BlockWeightedPressurePlateLight.java
@@ -48,7 +48,7 @@ public int getToolType() {
@Override
public Item[] getDrops(Item item) {
- if (item.isPickaxe() && item.getTier() >= ItemTool.TIER_WOODEN) {
+ if (item.isPickaxe()) {
return new Item[]{
toItem()
};
@@ -59,7 +59,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockWheat.java b/src/main/java/cn/nukkit/block/BlockWheat.java
index c20f9007ae1..d4cf81c5fc9 100644
--- a/src/main/java/cn/nukkit/block/BlockWheat.java
+++ b/src/main/java/cn/nukkit/block/BlockWheat.java
@@ -1,8 +1,7 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemSeedsWheat;
-import cn.nukkit.item.ItemWheat;
+import cn.nukkit.utils.Utils;
/**
* Created on 2015/12/2 by xtypr.
@@ -30,20 +29,25 @@ public int getId() {
@Override
public Item toItem() {
- return new ItemSeedsWheat();
+ return Item.get(Item.WHEAT_SEEDS);
}
@Override
public Item[] getDrops(Item item) {
if (this.getDamage() >= 0x07) {
return new Item[]{
- new ItemWheat(),
- new ItemSeedsWheat(0, (int) (4d * Math.random()))
+ Item.get(Item.WHEAT),
+ Item.get(Item.WHEAT_SEEDS, 0, Utils.random.nextInt(0, 4))
};
} else {
return new Item[]{
- new ItemSeedsWheat()
+ Item.get(Item.WHEAT_SEEDS)
};
}
}
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockWitherRose.java b/src/main/java/cn/nukkit/block/BlockWitherRose.java
new file mode 100644
index 00000000000..d6117960841
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWitherRose.java
@@ -0,0 +1,75 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.EntityLiving;
+import cn.nukkit.math.AxisAlignedBB;
+import cn.nukkit.potion.Effect;
+
+public class BlockWitherRose extends BlockFlower {
+
+ public BlockWitherRose() {
+ this(0);
+ }
+
+ public BlockWitherRose(int meta) {
+ super(0);
+ }
+
+ @Override
+ public int getId() {
+ return WITHER_ROSE;
+ }
+ @Override
+ public boolean canBeActivated() {
+ return false;
+ }
+
+ @Override
+ public void onEntityCollide(Entity entity) {
+ if (level.getServer().getDifficulty() != 0 && entity instanceof EntityLiving) {
+ EntityLiving living = (EntityLiving) entity;
+ if (!living.invulnerable && !living.hasEffect(Effect.WITHER)
+ && (!(living instanceof Player) || !((Player) living).isCreative() && !((Player) living).isSpectator())) {
+ Effect effect = Effect.getEffect(Effect.WITHER);
+ effect.setDuration(40);
+ living.addEffect(effect);
+ }
+ }
+ }
+
+ @Override
+ public boolean hasEntityCollision() {
+ return true;
+ }
+
+ @Override
+ protected AxisAlignedBB recalculateBoundingBox() {
+ return this;
+ }
+
+ @Override
+ public double getMinX() {
+ return this.x + 0.2;
+ }
+
+ @Override
+ public double getMinZ() {
+ return this.z + 0.2;
+ }
+
+ @Override
+ public double getMaxX() {
+ return this.x + 0.8;
+ }
+
+ @Override
+ public double getMaxY() {
+ return this.y + 0.8;
+ }
+
+ @Override
+ public double getMaxZ() {
+ return this.z + 0.8;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWood.java b/src/main/java/cn/nukkit/block/BlockWood.java
index c9aebf02a9f..befbfb5e12f 100644
--- a/src/main/java/cn/nukkit/block/BlockWood.java
+++ b/src/main/java/cn/nukkit/block/BlockWood.java
@@ -8,15 +8,31 @@
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockWood extends BlockSolidMeta {
+
public static final int OAK = 0;
public static final int SPRUCE = 1;
public static final int BIRCH = 2;
public static final int JUNGLE = 3;
+ private static final short[] FACES = {
+ 0,
+ 0,
+ 0b1000,
+ 0b1000,
+ 0b0100, // full bark
+ 0b0100
+ };
+
+ private static final int[] strippedIds = {
+ STRIPPED_OAK_LOG,
+ STRIPPED_SPRUCE_LOG,
+ STRIPPED_BIRCH_LOG,
+ STRIPPED_JUNGLE_LOG
+ };
public BlockWood() {
this(0);
@@ -38,12 +54,12 @@ public double getHardness() {
@Override
public double getResistance() {
- return 10;
+ return 2;
}
@Override
public String getName() {
- String[] names = new String[]{
+ String[] names = {
"Oak Wood",
"Spruce Wood",
"Birch Wood",
@@ -65,23 +81,17 @@ public int getBurnAbility() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- short[] faces = new short[]{
- 0,
- 0,
- 0b1000,
- 0b1000,
- 0b0100,
- 0b0100
- };
-
- this.setDamage(((this.getDamage() & 0x03) | faces[face.getIndex()]));
+ this.setDamage(((this.getDamage() & 0x03) | FACES[face.getIndex()]));
this.getLevel().setBlock(block, this, true, true);
-
return true;
}
@Override
public Item toItem() {
+ if (this.getDamage() > 11) {
+ int variant = this.getDamage() & 0x03;
+ return new ItemBlock(Block.get(WOOD_BARK, variant), variant);
+ }
return new ItemBlock(this, this.getDamage() & 0x03);
}
@@ -92,7 +102,7 @@ public int getToolType() {
@Override
public BlockColor getColor() {
- switch(getDamage() & 0x07){
+ switch (getDamage() & 0x03) {
default:
case OAK:
return BlockColor.WOOD_BLOCK_COLOR;
@@ -104,4 +114,38 @@ public BlockColor getColor() {
return BlockColor.DIRT_BLOCK_COLOR;
}
}
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public boolean onActivate(Item item, Player player) {
+ if (item.isAxe() && player != null && (player.isSurvival() || player.isCreative()) && (!(this instanceof BlockWoodBark) || this.getDamage() < 8)) {
+ Block strippedBlock = Block.get(getStrippedId(), getStrippedDamage());
+ item.useOn(this);
+ this.level.setBlock(this, strippedBlock, true, true);
+ return true;
+ }
+ return false;
+ }
+
+ protected int getStrippedId() {
+ int damage = getDamage();
+ if ((damage & 0b1100) == 0b1100) { // Only bark
+ return WOOD_BARK;
+ }
+
+ return strippedIds[damage & 0x03];
+ }
+
+ protected int getStrippedDamage() {
+ int damage = getDamage();
+ if ((damage & 0b1100) == 0b1100) { // Only bark
+ return damage & 0x03 | 0x8;
+ }
+
+ return damage >> 2;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockWood2.java b/src/main/java/cn/nukkit/block/BlockWood2.java
index 1de3f2c7eaf..668978c1c32 100644
--- a/src/main/java/cn/nukkit/block/BlockWood2.java
+++ b/src/main/java/cn/nukkit/block/BlockWood2.java
@@ -1,9 +1,11 @@
package cn.nukkit.block;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
import cn.nukkit.utils.BlockColor;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockWood2 extends BlockWood {
@@ -11,7 +13,7 @@ public class BlockWood2 extends BlockWood {
public static final int ACACIA = 0;
public static final int DARK_OAK = 1;
- private static final String[] NAMES = new String[]{
+ private static final String[] NAMES = {
"Acacia Wood",
"Dark Oak Wood",
""
@@ -37,7 +39,7 @@ public String getName() {
@Override
public BlockColor getColor() {
- switch(getDamage() & 0x07){
+ switch (getDamage() & 0x07) {
case ACACIA:
return BlockColor.ORANGE_BLOCK_COLOR;
case DARK_OAK:
@@ -46,4 +48,48 @@ public BlockColor getColor() {
return BlockColor.WOOD_BLOCK_COLOR;
}
}
+
+ @Override
+ public boolean canBeActivated() {
+ return true;
+ }
+
+ @Override
+ public Item toItem() {
+ if (this.getDamage() > 11) {
+ int variant = this.getDamage() & 0x07;
+ return new ItemBlock(Block.get(WOOD_BARK, variant), variant);
+ }
+ return new ItemBlock(this, this.getDamage() & 0x03);
+ }
+
+ @Override
+ protected int getStrippedId() {
+ int damage = getDamage();
+ if ((damage & 0b1100) == 0b1100) { // Only bark
+ return WOOD_BARK;
+ }
+
+ int typeId = damage & 0x3;
+ if (typeId == 0) {
+ return STRIPPED_ACACIA_LOG;
+ } else {
+ return STRIPPED_DARK_OAK_LOG;
+ }
+ }
+
+ @Override
+ protected int getStrippedDamage() {
+ int damage = getDamage();
+ if ((damage & 0b1100) == 0b1100) { // Only bark
+ int typeId = damage & 0x3;
+ if (typeId == 0) {
+ return 0x4 | 0x8;
+ } else {
+ return 0x5 | 0x8;
+ }
+ }
+
+ return super.getStrippedDamage();
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodBark.java b/src/main/java/cn/nukkit/block/BlockWoodBark.java
new file mode 100644
index 00000000000..38c6db436a8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodBark.java
@@ -0,0 +1,75 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.math.BlockFace;
+
+public class BlockWoodBark extends BlockWood {
+
+ private static final String[] names = {
+ "Oak Wood",
+ "Spruce Wood",
+ "Birch Wood",
+ "Jungle Wood",
+ "Acacia Wood",
+ "Dark Oak Wood",
+ };
+
+
+ public BlockWoodBark() {
+ this(0);
+ }
+
+ public BlockWoodBark(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public void setDamage(int meta) {
+ super.setDamage(meta);
+ }
+
+ @Override
+ public int getId() {
+ return WOOD_BARK;
+ }
+
+ @Override
+ public String getName() {
+ int variant = (this.getDamage() & 0x7);
+ if (names.length <= variant) {
+ return names[0];
+ }
+ return names[variant];
+ }
+
+ @Override
+ protected int getStrippedId() {
+ return this.getId();
+ }
+
+ @Override
+ protected int getStrippedDamage() {
+ return getDamage() | 0x8;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ /*if (face.getAxis().isHorizontal()) {
+ if (face.getAxis() == BlockFace.Axis.X) {
+ setDamage(getDamage() | 0x10);
+ } else {
+ setDamage(getDamage() | 0x20);
+ }
+ }*/
+ this.getLevel().setBlock(block, this, true, true);
+ return true;
+ }
+
+ @Override
+ public Item toItem() {
+ int meta = this.getDamage() & 0xF;
+ return new ItemBlock(Block.get(WOOD_BARK, meta), meta);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodStripped.java b/src/main/java/cn/nukkit/block/BlockWoodStripped.java
new file mode 100644
index 00000000000..fbee7af91b8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodStripped.java
@@ -0,0 +1,51 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.math.BlockFace;
+
+public abstract class BlockWoodStripped extends BlockWood {
+
+ private static final short[] FACES = {
+ 0,
+ 0,
+ 0b10,
+ 0b10,
+ 0b01,
+ 0b01
+ };
+
+ public BlockWoodStripped() {
+ this(0);
+ }
+
+ public BlockWoodStripped(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public abstract int getId();
+
+ @Override
+ public abstract String getName();
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ this.setDamage(FACES[face.getIndex()]);
+ this.getLevel().setBlock(block, this, true, true);
+ return true;
+ }
+
+ @Override
+ public boolean canBeActivated() {
+ return false;
+ }
+
+ @Override
+ public Item toItem() {
+ // I was this before merge from upstream
+ // return new ItemBlock(this, this.getDamage());
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodStrippedAcacia.java b/src/main/java/cn/nukkit/block/BlockWoodStrippedAcacia.java
new file mode 100644
index 00000000000..8dda9abec07
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodStrippedAcacia.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWoodStrippedAcacia extends BlockWoodStripped {
+
+ public BlockWoodStrippedAcacia() {
+ this(0);
+ }
+
+ public BlockWoodStrippedAcacia(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Acacia Log";
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_ACACIA_LOG;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.ORANGE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodStrippedBirch.java b/src/main/java/cn/nukkit/block/BlockWoodStrippedBirch.java
new file mode 100644
index 00000000000..116747d2844
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodStrippedBirch.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWoodStrippedBirch extends BlockWoodStripped {
+
+ public BlockWoodStrippedBirch() {
+ this(0);
+ }
+
+ public BlockWoodStrippedBirch(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Birch Log";
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_BIRCH_LOG;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SAND_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodStrippedDarkOak.java b/src/main/java/cn/nukkit/block/BlockWoodStrippedDarkOak.java
new file mode 100644
index 00000000000..63466210a97
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodStrippedDarkOak.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWoodStrippedDarkOak extends BlockWoodStripped {
+
+ public BlockWoodStrippedDarkOak() {
+ this(0);
+ }
+
+ public BlockWoodStrippedDarkOak(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Dark Oak Log";
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_DARK_OAK_LOG;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BROWN_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodStrippedJungle.java b/src/main/java/cn/nukkit/block/BlockWoodStrippedJungle.java
new file mode 100644
index 00000000000..04f8d706fc4
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodStrippedJungle.java
@@ -0,0 +1,29 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWoodStrippedJungle extends BlockWoodStripped {
+
+ public BlockWoodStrippedJungle() {
+ this(0);
+ }
+
+ public BlockWoodStrippedJungle(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Jungle Log";
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_JUNGLE_LOG;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DIRT_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodStrippedOak.java b/src/main/java/cn/nukkit/block/BlockWoodStrippedOak.java
new file mode 100644
index 00000000000..7a3bae4643f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodStrippedOak.java
@@ -0,0 +1,28 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWoodStrippedOak extends BlockWoodStripped {
+
+ public BlockWoodStrippedOak() {
+ this(0);
+ }
+
+ public BlockWoodStrippedOak(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Oak Log";
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_OAK_LOG;
+ }
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockWoodStrippedSpruce.java b/src/main/java/cn/nukkit/block/BlockWoodStrippedSpruce.java
new file mode 100644
index 00000000000..1154cad9487
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWoodStrippedSpruce.java
@@ -0,0 +1,28 @@
+package cn.nukkit.block;
+
+import cn.nukkit.utils.BlockColor;
+
+public class BlockWoodStrippedSpruce extends BlockWoodStripped {
+
+ public BlockWoodStrippedSpruce() {
+ this(0);
+ }
+
+ public BlockWoodStrippedSpruce(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Stripped Spruce Log";
+ }
+
+ @Override
+ public int getId() {
+ return STRIPPED_SPRUCE_LOG;
+ }
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.SPRUCE_BLOCK_COLOR;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/Blocks.java b/src/main/java/cn/nukkit/block/Blocks.java
new file mode 100644
index 00000000000..caa69bb63f2
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/Blocks.java
@@ -0,0 +1,609 @@
+package cn.nukkit.block;
+
+import static cn.nukkit.block.Block.list;
+import static cn.nukkit.block.BlockID.*;
+
+class Blocks {
+
+ static {
+ list[AIR] = BlockAir.class; //0
+ list[STONE] = BlockStone.class; //1
+ list[GRASS] = BlockGrass.class; //2
+ list[DIRT] = BlockDirt.class; //3
+ list[COBBLESTONE] = BlockCobblestone.class; //4
+ list[PLANKS] = BlockPlanks.class; //5
+ list[SAPLING] = BlockSapling.class; //6
+ list[BEDROCK] = BlockBedrock.class; //7
+ list[WATER] = BlockWater.class; //8
+ list[STILL_WATER] = BlockWaterStill.class; //9
+ list[LAVA] = BlockLava.class; //10
+ list[STILL_LAVA] = BlockLavaStill.class; //11
+ list[SAND] = BlockSand.class; //12
+ list[GRAVEL] = BlockGravel.class; //13
+ list[GOLD_ORE] = BlockOreGold.class; //14
+ list[IRON_ORE] = BlockOreIron.class; //15
+ list[COAL_ORE] = BlockOreCoal.class; //16
+ list[WOOD] = BlockWood.class; //17
+ list[LEAVES] = BlockLeaves.class; //18
+ list[SPONGE] = BlockSponge.class; //19
+ list[GLASS] = BlockGlass.class; //20
+ list[LAPIS_ORE] = BlockOreLapis.class; //21
+ list[LAPIS_BLOCK] = BlockLapis.class; //22
+ list[DISPENSER] = BlockDispenser.class; //23
+ list[SANDSTONE] = BlockSandstone.class; //24
+ list[NOTEBLOCK] = BlockNoteblock.class; //25
+ list[BED_BLOCK] = BlockBed.class; //26
+ list[POWERED_RAIL] = BlockRailPowered.class; //27
+ list[DETECTOR_RAIL] = BlockRailDetector.class; //28
+ list[STICKY_PISTON] = BlockPistonSticky.class; //29
+ list[COBWEB] = BlockCobweb.class; //30
+ list[TALL_GRASS] = BlockTallGrass.class; //31
+ list[DEAD_BUSH] = BlockDeadBush.class; //32
+ list[PISTON] = BlockPiston.class; //33
+ list[PISTON_HEAD] = BlockPistonHead.class; //34
+ list[WOOL] = BlockWool.class; //35
+ list[DANDELION] = BlockDandelion.class; //37
+ list[FLOWER] = BlockFlower.class; //38
+ list[BROWN_MUSHROOM] = BlockMushroomBrown.class; //39
+ list[RED_MUSHROOM] = BlockMushroomRed.class; //40
+ list[GOLD_BLOCK] = BlockGold.class; //41
+ list[IRON_BLOCK] = BlockIron.class; //42
+ list[DOUBLE_STONE_SLAB] = BlockDoubleSlabStone.class; //43
+ list[STONE_SLAB] = BlockSlabStone.class; //44
+ list[BRICKS_BLOCK] = BlockBricks.class; //45
+ list[TNT] = BlockTNT.class; //46
+ list[BOOKSHELF] = BlockBookshelf.class; //47
+ list[MOSS_STONE] = BlockMossStone.class; //48
+ list[OBSIDIAN] = BlockObsidian.class; //49
+ list[TORCH] = BlockTorch.class; //50
+ list[FIRE] = BlockFire.class; //51
+ list[MONSTER_SPAWNER] = BlockMobSpawner.class; //52
+ list[WOOD_STAIRS] = BlockStairsWood.class; //53
+ list[CHEST] = BlockChest.class; //54
+ list[REDSTONE_WIRE] = BlockRedstoneWire.class; //55
+ list[DIAMOND_ORE] = BlockOreDiamond.class; //56
+ list[DIAMOND_BLOCK] = BlockDiamond.class; //57
+ list[WORKBENCH] = BlockCraftingTable.class; //58
+ list[WHEAT_BLOCK] = BlockWheat.class; //59
+ list[FARMLAND] = BlockFarmland.class; //60
+ list[FURNACE] = BlockFurnace.class; //61
+ list[BURNING_FURNACE] = BlockFurnaceBurning.class; //62
+ list[SIGN_POST] = BlockSignPost.class; //63
+ list[WOOD_DOOR_BLOCK] = BlockDoorWood.class; //64
+ list[LADDER] = BlockLadder.class; //65
+ list[RAIL] = BlockRail.class; //66
+ list[COBBLESTONE_STAIRS] = BlockStairsCobblestone.class; //67
+ list[WALL_SIGN] = BlockWallSign.class; //68
+ list[LEVER] = BlockLever.class; //69
+ list[STONE_PRESSURE_PLATE] = BlockPressurePlateStone.class; //70
+ list[IRON_DOOR_BLOCK] = BlockDoorIron.class; //71
+ list[WOODEN_PRESSURE_PLATE] = BlockPressurePlateWood.class; //72
+ list[REDSTONE_ORE] = BlockOreRedstone.class; //73
+ list[GLOWING_REDSTONE_ORE] = BlockOreRedstoneGlowing.class; //74
+ list[UNLIT_REDSTONE_TORCH] = BlockRedstoneTorchUnlit.class;
+ list[REDSTONE_TORCH] = BlockRedstoneTorch.class; //76
+ list[STONE_BUTTON] = BlockButtonStone.class; //77
+ list[SNOW_LAYER] = BlockSnowLayer.class; //78
+ list[ICE] = BlockIce.class; //79
+ list[SNOW_BLOCK] = BlockSnow.class; //80
+ 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
+ list[SOUL_SAND] = BlockSoulSand.class; //88
+ list[GLOWSTONE_BLOCK] = BlockGlowstone.class; //89
+ list[NETHER_PORTAL] = BlockNetherPortal.class; //90
+ list[LIT_PUMPKIN] = BlockPumpkinLit.class; //91
+ list[CAKE_BLOCK] = BlockCake.class; //92
+ list[UNPOWERED_REPEATER] = BlockRedstoneRepeaterUnpowered.class; //93
+ list[POWERED_REPEATER] = BlockRedstoneRepeaterPowered.class; //94
+ list[INVISIBLE_BEDROCK] = BlockBedrockInvisible.class; //95
+ list[TRAPDOOR] = BlockTrapdoor.class; //96
+ list[MONSTER_EGG] = BlockMonsterEgg.class; //97
+ list[STONE_BRICKS] = BlockBricksStone.class; //98
+ list[BROWN_MUSHROOM_BLOCK] = BlockHugeMushroomBrown.class; //99
+ list[RED_MUSHROOM_BLOCK] = BlockHugeMushroomRed.class; //100
+ list[IRON_BARS] = BlockIronBars.class; //101
+ list[GLASS_PANE] = BlockGlassPane.class; //102
+ list[MELON_BLOCK] = BlockMelon.class; //103
+ list[PUMPKIN_STEM] = BlockStemPumpkin.class; //104
+ list[MELON_STEM] = BlockStemMelon.class; //105
+ list[VINE] = BlockVine.class; //106
+ list[FENCE_GATE] = BlockFenceGate.class; //107
+ list[BRICK_STAIRS] = BlockStairsBrick.class; //108
+ list[STONE_BRICK_STAIRS] = BlockStairsStoneBrick.class; //109
+ list[MYCELIUM] = BlockMycelium.class; //110
+ list[WATER_LILY] = BlockWaterLily.class; //111
+ list[NETHER_BRICKS] = BlockBricksNether.class; //112
+ list[NETHER_BRICK_FENCE] = BlockFenceNetherBrick.class; //113
+ list[NETHER_BRICKS_STAIRS] = BlockStairsNetherBrick.class; //114
+ list[NETHER_WART_BLOCK] = BlockNetherWart.class; //115
+ list[ENCHANTING_TABLE] = BlockEnchantingTable.class; //116
+ list[BREWING_STAND_BLOCK] = BlockBrewingStand.class; //117
+ list[CAULDRON_BLOCK] = BlockCauldron.class; //118
+ list[END_PORTAL] = BlockEndPortal.class; //119
+ list[END_PORTAL_FRAME] = BlockEndPortalFrame.class; //120
+ list[END_STONE] = BlockEndStone.class; //121
+ list[DRAGON_EGG] = BlockDragonEgg.class; //122
+ list[REDSTONE_LAMP] = BlockRedstoneLamp.class; //123
+ list[LIT_REDSTONE_LAMP] = BlockRedstoneLampLit.class; //124
+ list[DROPPER] = BlockDropper.class; //125
+ list[ACTIVATOR_RAIL] = BlockRailActivator.class; //126
+ list[COCOA] = BlockCocoa.class; //127
+ list[SANDSTONE_STAIRS] = BlockStairsSandstone.class; //128
+ list[EMERALD_ORE] = BlockOreEmerald.class; //129
+ list[ENDER_CHEST] = BlockEnderChest.class; //130
+ list[TRIPWIRE_HOOK] = BlockTripWireHook.class;
+ list[TRIPWIRE] = BlockTripWire.class; //132
+ list[EMERALD_BLOCK] = BlockEmerald.class; //133
+ list[SPRUCE_WOOD_STAIRS] = BlockStairsSpruce.class; //134
+ list[BIRCH_WOOD_STAIRS] = BlockStairsBirch.class; //135
+ list[JUNGLE_WOOD_STAIRS] = BlockStairsJungle.class; //136
+ list[COMMAND_BLOCK] = BlockCommandBlock.class; //137
+ list[BEACON] = BlockBeacon.class; //138
+ list[STONE_WALL] = BlockWall.class; //139
+ list[FLOWER_POT_BLOCK] = BlockFlowerPot.class; //140
+ list[CARROT_BLOCK] = BlockCarrot.class; //141
+ list[POTATO_BLOCK] = BlockPotato.class; //142
+ list[WOODEN_BUTTON] = BlockButtonWooden.class; //143
+ list[SKULL_BLOCK] = BlockSkull.class; //144
+ list[ANVIL] = BlockAnvil.class; //145
+ list[TRAPPED_CHEST] = BlockTrappedChest.class; //146
+ list[LIGHT_WEIGHTED_PRESSURE_PLATE] = BlockWeightedPressurePlateLight.class; //147
+ list[HEAVY_WEIGHTED_PRESSURE_PLATE] = BlockWeightedPressurePlateHeavy.class; //148
+ list[UNPOWERED_COMPARATOR] = BlockRedstoneComparatorUnpowered.class; //149
+ list[POWERED_COMPARATOR] = BlockRedstoneComparatorPowered.class; //149
+ list[DAYLIGHT_DETECTOR] = BlockDaylightDetector.class; //151
+ list[REDSTONE_BLOCK] = BlockRedstone.class; //152
+ list[QUARTZ_ORE] = BlockOreQuartz.class; //153
+ list[HOPPER_BLOCK] = BlockHopper.class; //154
+ list[QUARTZ_BLOCK] = BlockQuartz.class; //155
+ list[QUARTZ_STAIRS] = BlockStairsQuartz.class; //156
+ list[DOUBLE_WOOD_SLAB] = BlockDoubleSlabWood.class; //157
+ list[WOOD_SLAB] = BlockSlabWood.class; //158
+ list[STAINED_TERRACOTTA] = BlockTerracottaStained.class; //159
+ list[STAINED_GLASS_PANE] = BlockGlassPaneStained.class; //160
+ list[LEAVES2] = BlockLeaves2.class; //161
+ list[WOOD2] = BlockWood2.class; //162
+ list[ACACIA_WOOD_STAIRS] = BlockStairsAcacia.class; //163
+ list[DARK_OAK_WOOD_STAIRS] = BlockStairsDarkOak.class; //164
+ list[SLIME_BLOCK] = BlockSlime.class; //165
+ //list[GLOW_STICK] = BlockGlowStick.class; //166
+ list[IRON_TRAPDOOR] = BlockTrapdoorIron.class; //167
+ list[PRISMARINE] = BlockPrismarine.class; //168
+ list[SEA_LANTERN] = BlockSeaLantern.class; //169
+ list[HAY_BALE] = BlockHayBale.class; //170
+ list[CARPET] = BlockCarpet.class; //171
+ list[TERRACOTTA] = BlockTerracotta.class; //172
+ list[COAL_BLOCK] = BlockCoal.class; //173
+ list[PACKED_ICE] = BlockIcePacked.class; //174
+ list[DOUBLE_PLANT] = BlockDoublePlant.class; //175
+ list[STANDING_BANNER] = BlockBanner.class; //176
+ list[WALL_BANNER] = BlockWallBanner.class; //177
+ list[DAYLIGHT_DETECTOR_INVERTED] = BlockDaylightDetectorInverted.class; //178
+ list[RED_SANDSTONE] = BlockRedSandstone.class; //179
+ list[RED_SANDSTONE_STAIRS] = BlockStairsRedSandstone.class; //180
+ list[DOUBLE_RED_SANDSTONE_SLAB] = BlockDoubleSlabRedSandstone.class; //181
+ list[RED_SANDSTONE_SLAB] = BlockSlabRedSandstone.class; //182
+ list[FENCE_GATE_SPRUCE] = BlockFenceGateSpruce.class; //183
+ list[FENCE_GATE_BIRCH] = BlockFenceGateBirch.class; //184
+ list[FENCE_GATE_JUNGLE] = BlockFenceGateJungle.class; //185
+ list[FENCE_GATE_DARK_OAK] = BlockFenceGateDarkOak.class; //186
+ list[FENCE_GATE_ACACIA] = BlockFenceGateAcacia.class; //187
+ list[REPEATING_COMMAND_BLOCK] = BlockCommandBlockRepeating.class; //188
+ list[CHAIN_COMMAND_BLOCK] = BlockCommandBlockChain.class; //189
+ //list[HARD_GLASS_PANE] = BlockHardGlassPane.class; //190
+ //list[HARD_STAINED_GLASS_PANE] = BlockHardGlassPaneStained.class; //191
+ //list[CHEMICAL_HEAT] = BlockChemicalHeat.class; //192
+ list[SPRUCE_DOOR_BLOCK] = BlockDoorSpruce.class; //193
+ list[BIRCH_DOOR_BLOCK] = BlockDoorBirch.class; //194
+ list[JUNGLE_DOOR_BLOCK] = BlockDoorJungle.class; //195
+ list[ACACIA_DOOR_BLOCK] = BlockDoorAcacia.class; //196
+ list[DARK_OAK_DOOR_BLOCK] = BlockDoorDarkOak.class; //197
+ list[GRASS_PATH] = BlockGrassPath.class; //198
+ list[ITEM_FRAME_BLOCK] = BlockItemFrame.class; //199
+ list[CHORUS_FLOWER] = BlockChorusFlower.class; //200
+ list[PURPUR_BLOCK] = BlockPurpur.class; //201
+ //list[COLORED_TORCH_RG] = BlockColoredTorchRG.class; //202
+ list[PURPUR_STAIRS] = BlockStairsPurpur.class; //203
+ //list[COLORED_TORCH_BP] = BlockColoredTorchBP.class; //204
+ list[UNDYED_SHULKER_BOX] = BlockUndyedShulkerBox.class; //205
+ list[END_BRICKS] = BlockBricksEndStone.class; //206
+ list[FROSTED_ICE] = BlockIceFrosted.class; //207
+ list[END_ROD] = BlockEndRod.class; //208
+ list[END_GATEWAY] = BlockEndGateway.class; //209
+ list[ALLOW] = BlockAllow.class; //210
+ list[DENY] = BlockDeny.class; //211
+ list[BORDER_BLOCK] = BlockBorder.class; //212
+ list[MAGMA] = BlockMagma.class; //213
+ list[BLOCK_NETHER_WART_BLOCK] = BlockNetherWartBlock.class; //214
+ list[RED_NETHER_BRICK] = BlockBricksRedNether.class; //215
+ list[BONE_BLOCK] = BlockBone.class; //216
+ // 217 not yet in Minecraft
+ list[SHULKER_BOX] = BlockShulkerBox.class; //218
+ list[PURPLE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedPurple.class; //219
+ list[WHITE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedWhite.class; //220
+ list[ORANGE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedOrange.class; //221
+ list[MAGENTA_GLAZED_TERRACOTTA] = BlockTerracottaGlazedMagenta.class; //222
+ list[LIGHT_BLUE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedLightBlue.class; //223
+ list[YELLOW_GLAZED_TERRACOTTA] = BlockTerracottaGlazedYellow.class; //224
+ list[LIME_GLAZED_TERRACOTTA] = BlockTerracottaGlazedLime.class; //225
+ list[PINK_GLAZED_TERRACOTTA] = BlockTerracottaGlazedPink.class; //226
+ list[GRAY_GLAZED_TERRACOTTA] = BlockTerracottaGlazedGray.class; //227
+ list[SILVER_GLAZED_TERRACOTTA] = BlockTerracottaGlazedSilver.class; //228
+ list[CYAN_GLAZED_TERRACOTTA] = BlockTerracottaGlazedCyan.class; //229
+ // 230 Chalkboard in Education Edition
+ list[BLUE_GLAZED_TERRACOTTA] = BlockTerracottaGlazedBlue.class; //231
+ list[BROWN_GLAZED_TERRACOTTA] = BlockTerracottaGlazedBrown.class; //232
+ list[GREEN_GLAZED_TERRACOTTA] = BlockTerracottaGlazedGreen.class; //233
+ list[RED_GLAZED_TERRACOTTA] = BlockTerracottaGlazedRed.class; //234
+ list[BLACK_GLAZED_TERRACOTTA] = BlockTerracottaGlazedBlack.class; //235
+ list[CONCRETE] = BlockConcrete.class; //236
+ list[CONCRETE_POWDER] = BlockConcretePowder.class; //237
+ //list[CHEMISTRY_TABLE] = BlockChemistryTable.class; //238
+ //list[UNDERWATER_TORCH] = BlockUnderwaterTorch.class; //239
+ list[CHORUS_PLANT] = BlockChorusPlant.class; //240
+ list[STAINED_GLASS] = BlockGlassStained.class; //241
+ // 242 Camera in Education Edition
+ list[PODZOL] = BlockPodzol.class; //243
+ list[BEETROOT_BLOCK] = BlockBeetroot.class; //244
+ list[STONECUTTER] = BlockStonecutter.class; //244
+ list[GLOWING_OBSIDIAN] = BlockObsidianGlowing.class; //246
+ list[NETHER_REACTOR] = BlockNetherReactor.class; //247
+ list[INFO_UPDATE] = BlockInfoUpdate.class; //248
+ list[INFO_UPDATE2] = BlockInfoUpdate2.class; //249
+ list[PISTON_EXTENSION] = BlockPistonExtension.class; //250
+ list[OBSERVER] = BlockObserver.class; //251
+ list[STRUCTURE_BLOCK] = BlockStructureBlock.class; //252
+ //list[HARD_GLASS] = BlockHardGlass.class; //253
+ //list[HARD_STAINED_GLASS] = BlockHardGlassStained.class; //254
+ //list[RESERVED6] = BlockReserved6.class; //255
+ // 256 not yet in Minecraft
+ list[PRISMARINE_STAIRS] = BlockStairsPrismarine.class; //257
+ list[DARK_PRISMARINE_STAIRS] = BlockStairsDarkPrismarine.class; //258
+ list[PRISMARINE_BRICKS_STAIRS] = BlockStairsPrismarineBrick.class; //259
+ list[STRIPPED_SPRUCE_LOG] = BlockWoodStrippedSpruce.class; //260
+ list[STRIPPED_BIRCH_LOG] = BlockWoodStrippedBirch.class; //261
+ list[STRIPPED_JUNGLE_LOG] = BlockWoodStrippedJungle.class; //262
+ list[STRIPPED_ACACIA_LOG] = BlockWoodStrippedAcacia.class; //263
+ list[STRIPPED_DARK_OAK_LOG] = BlockWoodStrippedDarkOak.class; //264
+ list[STRIPPED_OAK_LOG] = BlockWoodStrippedOak.class; //265
+ list[BLUE_ICE] = BlockBlueIce.class; //266
+ //
+ list[SEAGRASS] = BlockSeagrass.class; //385
+ list[CORAL] = BlockCoral.class; //386
+ list[CORAL_BLOCK] = BlockCoralBlock.class; //387
+ list[CORAL_FAN] = BlockCoralFan.class; //388
+ list[CORAL_FAN_DEAD] = BlockCoralFanDead.class; //389
+ list[CORAL_FAN_HANG] = BlockCoralFanHang.class; //390
+ list[CORAL_FAN_HANG2] = BlockCoralFanHang2.class; //391
+ list[CORAL_FAN_HANG3] = BlockCoralFanHang3.class; //392
+ list[BLOCK_KELP] = BlockKelp.class; //393
+ list[DRIED_KELP_BLOCK] = BlockDriedKelpBlock.class; //394
+ list[ACACIA_BUTTON] = BlockButtonAcacia.class; //395
+ list[BIRCH_BUTTON] = BlockButtonBirch.class; //396
+ list[DARK_OAK_BUTTON] = BlockButtonDarkOak.class; //397
+ list[JUNGLE_BUTTON] = BlockButtonJungle.class; //398
+ list[SPRUCE_BUTTON] = BlockButtonSpruce.class; //399
+ list[ACACIA_TRAPDOOR] = BlockTrapdoorAcacia.class; //400
+ list[BIRCH_TRAPDOOR] = BlockTrapdoorBirch.class; //401
+ list[DARK_OAK_TRAPDOOR] = BlockTrapdoorDarkOak.class; //402
+ list[JUNGLE_TRAPDOOR] = BlockTrapdoorJungle.class; //403
+ list[SPRUCE_TRAPDOOR] = BlockTrapdoorSpruce.class; //404
+ list[ACACIA_PRESSURE_PLATE] = BlockPressurePlateAcacia.class; //405
+ list[BIRCH_PRESSURE_PLATE] = BlockPressurePlateBirch.class; //406
+ list[DARK_OAK_PRESSURE_PLATE] = BlockPressurePlateDarkOak.class; //407
+ list[JUNGLE_PRESSURE_PLATE] = BlockPressurePlateJungle.class; //408
+ list[SPRUCE_PRESSURE_PLATE] = BlockPressurePlateSpruce.class; //409
+ list[CARVED_PUMPKIN] = BlockPumpkinCarved.class; //410
+ list[SEA_PICKLE] = BlockSeaPickle.class; //411
+ list[CONDUIT] = BlockConduit.class; //412
+ // 413 not yet in Minecraft
+ list[TURTLE_EGG] = BlockTurtleEgg.class; //414
+ list[BUBBLE_COLUMN] = BlockBubbleColumn.class; //415
+ list[BARRIER] = BlockBarrier.class; //416
+ list[STONE_SLAB3] = BlockSlabStone3.class; //417
+ list[BAMBOO] = BlockBamboo.class; //418
+ list[BAMBOO_SAPLING] = BlockBambooSapling.class; //419
+ list[STONE_SLAB4] = BlockSlabStone4.class; //421
+ list[SCAFFOLDING] = BlockScaffolding.class; //420
+ list[DOUBLE_STONE_SLAB3] = BlockDoubleSlabStone3.class; //422
+ list[DOUBLE_STONE_SLAB4] = BlockDoubleSlabStone4.class; //423
+ list[GRANITE_STAIRS] = BlockStairsGranite.class; //424
+ list[DIORITE_STAIRS] = BlockStairsDiorite.class; //425
+ list[ANDESITE_STAIRS] = BlockStairsAndesite.class; //426
+ list[POLISHED_GRANITE_STAIRS] = BlockStairsGranitePolished.class; //427
+ list[POLISHED_DIORITE_STAIRS] = BlockStairsDioritePolished.class; //428
+ list[POLISHED_ANDESITE_STAIRS] = BlockStairsAndesitePolished.class; //429
+ list[MOSSY_STONE_BRICK_STAIRS] = BlockStairsMossyStoneBrick.class; //430
+ list[SMOOTH_RED_SANDSTONE_STAIRS] = BlockStairsSmoothRedSandstone.class; //431
+ list[SMOOTH_SANDSTONE_STAIRS] = BlockStairsSmoothSandstone.class; //432
+ list[END_BRICK_STAIRS] = BlockStairsEndBrick.class; //433
+ list[MOSSY_COBBLESTONE_STAIRS] = BlockStairsMossyCobblestone.class; //434
+ list[NORMAL_STONE_STAIRS] = BlockStairsStone.class; //435
+ list[SPRUCE_STANDING_SIGN] = BlockSpruceSignStanding.class; //436
+ list[SPRUCE_WALL_SIGN] = BlockSpruceWallSign.class; //437
+ list[SMOOTH_STONE] = BlockSmoothStone.class; //438
+ list[RED_NETHER_BRICK_STAIRS] = BlockStairsRedNetherBrick.class; //439
+ list[SMOOTH_QUARTZ_STAIRS] = BlockStairsSmoothQuartz.class; //440
+ list[BIRCH_STANDING_SIGN] = BlockBirchSignStanding.class; //441
+ list[BIRCH_WALL_SIGN] = BlockBirchWallSign.class; //442
+ list[JUNGLE_STANDING_SIGN] = BlockJungleSignStanding.class; //443
+ list[JUNGLE_WALL_SIGN] = BlockJungleWallSign.class; //444
+ list[ACACIA_STANDING_SIGN] = BlockAcaciaSignStanding.class; //445
+ list[ACACIA_WALL_SIGN] = BlockAcaciaWallSign.class; //446
+ list[DARK_OAK_STANDING_SIGN] = BlockDarkOakSignStanding.class; //447
+ list[DARK_OAK_WALL_SIGN] = BlockDarkOakWallSign.class; //448
+ list[LECTERN] = BlockLectern.class; //449
+ list[GRINDSTONE] = BlockGrindstone.class; //450
+ list[BLAST_FURNACE] = BlockBlastFurnace.class; //451
+ list[STONECUTTER_BLOCK] = BlockStonecutterBlock.class; //452
+ list[SMOKER] = BlockSmoker.class; //453
+ list[LIT_SMOKER] = BlockSmokerLit.class; //454
+ list[CARTOGRAPHY_TABLE] = BlockCartographyTable.class; //455
+ list[FLETCHING_TABLE] = BlockFletchingTable.class; //456
+ list[SMITHING_TABLE] = BlockSmithingTable.class; //457
+ list[BARREL] = BlockBarrel.class; //458
+ list[LOOM] = BlockLoom.class; //459
+ // 460 unused
+ list[BELL] = BlockBell.class; //461
+ list[SWEET_BERRY_BUSH] = BlockSweetBerryBush.class; //462
+ list[LANTERN] = BlockLantern.class; //463
+ list[CAMPFIRE_BLOCK] = BlockCampfire.class; //464
+ list[LAVA_CAULDRON] = BlockCauldronLava.class; //465
+ list[JIGSAW] = BlockJigsaw.class; //466
+ list[WOOD_BARK] = BlockWoodBark.class; //467
+ list[COMPOSTER] = BlockComposter.class; //468
+ list[LIT_BLAST_FURNACE] = BlockBlastFurnaceLit.class; //469
+ list[LIGHT_BLOCK] = BlockLightBlock.class; //470
+ list[WITHER_ROSE] = BlockWitherRose.class; //471
+ list[PISTON_HEAD_STICKY] = BlockPistonHeadSticky.class; //472
+ list[BEE_NEST] = BlockBeeNest.class; //473
+ list[BEEHIVE] = BlockBeehive.class; //474
+ list[HONEY_BLOCK] = BlockHoneyBlock.class; //475
+ list[HONEYCOMB_BLOCK] = BlockHoneycombBlock.class; //476
+ list[LODESTONE] = BlockLodestone.class; //477
+ list[CRIMSON_ROOTS] = BlockCrimsonRoots.class; //478
+ list[WARPED_ROOTS] = BlockWarpedRoots.class; //479
+ list[CRIMSON_STEM] = BlockCrimsonStem.class; //480
+ list[WARPED_STEM] = BlockWarpedStem.class; //481
+ list[WARPED_WART_BLOCK] = BlockWarpedWartBlock.class; //482
+ list[CRIMSON_FUNGUS] = BlockCrimsonFungus.class; //483
+ list[WARPED_FUNGUS] = BlockWarpedFungus.class; //484
+ list[SHROOMLIGHT] = BlockShroomlight.class; //485
+ list[WEEPING_VINES] = BlockWeepingVines.class; //486
+ list[CRIMSON_NYLIUM] = BlockCrimsonNylium.class; //487
+ list[WARPED_NYLIUM] = BlockWarpedNylium.class; //488
+ list[BASALT] = BlockBasalt.class; //489
+ list[POLISHED_BASALT] = BlockPolishedBasalt.class; //490
+ list[SOUL_SOIL] = BlockSoulSoil.class; //491
+ list[SOUL_FIRE] = BlockSoulFire.class; //492
+ list[NETHER_SPROUTS_BLOCK] = BlockNetherSprouts.class; //493
+ list[TARGET] = BlockTarget.class; //494
+ list[STRIPPED_CRIMSON_STEM] = BlockStrippedCrimsonStem.class; //495
+ list[STRIPPED_WARPED_STEM] = BlockStrippedWarpedStem.class; //496
+ list[CRIMSON_PLANKS] = BlockCrimsonPlanks.class; //497
+ list[WARPED_PLANKS] = BlockWarpedPlanks.class; //498
+ list[CRIMSON_DOOR_BLOCK] = BlockCrimsonDoor.class; //499
+ list[WARPED_DOOR_BLOCK] = BlockWarpedDoor.class; //500
+ list[CRIMSON_TRAPDOOR] = BlockCrimsonTrapdoor.class; //501
+ list[WARPED_TRAPDOOR] = BlockWarpedTrapdoor.class; //502
+ // 503-504 (Unused)
+ list[CRIMSON_STANDING_SIGN] = BlockCrimsonSign.class; //505
+ list[WARPED_STANDING_SIGN] = BlockWarpedSign.class; //506
+ list[CRIMSON_WALL_SIGN] = BlockCrimsonWallSign.class; //507
+ list[WARPED_WALL_SIGN] = BlockWarpedWallSign.class; //508
+ list[CRIMSON_STAIRS] = BlockCrimsonStairs.class; //509
+ list[WARPED_STAIRS] = BlockWarpedStairs.class; //510
+ list[CRIMSON_FENCE] = BlockFenceCrimson.class; // 511
+ list[WARPED_FENCE] = BlockFenceWarped.class; // 512
+ list[CRIMSON_FENCE_GATE] = BlockFenceGateCrimson.class; // 513
+ list[WARPED_FENCE_GATE] = BlockFenceGateWarped.class; // 514
+ list[CRIMSON_BUTTON] = BlockButtonCrimson.class; // 515
+ list[WARPED_BUTTON] = BlockButtonWarped.class; // 516
+ list[CRIMSON_PRESSURE_PLATE] = BlockPressurePlateCrimson.class; // 517
+ list[WARPED_PRESSURE_PLATE] = BlockPressurePlateWarped.class; // 518
+ list[CRIMSON_SLAB] = BlockSlabCrimson.class; //519
+ list[WARPED_SLAB] = BlockSlabWarped.class; //520
+ list[CRIMSON_DOUBLE_SLAB] = BlockDoubleSlabCrimson.class; //521
+ list[WARPED_DOUBLE_SLAB] = BlockDoubleSlabWarped.class; //522
+ list[SOUL_TORCH] = BlockSoulTorch.class; //523
+ list[SOUL_LANTERN] = BlockSoulLantern.class; //524
+ list[NETHERITE_BLOCK] = BlockNetheriteBlock.class; //525
+ list[ANCIENT_DEBRIS] = BlockAncientDebris.class; //526
+ list[RESPAWN_ANCHOR] = BlockRespawnAnchor.class; //527
+ list[BLACKSTONE] = BlockBlackstone.class; //528
+ list[POLISHED_BLACKSTONE_BRICKS] = BlockBricksBlackstonePolished.class; //529
+ list[POLISHED_BLACKSTONE_BRICK_STAIRS] = BlockStairsBrickBlackstonePolished.class; //530
+ list[BLACKSTONE_STAIRS] = BlockStairsBlackstone.class; //531
+ list[BLACKSTONE_WALL] = BlockBlackstoneWall.class; //532
+ list[POLISHED_BLACKSTONE_BRICK_WALL] = BlockPolishedBlackstoneBrickWall.class; //533
+ list[CHISELED_POLISHED_BLACKSTONE] = BlockBlackstonePolishedChiseled.class; //534
+ list[CRACKED_POLISHED_BLACKSTONE_BRICKS] = BlockBricksBlackstonePolishedCracked.class; //535
+ list[GILDED_BLACKSTONE] = BlockBlackstoneGilded.class; //536
+ list[BLACKSTONE_SLAB] = BlockSlabBlackstone.class; //537
+ list[BLACKSTONE_DOUBLE_SLAB] = BlockDoubleSlabBlackstone.class; //538
+ list[POLISHED_BLACKSTONE_BRICK_SLAB] = BlockSlabBrickBlackstonePolished.class; //539
+ list[POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB] = BlockDoubleSlabBrickBlackstonePolished.class; //540
+ list[CHAIN_BLOCK] = BlockChain.class; //541
+ list[TWISTING_VINES] = BlockVinesTwisting.class; //542
+ list[NETHER_GOLD_ORE] = BlockOreGoldNether.class; //543
+ list[CRYING_OBSIDIAN] = BlockObsidianCrying.class; //544
+ list[SOUL_CAMPFIRE_BLOCK] = BlockCampfireSoul.class; //545
+ list[POLISHED_BLACKSTONE] = BlockBlackstonePolished.class; //546
+ list[POLISHED_BLACKSTONE_STAIRS] = BlockStairsBlackstonePolished.class; //547
+ list[POLISHED_BLACKSTONE_SLAB] = BlockSlabBlackstonePolished.class; //548
+ list[POLISHED_BLACKSTONE_DOUBLE_SLAB] = BlockDoubleSlabBlackstonePolished.class; //549
+ list[POLISHED_BLACKSTONE_PRESSURE_PLATE] = BlockPressurePlatePolishedBlackstone.class; //550
+ list[POLISHED_BLACKSTONE_BUTTON] = BlockButtonPolishedBlackstone.class; //551
+ list[POLISHED_BLACKSTONE_WALL] = BlockPolishedBlackstoneWall.class; //552
+ list[WARPED_HYPHAE] = BlockHyphaeWarped.class; //553
+ list[CRIMSON_HYPHAE] = BlockHyphaeCrimson.class; //554
+ list[STRIPPED_CRIMSON_HYPHAE] = BlockHyphaeStrippedCrimson.class; //555
+ list[STRIPPED_WARPED_HYPHAE] = BlockHyphaeStrippedWarped.class; //556
+ list[CHISELED_NETHER_BRICKS] = BlockBricksNetherChiseled.class; //557
+ list[CRACKED_NETHER_BRICKS] = BlockBricksNetherCracked.class; //558
+ list[QUARTZ_BRICKS] = BlockQuartzBricks.class; //559
+ // 560 unknown
+ list[POWDER_SNOW] = BlockPowderSnow.class; // 561
+ // list[SCULK_SENSOR] = BlockSculkSensor.class; // 562
+ list[POINTED_DRIPSTONE] = BlockPointedDripstone.class; // 563
+ // 564-565 (Unused)
+ list[COPPER_ORE] = BlockOreCopper.class; //566
+ list[LIGHTNING_ROD] = BlockLightningRod.class; //567
+ // 568-571 (Unused)
+ list[DRIPSTONE_BLOCK] = BlockDripstone.class; //572
+ list[ROOTED_DIRT] = BlockDirtRooted.class; //573
+ list[HANGING_ROOTS] = BlockRootsHanging.class; //574
+ list[MOSS_BLOCK] = BlockMoss.class; //575
+ list[SPORE_BLOSSOM] = BlockSporeBlossom.class; //576
+ list[CAVE_VINES] = BlockCaveVines.class; //577
+ list[BIG_DRIPLEAF] = BlockDripleafBig.class; //578
+ list[AZALEA_LEAVES] = BlockAzaleaLeaves.class; //579
+ list[AZALEA_LEAVES_FLOWERED] = BlockAzaleaLeavesFlowered.class; //580
+ list[CALCITE] = BlockCalcite.class; //581
+ list[AMETHYST_BLOCK] = BlockAmethyst.class; //582
+ list[BUDDING_AMETHYST] = BlockBuddingAmethyst.class; //583
+ list[AMETHYST_CLUSTER] = BlockAmethystCluster.class; //584
+ list[LARGE_AMETHYST_BUD] = BlockAmethystBudLarge.class; //585
+ list[MEDIUM_AMETHYST_BUD] = BlockAmethystBudMedium.class; //586
+ list[SMALL_AMETHYST_BUD] = BlockAmethystBudSmall.class; //587
+ list[TUFF] = BlockTuff.class; //588
+ list[TINTED_GLASS] = BlockGlassTinted.class; //589
+ list[MOSS_CARPET] = BlockMossCarpet.class; //590
+ list[SMALL_DRIPLEAF] = BlockDripleafSmall.class; //591
+ list[AZALEA] = BlockAzalea.class; //592
+ list[FLOWERING_AZALEA] = BlockAzaleaFlowering.class; //593
+ list[GLOW_FRAME] = BlockItemFrameGlow.class; //594
+ list[COPPER_BLOCK] = BlockCopper.class; //595
+ list[EXPOSED_COPPER] = BlockCopperExposed.class; //596
+ list[WEATHERED_COPPER] = BlockCopperWeathered.class; //597
+ list[OXIDIZED_COPPER] = BlockCopperOxidized.class; //598
+ list[WAXED_COPPER] = BlockCopperWaxed.class; //599
+ list[WAXED_EXPOSED_COPPER] = BlockCopperExposedWaxed.class; //600
+ list[WAXED_WEATHERED_COPPER] = BlockCopperWeatheredWaxed.class; //601
+ list[CUT_COPPER] = BlockCopperCut.class; //602
+ list[EXPOSED_CUT_COPPER] = BlockCopperCutExposed.class; //603
+ list[WEATHERED_CUT_COPPER] = BlockCopperCutWeathered.class; //604
+ list[OXIDIZED_CUT_COPPER] = BlockCopperCutOxidized.class; //605
+ list[WAXED_CUT_COPPER] = BlockCopperCutWaxed.class; //606
+ list[WAXED_EXPOSED_CUT_COPPER] = BlockCopperCutExposedWaxed.class; //607
+ list[WAXED_WEATHERED_CUT_COPPER] = BlockCopperCutWeatheredWaxed.class; //608
+ list[CUT_COPPER_STAIRS] = BlockStairsCopperCut.class; //609
+ list[EXPOSED_CUT_COPPER_STAIRS] = BlockStairsCopperCutExposed.class; //610
+ list[WEATHERED_CUT_COPPER_STAIRS] = BlockStairsCopperCutWeathered.class; //611
+ list[OXIDIZED_CUT_COPPER_STAIRS] = BlockStairsCopperCutOxidized.class; //612
+ list[WAXED_CUT_COPPER_STAIRS] = BlockStairsCopperCutWaxed.class; //613
+ list[WAXED_EXPOSED_CUT_COPPER_STAIRS] = BlockStairsCopperCutExposedWaxed.class; //614
+ list[WAXED_WEATHERED_CUT_COPPER_STAIRS] = BlockStairsCopperCutWeatheredWaxed.class; //615
+ list[CUT_COPPER_SLAB] = BlockSlabCopperCut.class; //616
+ list[EXPOSED_CUT_COPPER_SLAB] = BlockSlabCopperCutExposed.class; //617
+ list[WEATHERED_CUT_COPPER_SLAB] = BlockSlabCopperCutWeathered.class; //618
+ list[OXIDIZED_CUT_COPPER_SLAB] = BlockSlabCopperCutOxidized.class; //619
+ list[WAXED_CUT_COPPER_SLAB] = BlockSlabCopperCutWaxed.class; //620
+ list[WAXED_EXPOSED_CUT_COPPER_SLAB] = BlockSlabCopperCutExposedWaxed.class; //621
+ list[WAXED_WEATHERED_CUT_COPPER_SLAB] = BlockSlabCopperCutWeatheredWaxed.class; //622
+ list[DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCut.class; //623
+ list[EXPOSED_DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCutExposed.class; //624
+ list[WEATHERED_DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCutWeathered.class; //625
+ list[OXIDIZED_DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCutOxidized.class; //626
+ list[WAXED_DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCutWaxed.class; //627
+ list[WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCutExposedWaxed.class; //628
+ list[WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCutWeatheredWaxed.class; //629
+ list[CAVE_VINES_BODY_WITH_BERRIES] = BlockCaveVinesBerriesBody.class; //630
+ list[CAVE_VINES_HEAD_WITH_BERRIES] = BlockCaveVinesBerriesHead.class; //631
+ list[SMOOTH_BASALT] = BlockBasaltSmooth.class; //632
+ list[DEEPSLATE] = BlockDeepslate.class; //633
+ list[COBBLED_DEEPSLATE] = BlockDeepslateCobbled.class; //634
+ list[COBBLED_DEEPSLATE_SLAB] = BlockSlabDeepslateCobbled.class; //635
+ list[COBBLED_DEEPSLATE_STAIRS] = BlockStairsDeepslateCobbled.class; //636
+ list[COBBLED_DEEPSLATE_WALL] = BlockWallDeepslateCobbled.class; //637
+ list[POLISHED_DEEPSLATE] = BlockDeepslatePolished.class; //638
+ list[POLISHED_DEEPSLATE_SLAB] = BlockSlabDeepslatePolished.class; //639
+ list[POLISHED_DEEPSLATE_STAIRS] = BlockStairsDeepslatePolished.class; //640
+ list[POLISHED_DEEPSLATE_WALL] = BlockWallDeepslatePolished.class; //641
+ list[DEEPSLATE_TILES] = BlockTilesDeepslate.class; //642
+ list[DEEPSLATE_TILE_SLAB] = BlockSlabTileDeepslate.class; //643
+ list[DEEPSLATE_TILE_STAIRS] = BlockStairsTileDeepslate.class; //644
+ list[DEEPSLATE_TILE_WALL] = BlockWallTileDeepslate.class; //645
+ list[DEEPSLATE_BRICKS] = BlockBricksDeepslate.class; //646
+ list[DEEPSLATE_BRICK_SLAB] = BlockSlabBrickDeepslate.class; //647
+ list[DEEPSLATE_BRICK_STAIRS] = BlockStairsBrickDeepslate.class; //648
+ list[DEEPSLATE_BRICK_WALL] = BlockWallBrickDeepslate.class; //649
+ list[CHISELED_DEEPSLATE] = BlockDeepslateChiseled.class; //650
+ list[COBBLED_DEEPSLATE_DOUBLE_SLAB] = BlockDoubleSlabDeepslateCobbled.class; //651
+ list[POLISHED_DEEPSLATE_DOUBLE_SLAB] = BlockDoubleSlabDeepslatePolished.class; //652
+ list[DEEPSLATE_TILE_DOUBLE_SLAB] = BlockDoubleSlabTileDeepslate.class; //653
+ list[DEEPSLATE_BRICK_DOUBLE_SLAB] = BlockDoubleSlabBrickDeepslate.class; //654
+ list[DEEPSLATE_LAPIS_ORE] = BlockOreLapisDeepslate.class; //655
+ list[DEEPSLATE_IRON_ORE] = BlockOreIronDeepslate.class; //656
+ list[DEEPSLATE_GOLD_ORE] = BlockOreGoldDeepslate.class; //657
+ list[DEEPSLATE_REDSTONE_ORE] = BlockOreRedstoneDeepslate.class; //658
+ list[LIT_DEEPSLATE_REDSTONE_ORE] = BlockOreRedstoneDeepslateGlowing.class; //659
+ list[DEEPSLATE_DIAMOND_ORE] = BlockOreDiamondDeepslate.class; //660
+ list[DEEPSLATE_COAL_ORE] = BlockOreCoalDeepslate.class; //661
+ list[DEEPSLATE_EMERALD_ORE] = BlockOreEmeraldDeepslate.class; //662
+ list[DEEPSLATE_COPPER_ORE] = BlockOreCopperDeepslate.class; //663
+ list[CRACKED_DEEPSLATE_TILES] = BlockTilesDeepslateCracked.class; //664
+ list[CRACKED_DEEPSLATE_BRICKS] = BlockBricksDeepslateCracked.class; //665
+ list[GLOW_LICHEN] = BlockGlowLichen.class; //666
+
+ // TODO: candle & candle_cake
+// list[CANDLE] = BlockCandle.class; //667
+// list[WHITE_CANDLE] = BlockCandleWhite.class; //668
+// list[ORANGE_CANDLE] = BlockCandleOrange.class; //669
+// list[MAGENTA_CANDLE] = BlockCandleMagenta.class; //670
+// list[LIGHT_BLUE_CANDLE] = BlockCandleLightBlue.class; //671
+// list[YELLOW_CANDLE] = BlockCandleYellow.class; //672
+// list[LIME_CANDLE] = BlockCandleLime.class; //673
+// list[PINK_CANDLE] = BlockCandlePink.class; //674
+// list[GRAY_CANDLE] = BlockCandleGray.class; //675
+// list[LIGHT_GRAY_CANDLE] = BlockCandleLightGray.class; //676
+// list[CYAN_CANDLE] = BlockCandleCyan.class; //677
+// list[PURPLE_CANDLE] = BlockCandlePurple.class; //678
+// list[BLUE_CANDLE] = BlockCandleBlue.class; //679
+// list[BROWN_CANDLE] = BlockCandleBrown.class; //680
+// list[GREEN_CANDLE] = BlockCandleGreen.class; //681
+// list[RED_CANDLE] = BlockCandleRed.class; //682
+// list[BLACK_CANDLE] = BlockCandleBlack.class; //683
+
+ // Unused 684 - 700
+ list[WAXED_OXIDIZED_COPPER] = BlockCopperOxidizedWaxed.class; //701
+ list[WAXED_OXIDIZED_CUT_COPPER] = BlockCopperCutOxidizedWaxed.class; //702
+ list[WAXED_OXIDIZED_CUT_COPPER_STAIRS] = BlockStairsCopperCutOxidizedWaxed.class; //703
+ list[WAXED_OXIDIZED_CUT_COPPER_SLAB] = BlockSlabCopperCutOxidizedWaxed.class; //704
+ list[WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB] = BlockDoubleSlabCopperCutOxidizedWaxed.class; //705
+ list[RAW_IRON_BLOCK] = BlockRawIron.class; //706
+ list[RAW_COPPER_BLOCK] = BlockRawCopper.class; //707
+ list[RAW_GOLD_BLOCK] = BlockRawGold.class; //708
+ list[INFESTED_DEEPSLATE] = BlockInfestedDeepslate.class; //709
+ // 710-712 (Unused)
+ // sculk
+ // sculk_vein
+ // sculk_catalyst
+ // sculk_shrieker
+ // 717-719 (Unused)
+ // client_request_placeholder_block
+ // mysterious_frame
+ // mysterious_frame_slot
+ // frog_spawn
+ // pearlescent_froglight
+ // verdant_froglight
+ // ochre_froglight
+ // 727
+ list[MUD] = BlockMud.class; //728
+ list[MUD_BRICKS] = BlockMudBrick.class; //730
+ list[PACKED_MUD] = BlockPackedMud.class; //732
+ list[MUD_BRICK_SLAB] = BlockMudBrickSlab.class; //733
+ list[MUD_BRICK_DOUBLE_SLAB] = BlockDoubleMudBrickSlab.class; //734
+ list[MUD_BRICK_STAIRS] = BlockMudBrickStairs.class; //735
+ list[MUD_BRICK_WALL] = BlockMudBrickWall.class; //736
+ }
+
+ static void init() {
+ // Init
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/Oxidizable.java b/src/main/java/cn/nukkit/block/Oxidizable.java
new file mode 100644
index 00000000000..7c9869f2c2b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/Oxidizable.java
@@ -0,0 +1,118 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.OxidizationLevel;
+import cn.nukkit.event.block.BlockFadeEvent;
+import cn.nukkit.item.Item;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.Location;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.particle.ScrapeParticle;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * @author joserobjr
+ */
+public interface Oxidizable {
+
+ Location getLocation();
+
+ default int onUpdate(int type) {
+ if (type != Level.BLOCK_UPDATE_RANDOM) {
+ return 0;
+ }
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ if (!(random.nextFloat() < 64F / 1125F)) {
+ return 0;
+ }
+
+ int oxiLvl = this.getOxidizationLevel().ordinal();
+ if (oxiLvl == OxidizationLevel.OXIDIZED.ordinal()) {
+ return 0;
+ }
+
+ // Just to make sure we don't accidentally degrade a waxed block.
+ if ((this instanceof Waxable) && ((Waxable) this).isWaxed()) {
+ return 0;
+ }
+
+ Block block = this instanceof Block? (Block) this : getLocation().getLevelBlock();
+ Location mutableLocation = block.getLocation();
+
+ int odds = 0;
+ int cons = 0;
+
+ scan:
+ for (int x = -4; x <= 4; x++) {
+ for (int y = -4; y <= 4; y++) {
+ for (int z = -4; z <= 4; z++) {
+ if (x == 0 && y == 0 && z == 0) {
+ continue;
+ }
+ mutableLocation.setComponents(block.x + x, block.y + y, block.z + z);
+ if (block.distanceSquared(mutableLocation) > 4) {
+ continue ;
+ }
+ Block relative = mutableLocation.getLevelBlock();
+ if (!(relative instanceof Oxidizable)) {
+ continue;
+ }
+ int relOxiLvl = ((Oxidizable) relative).getOxidizationLevel().ordinal();
+ if (relOxiLvl < oxiLvl) {
+ return type;
+ }
+
+ if (relOxiLvl > oxiLvl) {
+ cons++;
+ } else {
+ odds++;
+ }
+ }
+ }
+ }
+
+ float chance = (float)(cons + 1) / (float)(cons + odds + 1);
+ float multiplier = oxiLvl == 0? 0.75F : 1.0F;
+ chance = chance * chance * multiplier;
+ if (random.nextFloat() < chance) {
+ Block nextBlock = this.getStateWithOxidizationLevel(OxidizationLevel.values()[oxiLvl + 1]);
+ BlockFadeEvent event = new BlockFadeEvent(block, nextBlock);
+ block.getLevel().getServer().getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ block.getLevel().setBlock(block, event.getNewState());
+ }
+ }
+ return type;
+ }
+
+
+ default boolean onActivate(Item item, Player player) {
+ if (!item.isAxe()) {
+ return false;
+ }
+
+ OxidizationLevel oxidizationLevel = getOxidizationLevel();
+ if (OxidizationLevel.UNAFFECTED.equals(oxidizationLevel)) {
+ return false;
+ }
+
+ oxidizationLevel = OxidizationLevel.values()[oxidizationLevel.ordinal() - 1];
+ if (!setOxidizationLevel(oxidizationLevel)) {
+ return false;
+ }
+
+ Position location = this instanceof Block? (Position) this : getLocation();
+ if (player == null || !player.isCreative()) {
+ item.useOn(this instanceof Block? (Block) this : location.getLevelBlock());
+ }
+ location.getLevel().addParticle(new ScrapeParticle(location));
+ return true;
+ }
+
+ OxidizationLevel getOxidizationLevel();
+
+ boolean setOxidizationLevel(OxidizationLevel oxidizationLevel);
+
+ Block getStateWithOxidizationLevel(OxidizationLevel oxidizationLevel);
+}
diff --git a/src/main/java/cn/nukkit/block/Waxable.java b/src/main/java/cn/nukkit/block/Waxable.java
new file mode 100644
index 00000000000..e8408a1626a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/Waxable.java
@@ -0,0 +1,40 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.Location;
+import cn.nukkit.level.Position;
+import cn.nukkit.level.particle.WaxOffParticle;
+import cn.nukkit.level.particle.WaxOnParticle;
+
+public interface Waxable {
+
+ Location getLocation();
+
+ default boolean onActivate(Item item, Player player) {
+ boolean waxed = isWaxed();
+ if ((item.getId() != ItemID.HONEYCOMB || waxed) && (!item.isAxe() || !waxed)) {
+ return false;
+ }
+
+ waxed = !waxed;
+ if (!setWaxed(waxed)) {
+ return false;
+ }
+
+ Position location = this instanceof Block? (Position) this : getLocation();
+ if (player == null || !player.isCreative()) {
+ if (waxed) {
+ item.count--;
+ } else {
+ item.useOn(this instanceof Block? (Block) this : location.getLevelBlock());
+ }
+ }
+ location.getLevel().addParticle(waxed ? new WaxOnParticle(location) : new WaxOffParticle(location));
+ return true;
+ }
+
+ boolean isWaxed();
+ boolean setWaxed(boolean waxed);
+}
diff --git a/src/main/java/cn/nukkit/block/properties/BlockNotImplemented.java b/src/main/java/cn/nukkit/block/properties/BlockNotImplemented.java
new file mode 100644
index 00000000000..756637b9b54
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/BlockNotImplemented.java
@@ -0,0 +1,32 @@
+package cn.nukkit.block.properties;
+
+import cn.nukkit.block.BlockMeta;
+
+public class BlockNotImplemented extends BlockMeta {
+
+ private final int id;
+
+ public BlockNotImplemented(int id) {
+ this(id, 0);
+ }
+
+ public BlockNotImplemented(int id, int meta) {
+ super(meta);
+ this.id = id;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.1;
+ }
+
+ @Override
+ public String getName() {
+ return "Not Implemented";
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/properties/BlockPropertiesHelper.java b/src/main/java/cn/nukkit/block/properties/BlockPropertiesHelper.java
new file mode 100644
index 00000000000..8507639c66e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/BlockPropertiesHelper.java
@@ -0,0 +1,26 @@
+package cn.nukkit.block.properties;
+
+import cn.nukkit.customblock.container.BlockStorageContainer;
+
+public interface BlockPropertiesHelper extends BlockStorageContainer {
+
+ int getId();
+
+ int getDamage();
+ void setDamage(int meta);
+
+ @Override
+ default int getStorage() {
+ return this.getDamage();
+ }
+
+ @Override
+ default void setStorage(int damage) {
+ this.setDamage(damage);
+ }
+
+ @Override
+ default int getNukkitId() {
+ return this.getId();
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/properties/DripleafTilt.java b/src/main/java/cn/nukkit/block/properties/DripleafTilt.java
new file mode 100644
index 00000000000..97c1e897b22
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/DripleafTilt.java
@@ -0,0 +1,19 @@
+package cn.nukkit.block.properties;
+
+import lombok.Getter;
+
+@Getter
+public enum DripleafTilt {
+ NONE(true, -1),
+ UNSTABLE(false, 10),
+ PARTIAL_TILT(true, 10),
+ FULL_TILT(false, 100);
+
+ private final boolean stable;
+ private final int netxStateDelay;
+
+ DripleafTilt(boolean stable, int netxStateDelay) {
+ this.stable = stable;
+ this.netxStateDelay = netxStateDelay;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/properties/DripstoneThickness.java b/src/main/java/cn/nukkit/block/properties/DripstoneThickness.java
new file mode 100644
index 00000000000..f8da5c92fdb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/DripstoneThickness.java
@@ -0,0 +1,9 @@
+package cn.nukkit.block.properties;
+
+public enum DripstoneThickness {
+ TIP,
+ FRUSTUM,
+ MIDDLE,
+ BASE,
+ MERGE;
+}
diff --git a/src/main/java/cn/nukkit/block/properties/OxidizationLevel.java b/src/main/java/cn/nukkit/block/properties/OxidizationLevel.java
new file mode 100644
index 00000000000..4ac6a610f44
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/OxidizationLevel.java
@@ -0,0 +1,8 @@
+package cn.nukkit.block.properties;
+
+public enum OxidizationLevel {
+ UNAFFECTED,
+ EXPOSED,
+ WEATHERED,
+ OXIDIZED
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/properties/VanillaProperties.java b/src/main/java/cn/nukkit/block/properties/VanillaProperties.java
new file mode 100644
index 00000000000..6ae345f4e4b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/VanillaProperties.java
@@ -0,0 +1,20 @@
+package cn.nukkit.block.properties;
+
+import cn.nukkit.customblock.properties.BlockProperty;
+import cn.nukkit.customblock.properties.BooleanBlockProperty;
+import cn.nukkit.customblock.properties.EnumBlockProperty;
+import cn.nukkit.math.BlockFace;
+
+public class VanillaProperties {
+
+ public static final BooleanBlockProperty UPPER_BLOCK = new BooleanBlockProperty("upper_block_bit", false);
+
+ public static final BlockProperty DIRECTION = new EnumBlockProperty<>("direction", false,
+ new BlockFace[]{ BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST }).ordinal(true);
+
+ public static final BlockProperty FACING_DIRECTION = new EnumBlockProperty<>("facing_direction", false,
+ new BlockFace[] { BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST }).ordinal(true);
+
+ public static final BlockProperty STAIRS_DIRECTION = new EnumBlockProperty<>("weirdo_direction", false,
+ new BlockFace[]{ BlockFace.EAST, BlockFace.WEST, BlockFace.SOUTH, BlockFace.NORTH }).ordinal(true);
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntity.java b/src/main/java/cn/nukkit/blockentity/BlockEntity.java
index cbc97c453e7..ae65f0fac85 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntity.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntity.java
@@ -2,14 +2,15 @@
import cn.nukkit.Server;
import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockLayer;
import cn.nukkit.level.Position;
import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.level.persistence.PersistentDataContainer;
+import cn.nukkit.level.persistence.impl.PersistentDataContainerBlockWrapper;
import cn.nukkit.math.Vector3;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.utils.ChunkException;
-import cn.nukkit.utils.MainLogger;
-import co.aikar.timings.Timing;
-import co.aikar.timings.Timings;
+import cn.nukkit.utils.LevelException;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -19,6 +20,7 @@
* @author MagicDroidX
*/
public abstract class BlockEntity extends Position {
+
//WARNING: DO NOT CHANGE ANY NAME HERE, OR THE CLIENT WILL CRASH
public static final String CHEST = "Chest";
public static final String ENDER_CHEST = "EnderChest";
@@ -42,41 +44,49 @@ public abstract class BlockEntity extends Position {
public static final String JUKEBOX = "Jukebox";
public static final String SHULKER_BOX = "ShulkerBox";
public static final String BANNER = "Banner";
-
+ public static final String DROPPER = "Dropper";
+ public static final String DISPENSER = "Dispenser";
+ public static final String BARREL = "Barrel";
+ public static final String CAMPFIRE = "Campfire";
+ public static final String BELL = "Bell";
+ public static final String LECTERN = "Lectern";
+ public static final String BLAST_FURNACE = "BlastFurnace";
+ public static final String SMOKER = "Smoker";
+
+ // Not a vanilla block entity
+ public static final String PERSISTENT_CONTAINER = "PersistentContainer";
public static long count = 1;
- private static final BiMap> knownBlockEntities = HashBiMap.create(21);
+ private static final BiMap> knownBlockEntities = HashBiMap.create(30);
public FullChunk chunk;
public String name;
public long id;
- public boolean movable = true;
+ public boolean movable;
public boolean closed = false;
public CompoundTag namedTag;
- protected long lastUpdate;
protected Server server;
- protected Timing timing;
+
+ private PersistentDataContainer persistentContainer;
public BlockEntity(FullChunk chunk, CompoundTag nbt) {
if (chunk == null || chunk.getProvider() == null) {
- throw new ChunkException("Invalid garbage Chunk given to Block Entity");
+ throw new ChunkException("Invalid garbage chunk given to BlockEntity");
}
- this.timing = Timings.getBlockEntityTiming(this);
this.server = chunk.getProvider().getLevel().getServer();
this.chunk = chunk;
this.setLevel(chunk.getProvider().getLevel());
this.namedTag = nbt;
this.name = "";
- this.lastUpdate = System.currentTimeMillis();
this.id = BlockEntity.count++;
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.movable = this.namedTag.getBoolean("isMovable", true);
this.initBlockEntity();
@@ -84,12 +94,9 @@ public BlockEntity(FullChunk chunk, CompoundTag nbt) {
this.getLevel().addBlockEntity(this);
}
- protected void initBlockEntity() {
-
- }
+ protected void initBlockEntity() {}
public static BlockEntity createBlockEntity(String type, FullChunk chunk, CompoundTag nbt, Object... args) {
- type = type.replaceFirst("BlockEntity", ""); //TODO: Remove this after the first release
BlockEntity blockEntity = null;
if (knownBlockEntities.containsKey(type)) {
@@ -99,7 +106,7 @@ public static BlockEntity createBlockEntity(String type, FullChunk chunk, Compou
return null;
}
- for (Constructor constructor : clazz.getConstructors()) {
+ for (Constructor> constructor : clazz.getConstructors()) {
if (blockEntity != null) {
break;
}
@@ -120,11 +127,10 @@ public static BlockEntity createBlockEntity(String type, FullChunk chunk, Compou
blockEntity = (BlockEntity) constructor.newInstance(objects);
}
- } catch (Exception e) {
- MainLogger.getLogger().logException(e);
- }
-
+ } catch (Exception ignored) {}
}
+ } else {
+ Server.getInstance().getLogger().warning("Tried to create block entity that doesn't exists: " + type);
}
return blockEntity;
@@ -159,7 +165,7 @@ 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().isEmpty()) {
return tag;
} else {
return null;
@@ -170,6 +176,18 @@ public Block getBlock() {
return this.getLevelBlock();
}
+ @Override
+ public Block getLevelBlock() {
+ if (this.isValid()) return this.level.getBlock(this.chunk, this.getFloorX(), this.getFloorY(), this.getFloorZ(), BlockLayer.NORMAL, true);
+ else throw new LevelException("Undefined Level reference");
+ }
+
+ @Override
+ public Block getLevelBlock(BlockLayer layer) {
+ if (this.isValid()) return this.level.getBlock(this.chunk, this.getFloorX(), this.getFloorY(), this.getFloorZ(), layer, true);
+ else throw new LevelException("Undefined Level reference");
+ }
+
public abstract boolean isBlockEntityValid();
public boolean onUpdate() {
@@ -177,6 +195,9 @@ public boolean onUpdate() {
}
public final void scheduleUpdate() {
+ if (this.level.isBeingConverted) {
+ return;
+ }
this.level.scheduleBlockEntityUpdate(this);
}
@@ -193,12 +214,12 @@ public void close() {
}
}
- public void onBreak() {
-
- }
+ public void onBreak() {}
public void setDirty() {
- chunk.setChanged();
+ if (this.chunk != null) {
+ this.chunk.setChanged();
+ }
}
public String getName() {
@@ -210,10 +231,34 @@ public boolean isMovable() {
}
public static CompoundTag getDefaultCompound(Vector3 pos, String id) {
- return new CompoundTag("")
+ return new CompoundTag()
.putString("id", id)
.putInt("x", pos.getFloorX())
.putInt("y", pos.getFloorY())
.putInt("z", pos.getFloorZ());
}
-}
+
+ public PersistentDataContainer getPersistentDataContainer() {
+ if (this.persistentContainer == null) {
+ this.persistentContainer = new PersistentDataContainerBlockWrapper(this);
+ }
+ return this.persistentContainer;
+ }
+
+ public boolean hasPersistentDataContainer() {
+ return !this.getPersistentDataContainer().isEmpty();
+ }
+
+ public void onReplacedWith(BlockEntity blockEntity) {
+ blockEntity.getPersistentDataContainer().setStorage(this.getPersistentDataContainer().getStorage().clone());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof BlockEntity && this.getClass().equals(obj.getClass()) && super.equals(obj);
+ }
+
+ public boolean canSaveToStorage() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBanner.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBanner.java
index 66a7a38cfff..94bbeadc7d6 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityBanner.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBanner.java
@@ -28,7 +28,8 @@ protected void initBlockEntity() {
@Override
public boolean isBlockEntityValid() {
- return this.getBlock().getId() == Block.WALL_BANNER || this.getBlock().getId() == Block.STANDING_BANNER;
+ int id = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
+ return id == Block.WALL_BANNER || id == Block.STANDING_BANNER;
}
@Override
@@ -48,6 +49,7 @@ public int getBaseColor() {
public void setBaseColor(DyeColor color) {
this.namedTag.putInt("Base", color.getDyeData() & 0x0f);
+ setDirty();
}
public int getType() {
@@ -56,6 +58,7 @@ public int getType() {
public void setType(int type) {
this.namedTag.putInt("Type", type);
+ setDirty();
}
public void addPattern(BannerPattern pattern) {
@@ -64,6 +67,7 @@ public void addPattern(BannerPattern pattern) {
putInt("Color", pattern.getColor().getDyeData() & 0x0f).
putString("Pattern", pattern.getType().getName()));
this.namedTag.putList(patterns);
+ setDirty();
}
public BannerPattern getPattern(int index) {
@@ -72,9 +76,10 @@ public BannerPattern getPattern(int index) {
public void removePattern(int index) {
ListTag patterns = this.namedTag.getList("Patterns", CompoundTag.class);
- if(patterns.size() > index && index >= 0) {
+ if (patterns.size() > index && index >= 0) {
patterns.remove(index);
}
+ setDirty();
}
public int getPatternsSize() {
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBarrel.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBarrel.java
new file mode 100644
index 00000000000..6ac918397fe
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBarrel.java
@@ -0,0 +1,169 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.inventory.BarrelInventory;
+import cn.nukkit.inventory.InventoryHolder;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+
+import java.util.ArrayList;
+
+public class BlockEntityBarrel extends BlockEntitySpawnable implements InventoryHolder, BlockEntityContainer, BlockEntityNameable {
+
+ protected BarrelInventory inventory;
+
+ public BlockEntityBarrel(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ private void initInventory() {
+ if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) {
+ this.namedTag.putList(new ListTag("Items"));
+ }
+ ListTag list = (ListTag) this.namedTag.getList("Items");
+
+ this.inventory = new BarrelInventory(this);
+
+ for (CompoundTag compound : list.getAll()) {
+ Item item = NBTIO.getItemHelper(compound);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ if (!this.closed && this.inventory != null) {
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
+ }
+ }
+
+ super.close();
+ }
+
+ @Override
+ public void onBreak() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
+ for (Item content : inventory.getContents().values()) {
+ level.dropItem(this, content);
+ }
+ inventory.clearAll(); // Stop items from being moved around by another player in the inventory
+ }
+
+ @Override
+ public void saveNBT() {
+ super.saveNBT();
+
+ if (this.inventory != null) {
+ this.namedTag.putList(new ListTag("Items"));
+ for (int index = 0; index < this.getSize(); index++) {
+ this.setItem(index, this.inventory.getItem(index));
+ }
+ }
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == BlockID.BARREL;
+ }
+
+ @Override
+ public int getSize() {
+ return 27;
+ }
+
+ protected int getSlotIndex(int index) {
+ ListTag list = this.namedTag.getList("Items", CompoundTag.class);
+ for (int i = 0; i < list.size(); i++) {
+ if (list.get(i).getByte("Slot") == index) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public Item getItem(int index) {
+ int i = this.getSlotIndex(index);
+ if (i < 0) {
+ return new ItemBlock(Block.get(BlockID.AIR), 0, 0);
+ } else {
+ CompoundTag data = (CompoundTag) this.namedTag.getList("Items").get(i);
+ return NBTIO.getItemHelper(data);
+ }
+ }
+
+ @Override
+ public void setItem(int index, Item item) {
+ int i = this.getSlotIndex(index);
+
+ CompoundTag d = NBTIO.putItemHelper(item, index);
+
+ // If item is air or count less than 0, remove the item from the "Items" list
+ if (item.getId() == Item.AIR || item.getCount() <= 0) {
+ if (i >= 0) {
+ this.namedTag.getList("Items").remove(i);
+ }
+ } else if (i < 0) {
+ // 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 inventorySlot, so we are going to overwrite the item in the list
+ (this.namedTag.getList("Items", CompoundTag.class)).add(i, d);
+ }
+ }
+
+ @Override
+ public BarrelInventory getInventory() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
+ return this.inventory;
+ }
+
+ @Override
+ public String getName() {
+ return this.hasName() ? this.namedTag.getString("CustomName") : "Barrel";
+ }
+
+ @Override
+ public boolean hasName() {
+ return this.namedTag.contains("CustomName");
+ }
+
+ @Override
+ public void setName(String name) {
+ if (name == null || name.isEmpty()) {
+ this.namedTag.remove("CustomName");
+ return;
+ }
+
+ this.namedTag.putString("CustomName", name);
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag c = new CompoundTag()
+ .putString("id", BlockEntity.BARREL)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ if (this.hasName()) {
+ c.put("CustomName", this.namedTag.get("CustomName"));
+ }
+
+ return c;
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java
index f84df308af3..993b6473d1b 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java
@@ -1,19 +1,20 @@
package cn.nukkit.blockentity;
import cn.nukkit.Player;
+import cn.nukkit.Server;
import cn.nukkit.block.Block;
-import cn.nukkit.block.BlockID;
-import cn.nukkit.inventory.BeaconInventory;
-import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.Item;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.network.protocol.LevelSoundEventPacket;
import cn.nukkit.potion.Effect;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Map;
/**
- * author: Rover656
+ * @author Rover656
*/
public class BlockEntityBeacon extends BlockEntitySpawnable {
@@ -39,15 +40,14 @@ protected void initBlockEntity() {
namedTag.putInt("Secondary", 0);
}
- scheduleUpdate();
+ this.scheduleUpdate();
super.initBlockEntity();
}
@Override
public boolean isBlockEntityValid() {
- int blockID = getBlock().getId();
- return blockID == Block.BEACON;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.BEACON;
}
@Override
@@ -67,18 +67,26 @@ public CompoundTag getSpawnCompound() {
@Override
public boolean onUpdate() {
+ if (this.closed) {
+ return false;
+ }
+
//Only apply effects every 4 secs
if (currentTick++ % 80 != 0) {
return true;
}
- int oldPowerLevel = this.getPowerLevel();
//Get the power level based on the pyramid
- setPowerLevel(calculatePowerLevel());
- int newPowerLevel = this.getPowerLevel();
+ int power = this.calculatePowerLevel();
+ if (power == -1) { //Couldn't calculate due to unloaded chunks
+ return true;
+ }
+
+ int oldPowerLevel = this.getPowerLevel();
+ this.setPowerLevel(power);
//Skip beacons that do not have a pyramid or sky access
- if (newPowerLevel < 1 || !hasSkyAccess()) {
+ if (this.getPowerLevel() < 1 || !hasSkyAccess()) {
if (oldPowerLevel > 0) {
this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BEACON_DEACTIVATE);
}
@@ -89,18 +97,15 @@ public boolean onUpdate() {
this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BEACON_AMBIENT);
}
- //Get all players in game
- Map players = this.level.getPlayers();
-
- //Calculate vars for beacon power
- int range = 10 + getPowerLevel() * 10;
- int duration = 9 + getPowerLevel() * 2;
+ int powerLevel = getPowerLevel();
+ //In seconds
+ int duration = (9 + (powerLevel << 1)) * 20;
- for(Map.Entry entry : players.entrySet()) {
+ for (Map.Entry entry : this.level.getPlayers().entrySet()) {
Player p = entry.getValue();
//If the player is in range
- if (p.distance(this) < range) {
+ if (p.distance(this) < 10 + powerLevel * 10) {
Effect e;
if (getPrimaryPower() != 0) {
@@ -108,7 +113,7 @@ public boolean onUpdate() {
e = Effect.getEffect(getPrimaryPower());
//Set duration
- e.setDuration(duration * 20);
+ e.setDuration(duration);
//If secondary is selected as the primary too, apply 2 amplification
if (getSecondaryPower() == getPrimaryPower()) {
@@ -130,10 +135,10 @@ public boolean onUpdate() {
e = Effect.getEffect(Effect.REGENERATION);
//Set duration
- e.setDuration(duration * 20);
+ e.setDuration(duration);
//Regen I
- e.setAmplifier(1);
+ e.setAmplifier(0);
//Hide particles
e.setVisible(false);
@@ -141,6 +146,10 @@ public boolean onUpdate() {
//Add effect
p.addEffect(e);
}
+
+ if (powerLevel >= POWER_LEVEL_MAX) {
+ p.awardAchievement("fullBeacon");
+ }
}
}
@@ -155,9 +164,9 @@ private boolean hasSkyAccess() {
int tileZ = getFloorZ();
//Check every block from our y coord to the top of the world
- for (int y = tileY + 1; y <= 255; y++) {
- int testBlockId = level.getBlockIdAt(tileX, y, tileZ);
- if (!Block.transparent[testBlockId]) {
+ for (int y = tileY + 1; y <= this.level.getMaxBlockY(); y++) {
+ int testBlockId = level.getBlockIdAt(chunk, tileX, y, tileZ);
+ if (!Block.isBlockTransparentById(testBlockId)) {
//There is no sky access
return false;
}
@@ -178,7 +187,11 @@ private int calculatePowerLevel() {
for (int queryX = tileX - powerLevel; queryX <= tileX + powerLevel; queryX++) {
for (int queryZ = tileZ - powerLevel; queryZ <= tileZ + powerLevel; queryZ++) {
- int testBlockId = level.getBlockIdAt(queryX, queryY, queryZ);
+ int testBlockId = getBlockIdIfLoaded(queryX, queryY, queryZ);
+ if (testBlockId == -1) {
+ return -1;
+ }
+
if (
testBlockId != Block.IRON_BLOCK &&
testBlockId != Block.GOLD_BLOCK &&
@@ -187,7 +200,6 @@ private int calculatePowerLevel() {
) {
return powerLevel - 1;
}
-
}
}
}
@@ -195,6 +207,26 @@ private int calculatePowerLevel() {
return POWER_LEVEL_MAX;
}
+ private int getBlockIdIfLoaded(int bx, int by, int bz) {
+ if (by < this.level.getMinBlockY() || by > this.level.getMaxBlockY()) return 0;
+ int cx = bx >> 4;
+ int cz = bz >> 4;
+ FullChunk fullChunk = this.chunk;
+ if (fullChunk == null || cx != fullChunk.getX() || cz != fullChunk.getZ()) {
+ fullChunk = level.getChunkIfLoaded(cx, cz);
+ if (fullChunk == null) {
+ return -1;
+ }
+ }
+ return fullChunk.getBlockId(bx & 0x0f, by, bz & 0x0f);
+ }
+
+ @Override
+ public void setDirty() {
+ super.setDirty();
+ this.spawnToAll();
+ }
+
public int getPowerLevel() {
return namedTag.getInt("Level");
}
@@ -204,7 +236,6 @@ public void setPowerLevel(int level) {
if (level != currentLevel) {
namedTag.putInt("Level", level);
setDirty();
- this.spawnToAll();
}
}
@@ -217,7 +248,6 @@ public void setPrimaryPower(int power) {
if (power != currentPower) {
namedTag.putInt("Primary", power);
setDirty();
- this.spawnToAll();
}
}
@@ -230,24 +260,34 @@ public void setSecondaryPower(int power) {
if (power != currentPower) {
namedTag.putInt("Secondary", power);
setDirty();
- this.spawnToAll();
}
}
+ private static final IntSet ALLOWED_EFFECTS = new IntOpenHashSet(new int[]{0, Effect.SPEED, Effect.HASTE, Effect.DAMAGE_RESISTANCE, Effect.JUMP, Effect.STRENGTH, Effect.REGENERATION});
+
@Override
public boolean updateCompoundTag(CompoundTag nbt, Player player) {
if (!nbt.getString("id").equals(BlockEntity.BEACON)) {
return false;
}
- this.setPrimaryPower(nbt.getInt("primary"));
- this.setSecondaryPower(nbt.getInt("secondary"));
+ int primary = nbt.getInt("primary");
+ if (ALLOWED_EFFECTS.contains(primary)) {
+ this.setPrimaryPower(primary);
+ } else {
+ Server.getInstance().getLogger().debug(player.getName() + " tried to set an invalid primary effect to a beacon: " + primary);
+ }
- this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BEACON_POWER);
+ int secondary = nbt.getInt("secondary");
+ if (ALLOWED_EFFECTS.contains(secondary)) {
+ this.setSecondaryPower(secondary);
+ } else {
+ Server.getInstance().getLogger().debug(player.getName() + " tried to set an invalid secondary effect to a beacon: " + secondary);
+ }
- BeaconInventory inv = (BeaconInventory)player.getWindowById(Player.BEACON_WINDOW_ID);
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BEACON_POWER);
- inv.setItem(0, new ItemBlock(Block.get(BlockID.AIR), 0, 0));
+ player.getWindowById(Player.BEACON_WINDOW_ID).setItem(0, Item.get(Item.AIR));
return true;
}
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBed.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBed.java
index 9fc6c2b8e70..db6851e73d4 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityBed.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBed.java
@@ -29,7 +29,7 @@ protected void initBlockEntity() {
@Override
public boolean isBlockEntityValid() {
- return this.level.getBlockIdAt(this.getFloorX(), this.getFloorY(), this.getFloorZ()) == Item.BED_BLOCK;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Item.BED_BLOCK;
}
@Override
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBell.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBell.java
new file mode 100644
index 00000000000..8b7169e7419
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBell.java
@@ -0,0 +1,149 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.tag.ByteTag;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.IntTag;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+
+public class BlockEntityBell extends BlockEntitySpawnable {
+
+ private boolean ringing;
+ private int direction;
+ private int ticks;
+ public final LongOpenHashSet spawnExceptions = new LongOpenHashSet(2);
+
+ public BlockEntityBell(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ @Override
+ protected void initBlockEntity() {
+ if (!namedTag.contains("Ringing") || !(namedTag.get("Ringing") instanceof ByteTag)) {
+ ringing = false;
+ } else {
+ ringing = namedTag.getBoolean("Ringing");
+ }
+
+ if (!namedTag.contains("Direction") || !(namedTag.get("Direction") instanceof IntTag)) {
+ direction = 255;
+ } else {
+ direction = namedTag.getInt("Direction");
+ }
+
+ if (!namedTag.contains("Ticks") || !(namedTag.get("Ticks") instanceof IntTag)) {
+ ticks = 0;
+ } else {
+ ticks = namedTag.getInt("Ticks");
+ }
+
+ super.initBlockEntity();
+ scheduleUpdate();
+ }
+
+ @Override
+ public void saveNBT() {
+ namedTag.putBoolean("Ringing", ringing);
+ namedTag.putInt("Direction", direction);
+ namedTag.putInt("Ticks", ticks);
+ super.saveNBT();
+ }
+
+ @Override
+ public boolean onUpdate() {
+ if (ringing) {
+ if (ticks == 0) {
+ this.level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BLOCK_BELL_HIT);
+ spawnToAllWithExceptions();
+ spawnExceptions.clear();
+ } else if (ticks >= 50) {
+ ringing = false;
+ ticks = 0;
+ spawnToAllWithExceptions();
+ spawnExceptions.clear();
+ return false;
+ }
+ //spawnToAll();
+ ticks++;
+ return true;
+ } else if (ticks > 0) {
+ ticks = 0;
+ spawnToAllWithExceptions();
+ spawnExceptions.clear();
+ }
+
+ return false;
+ }
+
+ private void spawnToAllWithExceptions() {
+ if (this.closed) {
+ return;
+ }
+
+ for (Player player : this.getLevel().getChunkPlayers(this.chunk.getX(), this.chunk.getZ()).values()) {
+ if (player.spawned && !spawnExceptions.contains(player.getId())) {
+ this.spawnTo(player);
+ }
+ }
+ }
+
+ public boolean isRinging() {
+ return ringing;
+ }
+
+ public void setRinging(boolean ringing) {
+ if (this.level != null && this.ringing != ringing) {
+ this.ringing = ringing;
+ scheduleUpdate();
+ setDirty();
+ }
+ }
+
+ public int getDirection() {
+ return direction;
+ }
+
+ public void setDirection(int direction) {
+ if (this.direction != direction) {
+ this.direction = direction;
+ setDirty();
+ }
+ }
+
+ public int getTicks() {
+ return ticks;
+ }
+
+ public void setTicks(int ticks) {
+ if (this.ticks != ticks) {
+ this.ticks = ticks;
+ setDirty();
+ }
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag tag = new CompoundTag()
+ .putString("id", BlockEntity.BELL)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z)
+ .putBoolean("Ringing", this.ringing)
+ .putInt("Direction", this.direction)
+ .putInt("Ticks", this.ticks);
+ return tag;
+ }
+
+ @Override
+ public String getName() {
+ return "Bell";
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == BlockID.BELL;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBlastFurnace.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBlastFurnace.java
new file mode 100644
index 00000000000..71678eaec1e
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBlastFurnace.java
@@ -0,0 +1,149 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.event.inventory.FurnaceSmeltEvent;
+import cn.nukkit.inventory.FurnaceRecipe;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemArmor;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockEntityBlastFurnace extends BlockEntityFurnace {
+
+ public BlockEntityBlastFurnace(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ @Override
+ public String getName() {
+ return this.hasName() ? this.namedTag.getString("CustomName") : "Blast Furnace";
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ int blockID = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
+ return blockID == Block.BLAST_FURNACE || blockID == Block.LIT_BLAST_FURNACE;
+ }
+
+ private static final IntSet CAN_SMELT_EXCLUDING_TOOLS_AND_ARMOR = new IntOpenHashSet(new int[]{
+ Item.IRON_ORE, Item.GOLD_ORE, Item.DIAMOND_ORE, Item.LAPIS_ORE, Item.REDSTONE_ORE, Item.COAL_ORE, Item.EMERALD_ORE, Item.QUARTZ_ORE,
+ 255 - Block.NETHER_GOLD_ORE, 255 - Block.ANCIENT_DEBRIS, Item.RAW_COPPER, Item.RAW_IRON, Item.RAW_GOLD,
+ 255 - Block.DEEPSLATE_COAL_ORE, 255 - Block.DEEPSLATE_IRON_ORE, 255 - Block.DEEPSLATE_GOLD_ORE, 255 - Block.DEEPSLATE_LAPIS_ORE,
+ 255 - Block.DEEPSLATE_EMERALD_ORE, 255 - Block.DEEPSLATE_COPPER_ORE, 255 - Block.DEEPSLATE_REDSTONE_ORE, 255 - Block.DEEPSLATE_DIAMOND_ORE
+ });
+
+ @Override
+ public boolean onUpdate() {
+ if (this.closed) {
+ return false;
+ }
+
+ Item raw = this.inventory.getSmelting();
+ // TODO: blast furnace recipes
+ if (!CAN_SMELT_EXCLUDING_TOOLS_AND_ARMOR.contains(raw.getId()) && !(raw instanceof ItemTool && (raw.getTier() == ItemTool.TIER_IRON || raw.getTier() == ItemTool.TIER_GOLD)) && !(raw instanceof ItemArmor && raw.getTier() >= ItemArmor.TIER_IRON && raw.getTier() <= ItemArmor.TIER_GOLD)) {
+ if (burnTime > 0) {
+ burnTime--;
+ burnDuration = (int) Math.ceil((float) burnTime / maxTime * 100);
+
+ if (burnTime == 0) {
+ Block block = this.level.getBlock(this.chunk, (int) x, (int) y, (int) z, true);
+ if (block.getId() == BlockID.LIT_BLAST_FURNACE) {
+ this.level.setBlock(this, Block.get(BlockID.BLAST_FURNACE, block.getDamage()), true);
+ }
+ return false;
+ }
+ }
+
+ cookTime = 0;
+ sendPacket();
+ return true;
+ }
+
+ boolean ret = false;
+ Item product = this.inventory.getResult();
+ FurnaceRecipe smelt = this.server.getCraftingManager().matchFurnaceRecipe(raw);
+ boolean canSmelt = (smelt != null && raw.getCount() > 0 && ((smelt.getResult().equals(product) && product.getCount() < product.getMaxStackSize()) || product.getId() == Item.AIR));
+
+ Item fuel;
+ if (burnTime <= 0 && canSmelt && (fuel = this.inventory.getItemFast(1)).getFuelTime() != null && fuel.getCount() > 0) {
+ this.checkFuel(fuel.clone());
+ }
+
+ if (burnTime > 0) {
+ burnTime--;
+ burnDuration = (int) Math.ceil((float) burnTime / maxTime * 100);
+
+ if (this.crackledTime-- <= 0) {
+ this.crackledTime = ThreadLocalRandom.current().nextInt(30, 110);
+ this.getLevel().addLevelSoundEvent(this.add(0.5, 0.5, 0.5), LevelSoundEventPacket.SOUND_BLOCK_FURNACE_LIT);
+ }
+
+ if (smelt != null && canSmelt) {
+ cookTime++;
+ if (cookTime >= 100) {
+ product = Item.get(smelt.getResult().getId(), smelt.getResult().getDamage(), product.isNull() ? 1 : product.getCount() + 1);
+
+ FurnaceSmeltEvent ev = new FurnaceSmeltEvent(this, raw, product);
+ this.server.getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ this.inventory.setResult(ev.getResult());
+ this.experience += FURNACE_XP.getOrDefault(ev.getResult().getId(), 0d);
+ raw.setCount(raw.getCount() - 1);
+ if (raw.getCount() == 0) {
+ raw = new ItemBlock(Block.get(BlockID.AIR), 0, 0);
+ }
+ this.inventory.setSmelting(raw);
+ }
+
+ cookTime -= 100;
+ }
+ } else if (burnTime <= 0) {
+ burnTime = 0;
+ cookTime = 0;
+ burnDuration = 0;
+ } else {
+ cookTime = 0;
+ }
+ ret = true;
+ } else {
+ Block block = this.level.getBlock(this.chunk, (int) x, (int) y, (int) z, true);
+ if (block.getId() == BlockID.LIT_BLAST_FURNACE) {
+ this.level.setBlock(this, Block.get(BlockID.BLAST_FURNACE, block.getDamage()), true);
+ }
+ burnTime = 0;
+ cookTime = 0;
+ burnDuration = 0;
+ crackledTime = 0;
+ }
+
+ sendPacket();
+
+ return ret;
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag c = new CompoundTag()
+ .putString("id", BlockEntity.BLAST_FURNACE)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z)
+ .putShort("BurnDuration", burnDuration)
+ .putShort("BurnTime", burnTime)
+ .putShort("CookTime", cookTime);
+
+ if (this.hasName()) {
+ c.put("CustomName", this.namedTag.get("CustomName"));
+ }
+
+ return c;
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java b/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java
index 29045ca294e..cf2508e01f1 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBrewingStand.java
@@ -1,18 +1,15 @@
package cn.nukkit.blockentity;
import cn.nukkit.Player;
-import cn.nukkit.Server;
import cn.nukkit.block.Block;
import cn.nukkit.block.BlockBrewingStand;
import cn.nukkit.block.BlockID;
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.ContainerRecipe;
-import cn.nukkit.inventory.InventoryHolder;
+import cn.nukkit.inventory.*;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemID;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.nbt.tag.CompoundTag;
@@ -21,9 +18,6 @@
import cn.nukkit.network.protocol.LevelSoundEventPacket;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
public class BlockEntityBrewingStand extends BlockEntitySpawnable implements InventoryHolder, BlockEntityContainer, BlockEntityNameable {
@@ -35,12 +29,6 @@ public class BlockEntityBrewingStand extends BlockEntitySpawnable implements Inv
public int fuelTotal;
public int fuelAmount;
- public static final List ingredients = new ArrayList() {
- {
- addAll(Arrays.asList(Item.NETHER_WART, Item.GHAST_TEAR, Item.GLOWSTONE_DUST, Item.REDSTONE_DUST, Item.GUNPOWDER, Item.MAGMA_CREAM, Item.BLAZE_POWDER, Item.GOLDEN_CARROT, Item.SPIDER_EYE, Item.FERMENTED_SPIDER_EYE, Item.GLISTERING_MELON, Item.SUGAR, Item.RABBIT_FOOT, Item.PUFFERFISH, Item.TURTLE_SHELL, Item.PHANTOM_MEMBRANE, 437));
- }
- };
-
public BlockEntityBrewingStand(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
}
@@ -53,8 +41,12 @@ protected void initBlockEntity() {
namedTag.putList(new ListTag("Items"));
}
- for (int i = 0; i < getSize(); i++) {
- inventory.setItem(i, this.getItem(i));
+ ListTag list = (ListTag) this.namedTag.getList("Items");
+ for (CompoundTag compound : list.getAll()) {
+ Item item = NBTIO.getItemHelper(compound);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
}
if (!namedTag.contains("CookTime") || namedTag.getShort("CookTime") > MAX_BREW_TIME) {
@@ -85,7 +77,7 @@ public boolean hasName() {
@Override
public void setName(String name) {
- if (name == null || name.equals("")) {
+ if (name == null || name.isEmpty()) {
namedTag.remove("CustomName");
return;
}
@@ -96,8 +88,8 @@ public void setName(String name) {
@Override
public void close() {
if (!closed) {
- for (Player player : new HashSet<>(getInventory().getViewers())) {
- player.removeWindow(getInventory());
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
}
super.close();
}
@@ -108,11 +100,12 @@ public void onBreak() {
for (Item content : inventory.getContents().values()) {
level.dropItem(this, content);
}
- this.inventory.clearAll();
+ inventory.clearAll();
}
@Override
public void saveNBT() {
+ super.saveNBT();
namedTag.putList(new ListTag("Items"));
for (int index = 0; index < getSize(); index++) {
this.setItem(index, inventory.getItem(index));
@@ -125,7 +118,7 @@ public void saveNBT() {
@Override
public boolean isBlockEntityValid() {
- return getBlock().getId() == Block.BREWING_STAND_BLOCK;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.BREWING_STAND_BLOCK;
}
@Override
@@ -177,101 +170,130 @@ public BrewingInventory getInventory() {
return inventory;
}
- protected boolean checkIngredient(Item ingredient) {
- return ingredients.contains(ingredient.getId());
- }
-
@Override
public boolean onUpdate() {
if (closed) {
return false;
}
- boolean ret = false;
+ restockFuel();
- Item ingredient = this.inventory.getIngredient();
- boolean canBrew = false;
+ if (this.fuelAmount <= 0 || matchRecipes(true)[0] == null) {
+ stopBrewing();
+ return false;
+ }
- 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;
+ if (brewTime == MAX_BREW_TIME) {
+ StartBrewEvent e = new StartBrewEvent(this);
+ this.server.getPluginManager().callEvent(e);
- this.inventory.setFuel(fuel);
- this.sendFuel();
+ if (e.isCancelled()) {
+ return false;
+ }
+
+ this.sendBrewTime();
}
- if (this.fuelAmount > 0) {
- for (int i = 1; i <= 3; i++) {
- if (this.inventory.getItem(i).getId() == Item.POTION) {
- canBrew = true;
- }
+ if (--brewTime > 0) {
+
+ if (brewTime % 40 == 0) {
+ sendBrewTime();
}
- if (this.brewTime <= MAX_BREW_TIME && canBrew && ingredient.getCount() > 0) {
- if (!this.checkIngredient(ingredient)) {
- canBrew = false;
+ return true;
+ }
+
+ //20 seconds
+ BrewEvent e = new BrewEvent(this);
+ this.server.getPluginManager().callEvent(e);
+
+ if (e.isCancelled()) {
+ stopBrewing();
+ return true;
+ }
+
+ boolean mixed = false;
+ MixRecipe[] recipes = matchRecipes(false);
+ for (int i = 0; i < 3; i++) {
+ MixRecipe recipe = recipes[i];
+ if (recipe == null) {
+ continue;
+ }
+
+ Item previous = inventory.getItem(i + 1);
+ if (!previous.isNull()) {
+ Item result = recipe.getResult();
+ result.setCount(previous.getCount());
+ if (recipe instanceof ContainerRecipe) {
+ result.setDamage(previous.getDamage());
}
- } else {
- canBrew = false;
+ inventory.setItem(i + 1, result);
+ mixed = true;
}
}
- if (canBrew) {
- if (this.brewTime == MAX_BREW_TIME) {
- this.sendBrewTime();
- StartBrewEvent e = new StartBrewEvent(this);
- this.server.getPluginManager().callEvent(e);
+ if (mixed) {
+ Item ingredient = this.inventory.getIngredient();
+ ingredient.count--;
+ this.inventory.setIngredient(ingredient);
- if (e.isCancelled()) {
- return false;
- }
+ this.fuelAmount--;
+ this.sendFuel();
+
+ this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POTION_BREWED);
+ }
+
+ stopBrewing();
+ return true;
+ }
+
+ private void restockFuel() {
+ Item fuel = this.getInventory().getFuel();
+ if (this.fuelAmount > 0 || fuel.getId() != ItemID.BLAZE_POWDER || fuel.getCount() <= 0) {
+ return;
+ }
+
+ fuel.count--;
+ this.fuelAmount = 20;
+ this.fuelTotal = 20;
+
+ this.inventory.setFuel(fuel);
+ this.sendFuel();
+ }
+
+ private void stopBrewing() {
+ this.brewTime = 0;
+ this.sendBrewTime();
+ this.brewTime = MAX_BREW_TIME;
+ }
+
+ private MixRecipe[] matchRecipes(boolean quickTest) {
+ MixRecipe[] recipes = new MixRecipe[quickTest? 1 : 3];
+ Item ingredient = inventory.getIngredient();
+ CraftingManager craftingManager = getLevel().getServer().getCraftingManager();
+ for (int i = 0; i < 3; i++) {
+ Item potion = inventory.getItem(i + 1);
+ if (potion.isNull()) {
+ continue;
}
- this.brewTime--;
-
- if (this.brewTime <= 0) { //20 seconds
- BrewEvent e = new BrewEvent(this);
- this.server.getPluginManager().callEvent(e);
-
- if (!e.isCancelled()) {
- for (int i = 1; i <= 3; i++) {
- Item potion = this.inventory.getItem(i);
-
- ContainerRecipe containerRecipe = Server.getInstance().getCraftingManager().matchContainerRecipe(ingredient, potion);
- if (containerRecipe != null) {
- Item result = containerRecipe.getResult();
- result.setDamage(potion.getDamage());
- this.inventory.setItem(i, result);
- } else {
- BrewingRecipe recipe = Server.getInstance().getCraftingManager().matchBrewingRecipe(ingredient, potion);
- if (recipe != null) {
- this.inventory.setItem(i, recipe.getResult());
- }
- }
- }
- this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_POTION_BREWED);
-
- ingredient.count--;
- this.inventory.setIngredient(ingredient);
-
- this.fuelAmount--;
- this.sendFuel();
- }
+ MixRecipe recipe = craftingManager.matchBrewingRecipe(ingredient, potion);
+ if (recipe == null) {
+ recipe = craftingManager.matchContainerRecipe(ingredient, potion);
+ }
+ if (recipe == null) {
+ continue;
+ }
- this.brewTime = MAX_BREW_TIME;
+ if (quickTest) {
+ recipes[0] = recipe;
+ return recipes;
}
- ret = true;
- } else {
- this.brewTime = MAX_BREW_TIME;
+ recipes[i] = recipe;
}
- //this.sendBrewTime();
- lastUpdate = System.currentTimeMillis();
-
- return ret;
+ return recipes;
}
protected void sendFuel() {
@@ -328,6 +350,10 @@ public void updateBlock() {
block.setDamage(meta);
this.level.setBlock(block, block, false, false);
+
+ if (brewTime != MAX_BREW_TIME && matchRecipes(true)[0] == null) {
+ stopBrewing();
+ }
}
public int getFuel() {
@@ -358,4 +384,4 @@ public CompoundTag getSpawnCompound() {
return nbt;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityCampfire.java b/src/main/java/cn/nukkit/blockentity/BlockEntityCampfire.java
new file mode 100644
index 00000000000..33819d9e5f0
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityCampfire.java
@@ -0,0 +1,229 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockCampfire;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.event.block.CampfireSmeltEvent;
+import cn.nukkit.inventory.CampfireInventory;
+import cn.nukkit.inventory.CampfireRecipe;
+import cn.nukkit.inventory.InventoryHolder;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockEntityCampfire extends BlockEntitySpawnable implements InventoryHolder, BlockEntityContainer {
+
+ private CampfireInventory inventory;
+ private int[] burnTime;
+ private CampfireRecipe[] recipes;
+ private boolean[] keepItem;
+
+ public BlockEntityCampfire(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ @Override
+ protected void initBlockEntity() {
+ this.inventory = new CampfireInventory(this);
+ this.burnTime = new int[4];
+ this.recipes = new CampfireRecipe[4];
+ this.keepItem = new boolean[4];
+
+ for (int i = 1; i <= burnTime.length; i++) {
+ burnTime[i -1] = namedTag.getInt("ItemTime" + i);
+ keepItem[i -1] = namedTag.getBoolean("KeepItem" + 1);
+
+ if (this.namedTag.contains("Item" + i) && this.namedTag.get("Item" + i) instanceof CompoundTag) {
+ inventory.setItem(i - 1, NBTIO.getItemHelper(this.namedTag.getCompound("Item" + i)));
+ }
+ }
+
+ super.initBlockEntity();
+ this.scheduleUpdate();
+ }
+
+ @Override
+ public boolean onUpdate() {
+ if (this.closed) {
+ return false;
+ }
+
+ boolean needsUpdate = false;
+ Block block = this.getBlock();
+ boolean isLit = block instanceof BlockCampfire && !((BlockCampfire) block).isExtinguished();
+ for (int slot = 0; slot < inventory.getSize(); slot++) {
+ Item item = inventory.getItem(slot);
+ if (item == null || item.getId() == BlockID.AIR || item.getCount() <= 0) {
+ burnTime[slot] = 0;
+ recipes[slot] = null;
+ } else if (!keepItem[slot]) {
+ CampfireRecipe recipe = recipes[slot];
+ if (recipe == null) {
+ recipe = this.server.getCraftingManager().matchCampfireRecipe(item);
+ if (recipe == null) {
+ inventory.setItem(slot, Item.get(0));
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ this.level.dropItem(add(random.nextFloat(), 0.5, random.nextFloat()), item);
+ burnTime[slot] = 0;
+ recipes[slot] = null;
+ continue;
+ } else {
+ burnTime[slot] = 600;
+ recipes[slot] = recipe;
+ }
+ }
+
+ int burnTimeLeft = burnTime[slot];
+ if (burnTimeLeft <= 0) {
+ Item product = Item.get(recipe.getResult().getId(), recipe.getResult().getDamage(), item.getCount());
+ CampfireSmeltEvent event = new CampfireSmeltEvent(this, item, product);
+ if (!event.isCancelled()) {
+ inventory.setItem(slot, Item.get(0));
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ this.level.dropItem(add(random.nextFloat(), 0.5, random.nextFloat()), event.getResult());
+ burnTime[slot] = 0;
+ recipes[slot] = null;
+ } else if (event.getKeepItem()) {
+ keepItem[slot] = true;
+ burnTime[slot] = 0;
+ recipes[slot] = null;
+ }
+ } else if (isLit) {
+ burnTime[slot]--;
+ needsUpdate = true;
+ } else {
+ burnTime[slot] = 600;
+ }
+ }
+ }
+
+ return needsUpdate;
+ }
+
+ public boolean getKeepItem(int slot) {
+ if (slot < 0 || slot >= keepItem.length) {
+ return false;
+ }
+ return keepItem[slot];
+ }
+
+ public void setKeepItem(int slot, boolean keep) {
+ if (slot < 0 || slot >= keepItem.length) {
+ return;
+ }
+ this.keepItem[slot] = keep;
+ }
+
+ @Override
+ public void saveNBT() {
+ super.saveNBT();
+
+ for (int i = 1; i <= burnTime.length; i++) {
+ Item item = inventory.getItem(i - 1);
+ if (item == null || item.getId() == BlockID.AIR || item.getCount() <= 0) {
+ namedTag.remove("Item"+i);
+ namedTag.putInt("ItemTime" + i, 0);
+ namedTag.remove("KeepItem"+i);
+ } else {
+ namedTag.putCompound("Item"+i, NBTIO.putItemHelper(item));
+ namedTag.putInt("ItemTime" + i, burnTime[i - 1]);
+ namedTag.putBoolean("KeepItem"+i, keepItem[i-1]);
+ }
+ }
+ }
+
+ public void setRecipe(int index, CampfireRecipe recipe) {
+ this.recipes[index] = recipe;
+ }
+
+ @Override
+ public void close() {
+ if (!closed) {
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
+ }
+ super.close();
+ }
+ }
+
+ @Override
+ public void onBreak() {
+ for (Item content : inventory.getContents().values()) {
+ level.dropItem(this, content);
+ }
+ inventory.clearAll();
+ }
+
+ @Override
+ public String getName() {
+ return "Campfire";
+ }
+
+ @Override
+ public void spawnTo(Player player) {
+ if (!this.closed) {
+ player.dataPacket(this.createSpawnPacket());
+ }
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag c = new CompoundTag()
+ .putString("id", BlockEntity.CAMPFIRE)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ for (int i = 1; i <= burnTime.length; i++) {
+ Item item = inventory.getItem(i - 1);
+ if (item == null || item.getId() == BlockID.AIR || item.getCount() <= 0) {
+ c.remove("Item"+i);
+ } else {
+ c.putCompound("Item"+i, NBTIO.putNetworkItemHelper(item));
+ }
+ }
+
+ return c;
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == BlockID.CAMPFIRE_BLOCK;
+ }
+
+ @Override
+ public int getSize() {
+ return 4;
+ }
+
+ @Override
+ public Item getItem(int index) {
+ if (index < 0 || index >= getSize()) {
+ return new ItemBlock(Block.get(0), 0, 0);
+ } else {
+ CompoundTag data = this.namedTag.getCompound("Item" + (index + 1));
+ return NBTIO.getItemHelper(data);
+ }
+ }
+
+ @Override
+ public void setItem(int index, Item item) {
+ if (index < 0 || index >= getSize()) {
+ return;
+ }
+
+ CompoundTag nbt = NBTIO.putItemHelper(item);
+ this.namedTag.putCompound("Item" + (index + 1), nbt);
+ }
+
+ @Override
+ public CampfireInventory getInventory() {
+ return inventory;
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityCauldron.java b/src/main/java/cn/nukkit/blockentity/BlockEntityCauldron.java
index e986c1578ac..0c3742b9e4c 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityCauldron.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityCauldron.java
@@ -1,29 +1,46 @@
package cn.nukkit.blockentity;
+import cn.nukkit.Player;
import cn.nukkit.block.Block;
+import cn.nukkit.level.GlobalBlockPalette;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.tag.CompoundTag;
-
+import cn.nukkit.network.protocol.UpdateBlockPacket;
import cn.nukkit.utils.BlockColor;
+import java.util.Collection;
+
/**
- * author: CreeperFace
+ * @author CreeperFace
* Nukkit Project
*/
public class BlockEntityCauldron extends BlockEntitySpawnable {
+ public static final int POTION_TYPE_EMPTY = 0xFFFF;
+ public static final int POTION_TYPE_NORMAL = 0;
+ public static final int POTION_TYPE_SPLASH = 1;
+ public static final int POTION_TYPE_LINGERING = 2;
+
public BlockEntityCauldron(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
}
@Override
protected void initBlockEntity() {
+ int potionId;
if (!namedTag.contains("PotionId")) {
namedTag.putShort("PotionId", 0xffff);
}
+ potionId = namedTag.getShort("PotionId");
- if (!namedTag.contains("SplashPotion")) {
- namedTag.putByte("SplashPotion", 0);
+ int potionType = (potionId & 0xFFFF) == 0xFFFF? POTION_TYPE_EMPTY : POTION_TYPE_NORMAL;
+ if (namedTag.getBoolean("SplashPotion")) {
+ potionType = POTION_TYPE_SPLASH;
+ namedTag.remove("SplashPotion");
+ }
+
+ if (!namedTag.contains("PotionType")) {
+ namedTag.putShort("PotionType", potionType);
}
super.initBlockEntity();
@@ -35,6 +52,7 @@ public int getPotionId() {
public void setPotionId(int potionId) {
namedTag.putShort("PotionId", potionId);
+ setDirty();
this.spawnToAll();
}
@@ -42,12 +60,22 @@ public boolean hasPotion() {
return getPotionId() != 0xffff;
}
+ public void setPotionType(int potionType) {
+ this.namedTag.putShort("PotionType", potionType & 0xFFFF);
+ setDirty();
+ }
+
+ public int getPotionType() {
+ return this.namedTag.getShort("PotionType") & 0xFFFF;
+ }
+
public boolean isSplashPotion() {
- return namedTag.getByte("SplashPotion") > 0;
+ return namedTag.getShort("PotionType") == POTION_TYPE_SPLASH;
}
public void setSplashPotion(boolean value) {
- namedTag.putByte("SplashPotion", value ? 1 : 0);
+ namedTag.putShort("PotionType", value ? 1 : 0);
+ setDirty();
}
public BlockColor getCustomColor() {
@@ -75,28 +103,53 @@ public void setCustomColor(BlockColor color) {
public void setCustomColor(int r, int g, int b) {
int color = (r << 16 | g << 8 | b) & 0xffffff;
- namedTag.putInt("CustomColor", color);
- spawnToAll();
+ if (color != namedTag.getInt("CustomColor")) {
+ namedTag.putInt("CustomColor", color);
+ Block block = getBlock();
+ Collection pl = level.getChunkPlayers(getChunkX(), getChunkZ()).values();
+ for (Player p : pl) {
+ UpdateBlockPacket air = new UpdateBlockPacket();
+ air.blockRuntimeId = GlobalBlockPalette.getOrCreateRuntimeId(0);
+ air.flags = UpdateBlockPacket.FLAG_ALL_PRIORITY;
+ air.x = (int) x;
+ air.y = (int) y;
+ air.z = (int) z;
+ UpdateBlockPacket self = (UpdateBlockPacket) air.clone();
+ self.blockRuntimeId = GlobalBlockPalette.getOrCreateRuntimeId(block.getId(), block.getDamage());
+ p.dataPacket(air);
+ p.dataPacket(self);
+ }
+
+ setDirty();
+
+ spawnToAll();
+ }
}
public void clearCustomColor() {
namedTag.remove("CustomColor");
+ setDirty();
spawnToAll();
}
@Override
public boolean isBlockEntityValid() {
- return getBlock().getId() == Block.CAULDRON_BLOCK;
+ int id = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
+ return id == Block.CAULDRON_BLOCK || id == Block.LAVA_CAULDRON;
}
@Override
public CompoundTag getSpawnCompound() {
- return new CompoundTag()
+ CompoundTag compoundTag = new CompoundTag()
.putString("id", BlockEntity.CAULDRON)
.putInt("x", (int) this.x)
.putInt("y", (int) this.y)
.putInt("z", (int) this.z)
.putShort("PotionId", namedTag.getShort("PotionId"))
- .putByte("SplashPotion", namedTag.getByte("SplashPotion"));
+ .putByte("PotionType", namedTag.getShort("PotionType"));
+ if (namedTag.contains("CustomColor")) {
+ compoundTag.putInt("CustomColor", namedTag.getInt("CustomColor"));
+ }
+ return compoundTag;
}
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java b/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java
index a7a1d58495e..b793c2a19ca 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityChest.java
@@ -15,63 +15,63 @@
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;
-import java.util.HashSet;
+import java.util.ArrayList;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockEntityChest extends BlockEntitySpawnable implements InventoryHolder, BlockEntityContainer, BlockEntityNameable {
protected ChestInventory inventory;
- protected DoubleChestInventory doubleInventory = null;
+ protected DoubleChestInventory doubleInventory;
public BlockEntityChest(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
}
- @Override
- protected void initBlockEntity() {
- this.inventory = new ChestInventory(this);
-
+ private void initInventory() {
if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) {
this.namedTag.putList(new ListTag("Items"));
}
+ ListTag list = (ListTag) this.namedTag.getList("Items");
- /* for (int i = 0; i < this.getSize(); i++) {
- this.inventory.setItem(i, this.getItem(i));
- } */
+ this.inventory = new ChestInventory(this);
- ListTag list = (ListTag) this.namedTag.getList("Items");
for (CompoundTag compound : list.getAll()) {
Item item = NBTIO.getItemHelper(compound);
- this.inventory.slots.put(compound.getByte("Slot"), item);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
}
-
- super.initBlockEntity();
}
@Override
public void close() {
- if (!closed) {
+ if (!this.closed && this.inventory != null) {
+ if (this.doubleInventory != null) {
+ for (Player player : new ArrayList<>(this.doubleInventory.getViewers())) {
+ player.removeWindow(this.doubleInventory);
+ }
- for (Player player : new HashSet<>(this.getInventory().getViewers())) {
- player.removeWindow(this.getInventory());
+ this.doubleInventory = null;
}
- for (Player player : new HashSet<>(this.getInventory().getViewers())) {
- player.removeWindow(this.getRealInventory());
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
}
- super.close();
}
+
+ super.close();
}
@Override
public void onBreak() {
- if (this.isPaired()) {
- unpair();
+ if (this.inventory == null) {
+ this.initInventory();
}
+ unpair();
for (Item content : inventory.getContents().values()) {
level.dropItem(this, content);
}
@@ -80,16 +80,20 @@ public void onBreak() {
@Override
public void saveNBT() {
- this.namedTag.putList(new ListTag("Items"));
- for (int index = 0; index < this.getSize(); index++) {
- this.setItem(index, this.inventory.getItem(index));
+ super.saveNBT();
+
+ if (this.inventory != null) {
+ this.namedTag.putList(new ListTag("Items"));
+ for (int index = 0; index < this.getSize(); index++) {
+ this.setItem(index, this.inventory.getItem(index));
+ }
}
}
@Override
public boolean isBlockEntityValid() {
- int blockID = this.getBlock().getId();
- return blockID == Block.CHEST || blockID == Block.TRAPPED_CHEST;
+ int id = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
+ return id == Block.CHEST || id == Block.TRAPPED_CHEST;
}
@Override
@@ -141,6 +145,9 @@ public void setItem(int index, Item item) {
@Override
public BaseInventory getInventory() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
if (this.doubleInventory == null && this.isPaired()) {
this.checkPairing();
}
@@ -149,6 +156,9 @@ public BaseInventory getInventory() {
}
public ChestInventory getRealInventory() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
return inventory;
}
@@ -191,7 +201,7 @@ public boolean hasName() {
@Override
public void setName(String name) {
- if (name == null || name.equals("")) {
+ if (name == null || name.isEmpty()) {
this.namedTag.remove("CustomName");
return;
}
@@ -205,7 +215,7 @@ public boolean isPaired() {
public BlockEntityChest getPair() {
if (this.isPaired()) {
- BlockEntity blockEntity = this.getLevel().getBlockEntityIfLoaded(new Vector3(this.namedTag.getInt("pairx"), this.y, this.namedTag.getInt("pairz")));
+ BlockEntity blockEntity = this.getLevel().getBlockEntityIfLoaded(this.chunk, new Vector3(this.namedTag.getInt("pairx"), this.y, this.namedTag.getInt("pairz")));
if (blockEntity instanceof BlockEntityChest) {
return (BlockEntityChest) blockEntity;
}
@@ -285,5 +295,4 @@ public CompoundTag getSpawnCompound() {
return c;
}
-
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityComparator.java b/src/main/java/cn/nukkit/blockentity/BlockEntityComparator.java
index 5409ec5245c..ed2fc7c796d 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityComparator.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityComparator.java
@@ -1,6 +1,6 @@
package cn.nukkit.blockentity;
-import cn.nukkit.block.BlockRedstoneComparator;
+import cn.nukkit.block.Block;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.tag.CompoundTag;
@@ -23,7 +23,8 @@ public BlockEntityComparator(FullChunk chunk, CompoundTag nbt) {
@Override
public boolean isBlockEntityValid() {
- return this.getLevelBlock() instanceof BlockRedstoneComparator;
+ int blockID = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
+ return blockID == Block.POWERED_COMPARATOR || blockID == Block.UNPOWERED_COMPARATOR;
}
public int getOutputSignal() {
@@ -31,7 +32,10 @@ public int getOutputSignal() {
}
public void setOutputSignal(int outputSignal) {
- this.outputSignal = outputSignal;
+ if (this.outputSignal != outputSignal) {
+ this.outputSignal = outputSignal;
+ setDirty();
+ }
}
@Override
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityContainer.java b/src/main/java/cn/nukkit/blockentity/BlockEntityContainer.java
index 79e21d0e4f9..e7082f26472 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityContainer.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityContainer.java
@@ -5,15 +5,14 @@
/**
* 表达一个容器的接口。
* An interface describes a container.
- *
- * {@code BlockEntityContainer}容器必须包含物品的{@code Item}对象。
- * A {@code BlockEntityContainer} must contain items as {@code Item} objects.
+ *
+ * {@code BlockEntityContainer}容器必须包含物品的{@code Item}对象。
+ * A {@code BlockEntityContainer} must contain items as {@code Item} objects.
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
* @see BlockEntityChest
* @see BlockEntityFurnace
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public interface BlockEntityContainer {
@@ -23,20 +22,18 @@ public interface BlockEntityContainer {
*
* @param index 这个物品的索引序号。
The index number of this item.
* @return 这个物品的 {@code Item}对象。
An {@code Item} object for this item.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
Item getItem(int index);
/**
* 把一个物品存储进容器。
* Sets or stores this item into this container.
- *
- * 注意:如果这个容器相应的索引序号已经有了物品,那么新存储的物品将会替换原有的物品。
- * Notice: If there is already an item for this index number, the new item being stored will REPLACE the old one.
+ *
+ * 注意:如果这个容器相应的索引序号已经有了物品,那么新存储的物品将会替换原有的物品。
+ * Notice: If there is already an item for this index number, the new item being stored will REPLACE the old one.
*
* @param index 这个物品的索引序号。
The index number of this item.
* @param item 描述这个物品的 {@code Item}对象。
The {@code Item} object that describes this item.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
void setItem(int index, Item item);
@@ -45,7 +42,6 @@ public interface BlockEntityContainer {
* Returns the max number of items that this container can contain.
*
* @return 最多能包含的物品数量。
The max number.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
int getSize();
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java b/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java
new file mode 100644
index 00000000000..7d571b38b8f
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityDispenser.java
@@ -0,0 +1,166 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.inventory.DispenserInventory;
+import cn.nukkit.inventory.InventoryHolder;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+
+import java.util.ArrayList;
+
+public class BlockEntityDispenser extends BlockEntitySpawnable implements InventoryHolder, BlockEntityContainer, BlockEntityNameable {
+
+ protected DispenserInventory inventory;
+
+ public BlockEntityDispenser(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ private void initInventory() {
+ if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) {
+ this.namedTag.putList(new ListTag("Items"));
+ }
+ ListTag list = (ListTag) this.namedTag.getList("Items");
+
+ this.inventory = new DispenserInventory(this);
+
+ for (CompoundTag compound : list.getAll()) {
+ Item item = NBTIO.getItemHelper(compound);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
+ }
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == BlockID.DISPENSER;
+ }
+
+ @Override
+ public String getName() {
+ return this.hasName() ? this.namedTag.getString("CustomName") : "Dispenser";
+ }
+
+ @Override
+ public boolean hasName() {
+ return this.namedTag.contains("CustomName");
+ }
+
+ @Override
+ public void setName(String name) {
+ if (name == null || name.isEmpty()) {
+ this.namedTag.remove("CustomName");
+ return;
+ }
+
+ this.namedTag.putString("CustomName", name);
+ }
+
+ @Override
+ public int getSize() {
+ return 9;
+ }
+
+ protected int getSlotIndex(int index) {
+ ListTag list = this.namedTag.getList("Items", CompoundTag.class);
+ for (int i = 0; i < list.size(); i++) {
+ if (list.get(i).getByte("Slot") == index) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public Item getItem(int index) {
+ int i = this.getSlotIndex(index);
+ if (i < 0) {
+ return new ItemBlock(Block.get(BlockID.AIR), 0, 0);
+ } else {
+ CompoundTag data = (CompoundTag) this.namedTag.getList("Items").get(i);
+ return NBTIO.getItemHelper(data);
+ }
+ }
+
+ @Override
+ public void setItem(int index, Item item) {
+ int i = this.getSlotIndex(index);
+
+ CompoundTag d = NBTIO.putItemHelper(item, index);
+
+ if (item.getId() == Item.AIR || item.getCount() <= 0) {
+ if (i >= 0) {
+ this.namedTag.getList("Items").getAll().remove(i);
+ }
+ } else if (i < 0) {
+ (this.namedTag.getList("Items", CompoundTag.class)).add(d);
+ } else {
+ (this.namedTag.getList("Items", CompoundTag.class)).add(i, d);
+ }
+ }
+
+ @Override
+ public void saveNBT() {
+ super.saveNBT();
+
+ if (this.inventory != null) {
+ this.namedTag.putList(new ListTag("Items"));
+ for (int index = 0; index < this.getSize(); index++) {
+ this.setItem(index, this.inventory.getItem(index));
+ }
+ }
+ }
+
+ @Override
+ public DispenserInventory getInventory() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
+ return this.inventory;
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag c = new CompoundTag()
+ .putString("id", BlockEntity.DISPENSER)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ if (this.hasName()) {
+ c.put("CustomName", this.namedTag.get("CustomName"));
+ }
+
+ return c;
+ }
+
+ @Override
+ public void close() {
+ if (!this.closed && this.inventory != null) {
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
+ }
+ }
+
+ super.close();
+ }
+
+ @Override
+ public void onBreak() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
+ for (Item content : inventory.getContents().values()) {
+ level.dropItem(this, content);
+ }
+ inventory.clearAll();
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java b/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java
new file mode 100644
index 00000000000..1dd8d944aa5
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityDropper.java
@@ -0,0 +1,166 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.inventory.DropperInventory;
+import cn.nukkit.inventory.InventoryHolder;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
+
+import java.util.ArrayList;
+
+public class BlockEntityDropper extends BlockEntitySpawnable implements InventoryHolder, BlockEntityContainer, BlockEntityNameable {
+
+ protected DropperInventory inventory;
+
+ public BlockEntityDropper(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ private void initInventory() {
+ if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) {
+ this.namedTag.putList(new ListTag("Items"));
+ }
+ ListTag list = (ListTag) this.namedTag.getList("Items");
+
+ this.inventory = new DropperInventory(this);
+
+ for (CompoundTag compound : list.getAll()) {
+ Item item = NBTIO.getItemHelper(compound);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
+ }
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.DROPPER;
+ }
+
+ @Override
+ public String getName() {
+ return this.hasName() ? this.namedTag.getString("CustomName") : "Dropper";
+ }
+
+ @Override
+ public boolean hasName() {
+ return this.namedTag.contains("CustomName");
+ }
+
+ @Override
+ public void setName(String name) {
+ if (name == null || name.isEmpty()) {
+ this.namedTag.remove("CustomName");
+ return;
+ }
+
+ this.namedTag.putString("CustomName", name);
+ }
+
+ @Override
+ public int getSize() {
+ return 9;
+ }
+
+ protected int getSlotIndex(int index) {
+ ListTag list = this.namedTag.getList("Items", CompoundTag.class);
+ for (int i = 0; i < list.size(); i++) {
+ if (list.get(i).getByte("Slot") == index) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public Item getItem(int index) {
+ int i = this.getSlotIndex(index);
+ if (i < 0) {
+ return new ItemBlock(Block.get(BlockID.AIR), 0, 0);
+ } else {
+ CompoundTag data = (CompoundTag) this.namedTag.getList("Items").get(i);
+ return NBTIO.getItemHelper(data);
+ }
+ }
+
+ @Override
+ public void setItem(int index, Item item) {
+ int i = this.getSlotIndex(index);
+
+ CompoundTag d = NBTIO.putItemHelper(item, index);
+
+ if (item.getId() == Item.AIR || item.getCount() <= 0) {
+ if (i >= 0) {
+ this.namedTag.getList("Items").getAll().remove(i);
+ }
+ } else if (i < 0) {
+ (this.namedTag.getList("Items", CompoundTag.class)).add(d);
+ } else {
+ (this.namedTag.getList("Items", CompoundTag.class)).add(i, d);
+ }
+ }
+
+ @Override
+ public void saveNBT() {
+ super.saveNBT();
+
+ if (this.inventory != null) {
+ this.namedTag.putList(new ListTag("Items"));
+ for (int index = 0; index < this.getSize(); index++) {
+ this.setItem(index, this.inventory.getItem(index));
+ }
+ }
+ }
+
+ @Override
+ public DropperInventory getInventory() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
+ return this.inventory;
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag c = new CompoundTag()
+ .putString("id", BlockEntity.DROPPER)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ if (this.hasName()) {
+ c.put("CustomName", this.namedTag.get("CustomName"));
+ }
+
+ return c;
+ }
+
+ @Override
+ public void close() {
+ if (!this.closed && this.inventory != null) {
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
+ }
+ }
+
+ super.close();
+ }
+
+ @Override
+ public void onBreak() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
+ for (Item content : inventory.getContents().values()) {
+ level.dropItem(this, content);
+ }
+ inventory.clearAll();
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityEnchantTable.java b/src/main/java/cn/nukkit/blockentity/BlockEntityEnchantTable.java
index 34cb11043e5..f6324121805 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityEnchantTable.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityEnchantTable.java
@@ -5,7 +5,7 @@
import cn.nukkit.nbt.tag.CompoundTag;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockEntityEnchantTable extends BlockEntitySpawnable implements BlockEntityNameable {
@@ -16,7 +16,7 @@ public BlockEntityEnchantTable(FullChunk chunk, CompoundTag nbt) {
@Override
public boolean isBlockEntityValid() {
- return getBlock().getId() == Block.ENCHANT_TABLE;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.ENCHANT_TABLE;
}
@Override
@@ -31,7 +31,9 @@ public boolean hasName() {
@Override
public void setName(String name) {
- if (name == null || name.equals("")) {
+ setDirty();
+
+ if (name == null || name.isEmpty()) {
this.namedTag.remove("CustomName");
return;
}
@@ -53,5 +55,4 @@ public CompoundTag getSpawnCompound() {
return c;
}
-
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityEnderChest.java b/src/main/java/cn/nukkit/blockentity/BlockEntityEnderChest.java
index ef834c937cd..a515f6bbaba 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityEnderChest.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityEnderChest.java
@@ -12,7 +12,7 @@ public BlockEntityEnderChest(FullChunk chunk, CompoundTag nbt) {
@Override
public boolean isBlockEntityValid() {
- return this.getBlock().getId() == Block.ENDER_CHEST;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.ENDER_CHEST;
}
@Override
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityFlowerPot.java b/src/main/java/cn/nukkit/blockentity/BlockEntityFlowerPot.java
index b83e63e07fb..1ee488b0f90 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityFlowerPot.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityFlowerPot.java
@@ -1,8 +1,10 @@
package cn.nukkit.blockentity;
import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.tag.CompoundTag;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
/**
* Created by Snake1999 on 2016/2/4.
@@ -33,8 +35,16 @@ protected void initBlockEntity() {
@Override
public boolean isBlockEntityValid() {
- int blockID = getBlock().getId();
- return blockID == Block.FLOWER_POT_BLOCK;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.FLOWER_POT_BLOCK;
+ }
+
+ private static final Int2ObjectOpenHashMap HAS_STRING_ITEM_OVERRIDE = new Int2ObjectOpenHashMap<>();
+
+ static {
+ HAS_STRING_ITEM_OVERRIDE.put(BlockID.CRIMSON_ROOTS, "minecraft:crimson_roots");
+ HAS_STRING_ITEM_OVERRIDE.put(BlockID.WARPED_ROOTS, "minecraft:warped_roots");
+ HAS_STRING_ITEM_OVERRIDE.put(BlockID.CRIMSON_FUNGUS, "minecraft:crimson_fungus");
+ HAS_STRING_ITEM_OVERRIDE.put(BlockID.WARPED_FUNGUS, "minecraft:warped_fungus");
}
@Override
@@ -47,10 +57,14 @@ public CompoundTag getSpawnCompound() {
int item = namedTag.getShort("item");
if (item != Block.AIR) {
- tag.putShort("item", this.namedTag.getShort("item"))
- .putInt("mData", this.namedTag.getInt("data"));
+ // Fix latest game versions not displaying legacy items correctly
+ if (HAS_STRING_ITEM_OVERRIDE.containsKey(item)) {
+ tag.putCompound("PlantBlock", new CompoundTag().putString("name", HAS_STRING_ITEM_OVERRIDE.get(item)));
+ } else {
+ tag.putShort("item", this.namedTag.getShort("item"))
+ .putInt("mData", this.namedTag.getInt("data"));
+ }
}
return tag;
}
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java b/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java
index 6bf2223a965..da4685be253 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityFurnace.java
@@ -1,22 +1,24 @@
package cn.nukkit.blockentity;
import cn.nukkit.Player;
-import cn.nukkit.block.*;
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
import cn.nukkit.event.inventory.FurnaceBurnEvent;
import cn.nukkit.event.inventory.FurnaceSmeltEvent;
-import cn.nukkit.inventory.FurnaceInventory;
-import cn.nukkit.inventory.FurnaceRecipe;
-import cn.nukkit.inventory.InventoryHolder;
+import cn.nukkit.inventory.*;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemBlock;
import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.math.NukkitMath;
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;
import cn.nukkit.network.protocol.ContainerSetDataPacket;
import cn.nukkit.network.protocol.LevelSoundEventPacket;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -30,8 +32,45 @@ public class BlockEntityFurnace extends BlockEntitySpawnable implements Inventor
protected int burnDuration;
protected int cookTime;
protected int maxTime;
-
- private int crackledTime;
+ protected int crackledTime;
+ protected double experience;
+
+ public static final Map FURNACE_XP = new HashMap<>();
+
+ static {
+ FURNACE_XP.put(Item.BAKED_POTATO, 0.35d);
+ FURNACE_XP.put(Item.DRIED_KELP, 0.1d);
+ FURNACE_XP.put(Item.STEAK, 0.35d);
+ FURNACE_XP.put(Item.COOKED_PORKCHOP, 0.35d);
+ FURNACE_XP.put(Item.COOKED_MUTTON, 0.35d);
+ FURNACE_XP.put(Item.COOKED_CHICKEN, 0.35d);
+ FURNACE_XP.put(Item.COOKED_RABBIT, 0.35d);
+ FURNACE_XP.put(Item.COOKED_FISH, 0.35d);
+ FURNACE_XP.put(Item.COOKED_SALMON, 0.35d);
+
+ FURNACE_XP.put(Item.REDSTONE_DUST, 0.3d);
+ FURNACE_XP.put(Item.COAL, 0.1d);
+ FURNACE_XP.put(Item.EMERALD, 1d);
+ FURNACE_XP.put(Item.DYE, 0.2d); // Lapis & Cactus
+ FURNACE_XP.put(Item.DIAMOND, 1d);
+ FURNACE_XP.put(Item.NETHER_QUARTZ, 0.2d);
+ FURNACE_XP.put(Item.IRON_INGOT, 0.7d);
+ FURNACE_XP.put(Item.COPPER_INGOT, 0.7d);
+ FURNACE_XP.put(Item.GOLD_INGOT, 1d);
+ FURNACE_XP.put(Item.NETHERITE_SCRAP, 1d);
+ FURNACE_XP.put(Item.IRON_NUGGET, 0.1d);
+ FURNACE_XP.put(Item.GOLD_NUGGET, 0.1d);
+
+ FURNACE_XP.put(Item.STONE, 0.1d);
+ FURNACE_XP.put(Item.TERRACOTTA, 0.35d);
+ FURNACE_XP.put(Item.GLASS, 0.1d);
+ FURNACE_XP.put(Item.SPONGE, 0.15d);
+ FURNACE_XP.put(Item.POPPED_CHORUS_FRUIT, 0.1d);
+ FURNACE_XP.put(Item.BRICK, 0.3d);
+ FURNACE_XP.put(Item.NETHER_BRICK, 0.1d);
+
+ FURNACE_XP.put(255 - Item.SMOOTH_BASALT, 0.1d);
+ }
public BlockEntityFurnace(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
@@ -39,14 +78,24 @@ public BlockEntityFurnace(FullChunk chunk, CompoundTag nbt) {
@Override
protected void initBlockEntity() {
- this.inventory = new FurnaceInventory(this);
+ if (this instanceof BlockEntityBlastFurnace) {
+ this.inventory = new BlastFurnaceInventory((BlockEntityBlastFurnace) this);
+ } else if (this instanceof BlockEntitySmoker) {
+ this.inventory = new SmokerInventory((BlockEntitySmoker) this);
+ } else {
+ this.inventory = new FurnaceInventory(this);
+ }
if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) {
this.namedTag.putList(new ListTag("Items"));
}
- for (int i = 0; i < this.getSize(); i++) {
- this.inventory.setItem(i, this.getItem(i));
+ ListTag list = (ListTag) this.namedTag.getList("Items");
+ for (CompoundTag compound : list.getAll()) {
+ Item item = NBTIO.getItemHelper(compound);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
}
if (!this.namedTag.contains("BurnTime") || this.namedTag.getShort("BurnTime") < 0) {
@@ -79,6 +128,12 @@ protected void initBlockEntity() {
this.namedTag.remove("BurnTicks");
}
+ if (this.namedTag.contains("Experience") && this.namedTag.getDouble("Experience") > 0) {
+ this.experience = this.namedTag.getDouble("Experience");
+ } else {
+ this.experience = 0;
+ }
+
if (burnTime > 0) {
this.scheduleUpdate();
}
@@ -98,7 +153,7 @@ public boolean hasName() {
@Override
public void setName(String name) {
- if (name == null || name.equals("")) {
+ if (name == null || name.isEmpty()) {
this.namedTag.remove("CustomName");
return;
}
@@ -109,9 +164,10 @@ public void setName(String name) {
@Override
public void close() {
if (!closed) {
- for (Player player : new HashSet<>(this.getInventory().getViewers())) {
- player.removeWindow(this.getInventory());
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
}
+
super.close();
}
}
@@ -121,11 +177,12 @@ public void onBreak() {
for (Item content : inventory.getContents().values()) {
level.dropItem(this, content);
}
- this.inventory.clearAll();
+ inventory.clearAll();
}
@Override
public void saveNBT() {
+ super.saveNBT();
this.namedTag.putList(new ListTag("Items"));
for (int index = 0; index < this.getSize(); index++) {
this.setItem(index, this.inventory.getItem(index));
@@ -135,11 +192,12 @@ public void saveNBT() {
this.namedTag.putShort("BurnTime", burnTime);
this.namedTag.putShort("BurnDuration", burnDuration);
this.namedTag.putShort("MaxTime", maxTime);
+ this.namedTag.putDouble("Experience", experience);
}
@Override
public boolean isBlockEntityValid() {
- int blockID = getBlock().getId();
+ int blockID = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
return blockID == Block.FURNACE || blockID == Block.BURNING_FURNACE;
}
@@ -194,7 +252,9 @@ public FurnaceInventory getInventory() {
protected void checkFuel(Item fuel) {
FurnaceBurnEvent ev = new FurnaceBurnEvent(this, fuel, fuel.getFuelTime() == null ? 0 : fuel.getFuelTime());
+
this.server.getPluginManager().callEvent(ev);
+
if (ev.isCancelled()) {
return;
}
@@ -202,8 +262,14 @@ protected void checkFuel(Item fuel) {
maxTime = ev.getBurnTime();
burnTime = ev.getBurnTime();
burnDuration = 0;
- if (this.getBlock().getId() == Item.FURNACE) {
- this.getLevel().setBlock(this, Block.get(BlockID.BURNING_FURNACE, this.getBlock().getDamage()), true);
+
+ Block block = this.level.getBlock(this.chunk, (int) x, (int) y, (int) z, true);
+ if (block.getId() == Item.FURNACE) {
+ this.getLevel().setBlock(this, Block.get(BlockID.BURNING_FURNACE, block.getDamage()), true);
+ } else if (block.getId() == Item.SMOKER) {
+ this.getLevel().setBlock(this, Block.get(BlockID.LIT_SMOKER, block.getDamage()), true);
+ } else if (block.getId() == Item.BLAST_FURNACE) {
+ this.getLevel().setBlock(this, Block.get(BlockID.LIT_BLAST_FURNACE, block.getDamage()), true);
}
if (burnTime > 0 && ev.isBurning()) {
@@ -226,17 +292,15 @@ public boolean onUpdate() {
return false;
}
- this.timing.startTiming();
-
boolean ret = false;
- Item fuel = this.inventory.getFuel();
Item raw = this.inventory.getSmelting();
Item product = this.inventory.getResult();
FurnaceRecipe smelt = this.server.getCraftingManager().matchFurnaceRecipe(raw);
- boolean canSmelt = (smelt != null && raw.getCount() > 0 && ((smelt.getResult().equals(product, true) && product.getCount() < product.getMaxStackSize()) || product.getId() == Item.AIR));
+ boolean canSmelt = (smelt != null && raw.getCount() > 0 && ((smelt.getResult().equals(product) && product.getCount() < product.getMaxStackSize()) || product.getId() == Item.AIR));
- if (burnTime <= 0 && canSmelt && fuel.getFuelTime() != null && fuel.getCount() > 0) {
- this.checkFuel(fuel);
+ Item fuel;
+ if (burnTime <= 0 && canSmelt && (fuel = this.inventory.getItemFast(1)).getFuelTime() != null && fuel.getCount() > 0) {
+ this.checkFuel(fuel.clone());
}
if (burnTime > 0) {
@@ -244,19 +308,20 @@ public boolean onUpdate() {
burnDuration = (int) Math.ceil((float) burnTime / maxTime * 200);
if (this.crackledTime-- <= 0) {
- this.crackledTime = ThreadLocalRandom.current().nextInt(20, 100);
+ this.crackledTime = ThreadLocalRandom.current().nextInt(30, 110);
this.getLevel().addLevelSoundEvent(this.add(0.5, 0.5, 0.5), LevelSoundEventPacket.SOUND_BLOCK_FURNACE_LIT);
}
if (smelt != null && canSmelt) {
cookTime++;
if (cookTime >= 200) {
- product = Item.get(smelt.getResult().getId(), smelt.getResult().getDamage(), product.getCount() + 1);
+ product = Item.get(smelt.getResult().getId(), smelt.getResult().getDamage(), product.isNull() ? 1 : product.getCount() + 1);
FurnaceSmeltEvent ev = new FurnaceSmeltEvent(this, raw, product);
this.server.getPluginManager().callEvent(ev);
if (!ev.isCancelled()) {
this.inventory.setResult(ev.getResult());
+ this.experience += FURNACE_XP.getOrDefault(ev.getResult().getId(), 0d);
raw.setCount(raw.getCount() - 1);
if (raw.getCount() == 0) {
raw = new ItemBlock(Block.get(BlockID.AIR), 0, 0);
@@ -275,22 +340,28 @@ public boolean onUpdate() {
}
ret = true;
} else {
- if (this.getBlock().getId() == Item.BURNING_FURNACE) {
- this.getLevel().setBlock(this, Block.get(BlockID.FURNACE, this.getBlock().getDamage()), true);
+ Block block = this.level.getBlock(this.chunk, (int) x, (int) y, (int) z, true);
+ if (block.getId() == Item.BURNING_FURNACE) {
+ this.getLevel().setBlock(this, Block.get(BlockID.FURNACE, block.getDamage()), true);
}
burnTime = 0;
cookTime = 0;
burnDuration = 0;
- this.crackledTime = 0;
+ crackledTime = 0;
}
- for (Player player : this.getInventory().getViewers()) {
- int windowId = player.getWindowId(this.getInventory());
+ sendPacket();
+
+ return ret;
+ }
+
+ protected void sendPacket() {
+ for (Player player : this.inventory.getViewers()) {
+ int windowId = player.getWindowId(this.inventory);
if (windowId > 0) {
ContainerSetDataPacket pk = new ContainerSetDataPacket();
pk.windowId = windowId;
pk.property = ContainerSetDataPacket.PROPERTY_FURNACE_TICK_COUNT;
-
pk.value = cookTime;
player.dataPacket(pk);
@@ -301,12 +372,6 @@ public boolean onUpdate() {
player.dataPacket(pk);
}
}
-
- this.lastUpdate = System.currentTimeMillis();
-
- this.timing.stopTiming();
-
- return ret;
}
@Override
@@ -358,4 +423,20 @@ public int getMaxTime() {
public void setMaxTime(int maxTime) {
this.maxTime = maxTime;
}
+
+ public double getExperience() {
+ return this.experience;
+ }
+
+ public void setExperience(double experience) {
+ this.experience = experience;
+ }
+
+ public void releaseExperience() {
+ int experience = NukkitMath.floorDouble(this.experience);
+ if (experience >= 1) {
+ this.experience = 0;
+ this.level.dropExpOrb(this, experience);
+ }
+ }
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java
index db34f1f3bf9..245f0267a7e 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityHopper.java
@@ -2,6 +2,7 @@
import cn.nukkit.Player;
import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockComposter;
import cn.nukkit.block.BlockID;
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.item.EntityItem;
@@ -16,8 +17,9 @@
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;
+import lombok.Setter;
-import java.util.HashSet;
+import java.util.ArrayList;
/**
* Created by CreeperFace on 8.5.2017.
@@ -30,6 +32,11 @@ public class BlockEntityHopper extends BlockEntitySpawnable implements Inventory
private AxisAlignedBB pickupArea;
+ @Setter
+ private InventoryHolder minecartPickupInventory;
+ @Setter
+ private InventoryHolder minecartPushInventory;
+
public BlockEntityHopper(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
}
@@ -48,8 +55,12 @@ protected void initBlockEntity() {
this.namedTag.putList(new ListTag("Items"));
}
- 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);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
}
this.pickupArea = new SimpleAxisAlignedBB(this.x, this.y, this.z, this.x + 1, this.y + 2, this.z + 1);
@@ -61,7 +72,7 @@ protected void initBlockEntity() {
@Override
public boolean isBlockEntityValid() {
- return this.level.getBlockIdAt(this.getFloorX(), this.getFloorY(), this.getFloorZ()) == Block.HOPPER_BLOCK;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.HOPPER_BLOCK;
}
@Override
@@ -76,7 +87,7 @@ public boolean hasName() {
@Override
public void setName(String name) {
- if (name == null || name.equals("")) {
+ if (name == null || name.isEmpty()) {
this.namedTag.remove("CustomName");
return;
}
@@ -138,6 +149,7 @@ public void setItem(int index, Item item) {
@Override
public void saveNBT() {
+ super.saveNBT();
this.namedTag.putList(new ListTag("Items"));
for (int index = 0; index < this.getSize(); index++) {
this.setItem(index, this.inventory.getItem(index));
@@ -158,21 +170,23 @@ public boolean onUpdate() {
}
this.transferCooldown--;
-
- if (this.level.isBlockPowered(getBlock())) {
- return true;
- }
if (!this.isOnTransferCooldown()) {
- BlockEntity blockEntity = this.level.getBlockEntity(this.up());
+ if (this.level.isBlockPowered(this.chunk, this)) {
+ return true;
+ }
+
+ // Note: Initial inventory full/empty checks are moved here from pull/push methods
- boolean changed = pushItems();
+ boolean changed = !this.inventory.slots.isEmpty() && (pushItems() || pushItemsToMinecart());
- if (!changed) {
- if (!(blockEntity instanceof BlockEntityContainer)) {
- changed = pickupItems();
+ if (!changed && !this.inventory.isFull()) {
+ BlockEntity blockEntity = this.level.getBlockEntity(this.chunk, this.up());
+ Block block = null;
+ if (blockEntity instanceof BlockEntityContainer || (block = this.level.getBlock(this.chunk, this.getFloorX(), this.getFloorY() + 1, this.getFloorZ(), false)) instanceof BlockComposter) {
+ changed = pullItems(blockEntity, block);
} else {
- changed = pullItems();
+ changed = pullItemsFromMinecart() || pickupItems();
}
}
@@ -186,13 +200,93 @@ public boolean onUpdate() {
return true;
}
- public boolean pullItems() {
- if (this.inventory.isFull()) {
- return false;
+ private boolean pullItemsFromMinecart() {
+ if (this.minecartPickupInventory != null) {
+ Inventory inv = this.minecartPickupInventory.getInventory();
+
+ for (int i = 0; i < inv.getSize(); i++) {
+ Item item = inv.getItem(i);
+
+ if (!item.isNull()) {
+ Item itemToAdd = item.clone();
+ itemToAdd.count = 1;
+ if (!this.inventory.canAddItem(itemToAdd)) {
+ continue;
+ }
+
+ InventoryMoveItemEvent ev = new InventoryMoveItemEvent(inv, this.inventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
+ this.server.getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ continue;
+ }
+
+ Item[] items = this.inventory.addItem(itemToAdd);
+ if (items.length >= 1) {
+ continue;
+ }
+
+ item.count--;
+ inv.setItem(i, item);
+
+ this.minecartPickupInventory = null;
+ return true;
+ }
+ }
}
- BlockEntity blockEntity = this.level.getBlockEntity(this.up());
- //Fix for furnace outputs
+ return false;
+ }
+
+ private boolean pushItemsToMinecart() {
+ if (this.minecartPushInventory != null) {
+ Inventory holderInventory = this.minecartPushInventory.getInventory();
+
+ if (holderInventory.isFull()) {
+ return false;
+ }
+
+ for (int i = 0; i < this.inventory.getSize(); i++) {
+ Item item = this.inventory.getItem(i);
+
+ if (!item.isNull()) {
+ Item itemToAdd = item.clone();
+ itemToAdd.setCount(1);
+
+ if (!holderInventory.canAddItem(itemToAdd)) {
+ continue;
+ }
+
+ InventoryMoveItemEvent ev = new InventoryMoveItemEvent(this.inventory, holderInventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
+ this.server.getPluginManager().callEvent(ev);
+
+ if (ev.isCancelled()) {
+ continue;
+ }
+
+ Item[] items = holderInventory.addItem(itemToAdd);
+ if (items.length > 0) {
+ continue;
+ }
+
+ item.count--;
+ this.inventory.setItem(i, item);
+
+ this.minecartPushInventory = null;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public boolean pullItems() {
+ return this.pullItems(
+ this.level.getBlockEntity(this.chunk, this.up()),
+ this.level.getBlock(this.chunk, this.getFloorX(), this.getFloorY() + 1, this.getFloorZ(), false));
+ }
+
+ private boolean pullItems(BlockEntity blockEntity, Block block) {
if (blockEntity instanceof BlockEntityFurnace) {
FurnaceInventory inv = ((BlockEntityFurnace) blockEntity).getInventory();
Item item = inv.getResult();
@@ -214,7 +308,7 @@ public boolean pullItems() {
Item[] items = this.inventory.addItem(itemToAdd);
- if (items.length <= 0) {
+ if (items.length == 0) {
item.count--;
inv.setResult(item);
return true;
@@ -253,15 +347,29 @@ public boolean pullItems() {
return true;
}
}
+ } else if (block instanceof BlockComposter) {
+ BlockComposter composter = (BlockComposter) block;
+ Item item = composter.empty();
+ if (item == null || item.isNull()) {
+ return false;
+ }
+ Item itemToAdd = item.clone();
+ itemToAdd.setCount(1);
+ if (!this.inventory.canAddItem(itemToAdd)) {
+ return false;
+ }
+ InventoryMoveItemEvent ev = new InventoryMoveItemEvent(null, this.inventory, this, item, InventoryMoveItemEvent.Action.PICKUP);
+ this.server.getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
+ return false;
+ }
+ Item[] items = inventory.addItem(itemToAdd);
+ return items.length < 1;
}
return false;
}
public boolean pickupItems() {
- if (this.inventory.isFull()) {
- return false;
- }
-
boolean pickedUpItem = false;
for (Entity entity : this.level.getCollidingEntities(this.pickupArea)) {
@@ -303,15 +411,14 @@ public boolean pickupItems() {
}
}
- //TODO: check for minecart
return pickedUpItem;
}
@Override
public void close() {
if (!closed) {
- for (Player player : new HashSet<>(this.getInventory().getViewers())) {
- player.removeWindow(this.getInventory());
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
}
super.close();
}
@@ -322,25 +429,21 @@ public void onBreak() {
for (Item content : inventory.getContents().values()) {
level.dropItem(this, content);
}
- this.inventory.clearAll();
+ inventory.clearAll();
}
public boolean pushItems() {
- if (this.inventory.isEmpty()) {
- return false;
- }
+ int blockData = this.level.getBlockDataAt(this.chunk, (int) x, (int) y, (int) z, Block.LAYER_NORMAL);
+ BlockEntity be = this.level.getBlockEntity(this.chunk, this.getSide(BlockFace.fromIndex(blockData)));
- BlockEntity be = this.level.getBlockEntity(this.getSide(BlockFace.fromIndex(this.level.getBlockDataAt(this.getFloorX(), this.getFloorY(), this.getFloorZ()))));
-
- if (be instanceof BlockEntityHopper && this.getBlock().getDamage() == 0 || !(be instanceof InventoryHolder))
+ if (!(be instanceof InventoryHolder) || (be instanceof BlockEntityHopper && blockData == 0)) {
return false;
+ }
InventoryMoveItemEvent event;
- //Fix for furnace inputs
if (be instanceof BlockEntityFurnace) {
- BlockEntityFurnace furnace = (BlockEntityFurnace) be;
- FurnaceInventory inventory = furnace.getInventory();
+ FurnaceInventory inventory = ((BlockEntityFurnace) be).getInventory();
if (inventory.isFull()) {
return false;
}
@@ -353,8 +456,7 @@ public boolean pushItems() {
Item itemToAdd = item.clone();
itemToAdd.setCount(1);
- //Check direction of hopper
- if (this.getBlock().getDamage() == 0) {
+ if (blockData == 0) {
Item smelting = inventory.getSmelting();
if (smelting.isNull()) {
event = new InventoryMoveItemEvent(this.inventory, inventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
@@ -365,7 +467,7 @@ public boolean pushItems() {
item.count--;
pushedItem = true;
}
- } else if (inventory.getSmelting().getId() == itemToAdd.getId() && inventory.getSmelting().getDamage() == itemToAdd.getDamage() && smelting.count < smelting.getMaxStackSize()) {
+ } else if (smelting.getId() == itemToAdd.getId() && smelting.getDamage() == itemToAdd.getDamage() && smelting.count < smelting.getMaxStackSize()) {
event = new InventoryMoveItemEvent(this.inventory, inventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
this.server.getPluginManager().callEvent(event);
@@ -402,11 +504,89 @@ public boolean pushItems() {
if (pushedItem) {
this.inventory.setItem(i, item);
+ return true;
}
}
}
- return pushedItem;
+ return false;
+ } else if (be instanceof BlockEntityBrewingStand) {
+ BrewingInventory inventory = ((BlockEntityBrewingStand) be).getInventory();
+ if (inventory.isFull()) {
+ return false;
+ }
+
+ boolean pushedItem = false;
+
+ for (int i = 0; i < this.inventory.getSize(); i++) {
+ Item item = this.inventory.getItem(i);
+ if (!item.isNull()) {
+ Item itemToAdd = item.clone();
+ itemToAdd.setCount(1);
+
+ boolean isPotion = itemToAdd.getId() == Item.GLASS_BOTTLE || itemToAdd.getId() == Item.POTION || itemToAdd.getId() == Item.SPLASH_POTION;
+
+ if (blockData == 0) { // Hopper is above the brewing stand
+ if (isPotion) {
+ continue; // No potions as ingredient
+ }
+
+ Item ingredient = inventory.getIngredient();
+ if (ingredient.isNull()) {
+ event = new InventoryMoveItemEvent(this.inventory, inventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
+ this.server.getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ inventory.setIngredient(itemToAdd);
+ pushedItem = true;
+ }
+ } else if (ingredient.getId() == itemToAdd.getId() && ingredient.getDamage() == itemToAdd.getDamage() && ingredient.count < ingredient.getMaxStackSize()) {
+ event = new InventoryMoveItemEvent(this.inventory, inventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
+ this.server.getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ ingredient.count++;
+ inventory.setIngredient(ingredient);
+ pushedItem = true;
+ }
+ }
+ } else if (itemToAdd.getId() == Item.BLAZE_POWDER) { // Only blaze powder to fuel slot
+ Item fuel = inventory.getFuel();
+ if (fuel.isNull()) {
+ event = new InventoryMoveItemEvent(this.inventory, inventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
+ this.server.getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ inventory.setFuel(itemToAdd);
+ pushedItem = true;
+ }
+ } else if (fuel.getId() == itemToAdd.getId() && fuel.getDamage() == itemToAdd.getDamage() && fuel.count < fuel.getMaxStackSize()) {
+ event = new InventoryMoveItemEvent(this.inventory, inventory, this, itemToAdd, InventoryMoveItemEvent.Action.SLOT_CHANGE);
+ this.server.getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ fuel.count++;
+ inventory.setFuel(fuel);
+ pushedItem = true;
+ }
+ }
+ } else if (isPotion) { // Only bottles/potions to bottle slots
+ if (inventory.addItem(itemToAdd).length > 0) { // BrewingInventory overrides addItemSize to check the correct slots
+ continue;
+ } else {
+ pushedItem = true;
+ }
+ }
+
+ if (pushedItem) {
+ item.count--;
+ this.inventory.setItem(i, item);
+ return true;
+ }
+ }
+ }
+
+ return false;
} else {
Inventory inventory = ((InventoryHolder) be).getInventory();
@@ -445,7 +625,6 @@ public boolean pushItems() {
}
}
- //TODO: check for minecart
return false;
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java b/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java
index 5a6b34700a5..1327d244dbc 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityItemFrame.java
@@ -10,6 +10,7 @@
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.ListTag;
import cn.nukkit.network.protocol.LevelEventPacket;
import java.util.concurrent.ThreadLocalRandom;
@@ -19,6 +20,8 @@
*/
public class BlockEntityItemFrame extends BlockEntitySpawnable {
+ private Item item_;
+
public BlockEntityItemFrame(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
}
@@ -26,11 +29,13 @@ public BlockEntityItemFrame(FullChunk chunk, CompoundTag nbt) {
@Override
protected void initBlockEntity() {
if (!namedTag.contains("Item")) {
- namedTag.putCompound("Item", NBTIO.putItemHelper(new ItemBlock(Block.get(BlockID.AIR))));
+ namedTag.putCompound("Item", NBTIO.putItemHelper(item_ = new ItemBlock(Block.get(BlockID.AIR))));
}
+
if (!namedTag.contains("ItemRotation")) {
namedTag.putByte("ItemRotation", 0);
}
+
if (!namedTag.contains("ItemDropChance")) {
namedTag.putFloat("ItemDropChance", 1.0f);
}
@@ -47,7 +52,7 @@ public String getName() {
@Override
public boolean isBlockEntityValid() {
- return this.getBlock().getId() == Block.ITEM_FRAME_BLOCK;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.ITEM_FRAME_BLOCK;
}
public int getItemRotation() {
@@ -61,8 +66,11 @@ public void setItemRotation(int itemRotation) {
}
public Item getItem() {
- CompoundTag NBTTag = this.namedTag.getCompound("Item");
- return NBTIO.getItemHelper(NBTTag);
+ if (item_ == null) {
+ CompoundTag NBTTag = this.namedTag.getCompound("Item");
+ item_ = NBTIO.getItemHelper(NBTTag);
+ }
+ return item_;
}
public void setItem(Item item) {
@@ -70,6 +78,7 @@ public void setItem(Item item) {
}
public void setItem(Item item, boolean setChanged) {
+ item_ = null;
this.namedTag.putCompound("Item", NBTIO.putItemHelper(item));
if (setChanged) {
this.setDirty();
@@ -84,11 +93,13 @@ public float getItemDropChance() {
public void setItemDropChance(float chance) {
this.namedTag.putFloat("ItemDropChance", chance);
+ super.setDirty(); // No need to spawnToAll
}
+ @Override
public void setDirty() {
- this.spawnToAll();
super.setDirty();
+ this.spawnToAll();
}
@Override
@@ -96,19 +107,50 @@ public CompoundTag getSpawnCompound() {
if (!this.namedTag.contains("Item")) {
this.setItem(new ItemBlock(Block.get(BlockID.AIR)), false);
}
- CompoundTag item = namedTag.getCompound("Item").copy();
- item.setName("Item");
+ CompoundTag itemOriginal = namedTag.getCompound("Item");
+
CompoundTag tag = new CompoundTag()
.putString("id", BlockEntity.ITEM_FRAME)
.putInt("x", (int) this.x)
.putInt("y", (int) this.y)
.putInt("z", (int) this.z);
- int itemId = item.getShort("id");
+ int itemId = itemOriginal.getShort("id");
if (itemId != Item.AIR) {
- String identifier = RuntimeItems.getMapping().toRuntime(itemId, item.getShort("Damage")).getIdentifier();
- item.putString("Name", identifier);
- item.remove("id");
+ CompoundTag item;
+ if (itemId == Item.MAP) {
+ item = itemOriginal.copy();
+ item.setName("Item");
+
+ String identifier = RuntimeItems.getMapping().toRuntime(itemId, itemOriginal.getShort("Damage")).getIdentifier();
+ item.putString("Name", identifier);
+ item.remove("id");
+
+ item.getCompound("tag").remove("Colors");
+ } else {
+ // Instead of copying the item's whole nbt just send the data necessary to display the item
+ item = new CompoundTag("Item")
+ .putByte("Count", itemOriginal.getByte("Count"))
+ .putShort("Damage", itemOriginal.getShort("Damage"));
+
+ String identifier = RuntimeItems.getMapping().toRuntime(itemId, itemOriginal.getShort("Damage")).getIdentifier();
+ item.putString("Name", identifier);
+
+ if (itemOriginal.contains("tag")) {
+ CompoundTag oldTag = itemOriginal.getCompound("tag");
+ CompoundTag newTag = new CompoundTag();
+
+ if (oldTag.contains("ench")) {
+ newTag.putList(new ListTag<>("ench"));
+ }
+
+ if (oldTag.contains("display") && oldTag.get("display") instanceof CompoundTag) {
+ newTag.putCompound("display", new CompoundTag("display").putString("Name", ((CompoundTag) oldTag.get("display")).getString("Name")));
+ }
+
+ item.put("tag", newTag);
+ }
+ }
tag.putCompound("Item", item)
.putByte("ItemRotation", this.getItemRotation());
@@ -131,12 +173,11 @@ public boolean dropItem(Player player) {
return true;
}
}
-
+ this.setItem(Item.get(Item.AIR));
+ this.setItemRotation(0);
if (this.getItemDropChance() > ThreadLocalRandom.current().nextFloat()) {
this.level.dropItem(this.add(0.5, 0, 0.5), item);
}
- this.setItem(Item.get(Item.AIR));
- this.setItemRotation(0);
this.level.addLevelEvent(this, LevelEventPacket.EVENT_SOUND_ITEM_FRAME_ITEM_REMOVED);
return true;
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityJukebox.java b/src/main/java/cn/nukkit/blockentity/BlockEntityJukebox.java
index ce7e18ee4ff..eb3b7938f2c 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityJukebox.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityJukebox.java
@@ -34,12 +34,13 @@ protected void initBlockEntity() {
@Override
public boolean isBlockEntityValid() {
- return this.getLevel().getBlockIdAt(getFloorX(), getFloorY(), getFloorZ()) == Block.JUKEBOX;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.JUKEBOX;
}
public void setRecordItem(Item recordItem) {
Objects.requireNonNull(recordItem, "Record item cannot be null");
this.recordItem = recordItem;
+ setDirty();
}
public Item getRecordItem() {
@@ -110,6 +111,7 @@ public void dropItem() {
stop();
this.level.dropItem(this.up(), this.recordItem);
this.recordItem = Item.get(0);
+ setDirty();
}
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityLectern.java b/src/main/java/cn/nukkit/blockentity/BlockEntityLectern.java
new file mode 100644
index 00000000000..1d5517f41c2
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityLectern.java
@@ -0,0 +1,133 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.block.BlockID;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemID;
+import cn.nukkit.level.format.Chunk;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.IntTag;
+
+public class BlockEntityLectern extends BlockEntitySpawnable {
+
+ private int totalPages;
+
+ public BlockEntityLectern(Chunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ @Override
+ protected void initBlockEntity() {
+ if (!(this.namedTag.get("book") instanceof CompoundTag)) {
+ this.namedTag.remove("book");
+ }
+
+ if (!(this.namedTag.get("page") instanceof IntTag)) {
+ this.namedTag.remove("page");
+ }
+
+ updateTotalPages(false);
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag c = new CompoundTag()
+ .putString("id", BlockEntity.LECTERN)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+
+ Item book = getBook();
+ if (book.getId() != BlockID.AIR) {
+ c.putCompound("book", NBTIO.putNetworkItemHelper(book));
+ c.putBoolean("hasBook", true);
+ c.putInt("page", getRawPage());
+ c.putInt("totalPages", totalPages);
+ } else {
+ c.putBoolean("hasBook", false);
+ }
+
+ return c;
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == BlockID.LECTERN;
+ }
+
+ @Override
+ public void onBreak() {
+ Item book = getBook();
+ if (book.getId() != BlockID.AIR) {
+ level.dropItem(this, book);
+ }
+ this.namedTag.remove("book");
+ }
+
+ public boolean hasBook() {
+ return this.namedTag.contains("book") && this.namedTag.get("book") instanceof CompoundTag;
+ }
+
+ public Item getBook() {
+ if (!hasBook()) {
+ return Item.get(BlockID.AIR, 0, 0);
+ } else {
+ return NBTIO.getItemHelper(this.namedTag.getCompound("book"));
+ }
+ }
+
+ public void setBook(Item item) {
+ if (item.getId() == ItemID.WRITTEN_BOOK || item.getId() == ItemID.BOOK_AND_QUILL) {
+ this.namedTag.putCompound("book", NBTIO.putItemHelper(item));
+ } else {
+ this.namedTag.remove("book");
+ this.namedTag.remove("page");
+ }
+
+ updateTotalPages(true);
+ setDirty();
+ }
+
+ public int getLeftPage() {
+ return (getRawPage() * 2) + 1;
+ }
+
+ public int getRightPage() {
+ return getLeftPage() + 1;
+ }
+
+ public void setLeftPage(int newLeftPage) {
+ setRawPage((newLeftPage - 1) /2);
+ }
+
+ public void setRightPage(int newRightPage) {
+ setLeftPage(newRightPage - 1);
+ }
+
+ public void setRawPage(int page) {
+ this.namedTag.putInt("page", Math.min(page, totalPages));
+ setDirty();
+ this.getLevel().updateAround(this);
+ }
+
+ public int getRawPage() {
+ return this.namedTag.getInt("page");
+ }
+
+ public int getTotalPages() {
+ return totalPages;
+ }
+
+ private void updateTotalPages(boolean updateRedstone) {
+ Item book = getBook();
+ if (book.getId() == BlockID.AIR || !book.hasCompoundTag()) {
+ totalPages = 0;
+ } else {
+ totalPages = book.getNamedTag().getList("pages", CompoundTag.class).size();
+ }
+
+ if (updateRedstone) {
+ this.getLevel().updateAroundRedstone(this, null);
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java
index 564a9649fb4..b823d250a1d 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityMovingBlock.java
@@ -42,7 +42,7 @@ public Block getBlock() {
@Override
public boolean isBlockEntityValid() {
- return true;
+ return true; // TODO
}
@Override
@@ -54,4 +54,4 @@ public CompoundTag getSpawnCompound() {
.putInt("pistonPosY", this.piston.y)
.putInt("pistonPosZ", this.piston.z);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityMusic.java b/src/main/java/cn/nukkit/blockentity/BlockEntityMusic.java
index 101c13a9cc3..d465e6384fc 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityMusic.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityMusic.java
@@ -15,6 +15,7 @@ protected void initBlockEntity() {
if (!this.namedTag.contains("note")) {
this.namedTag.putByte("note", 0);
}
+
if (!this.namedTag.contains("powered")) {
this.namedTag.putBoolean("powered", false);
}
@@ -24,11 +25,12 @@ protected void initBlockEntity() {
@Override
public boolean isBlockEntityValid() {
- return this.getBlock().getId() == Block.NOTEBLOCK;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.NOTEBLOCK;
}
public void changePitch() {
this.namedTag.putByte("note", (this.namedTag.getByte("note") + 1) % 25);
+ setDirty();
}
public int getPitch() {
@@ -37,6 +39,7 @@ public int getPitch() {
public void setPowered(boolean powered) {
this.namedTag.putBoolean("powered", powered);
+ setDirty();
}
public boolean isPowered() {
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityNameable.java b/src/main/java/cn/nukkit/blockentity/BlockEntityNameable.java
index e7bea12b7cf..914c93ef096 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityNameable.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityNameable.java
@@ -6,7 +6,6 @@
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public interface BlockEntityNameable {
@@ -15,7 +14,6 @@ public interface BlockEntityNameable {
* Gets the name of this object.
*
* @return 这个事物的名字。
The name of this object.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
String getName();
@@ -24,7 +22,6 @@ public interface BlockEntityNameable {
* Changes the name of this object, or names it.
*
* @param name 这个事物的新名字。
The new name of this object.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
void setName(String name);
@@ -33,7 +30,6 @@ public interface BlockEntityNameable {
* Whether this object has a name.
*
* @return 如果有名字,返回 {@code true}。
{@code true} for this object has a name.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
boolean hasName();
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java
index c722c423bbc..b7f9aba31b4 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityPistonArm.java
@@ -1,10 +1,8 @@
package cn.nukkit.blockentity;
-import cn.nukkit.entity.Entity;
+import cn.nukkit.block.Block;
import cn.nukkit.level.format.FullChunk;
-import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.BlockFace;
-import cn.nukkit.math.SimpleAxisAlignedBB;
import cn.nukkit.math.Vector3;
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.IntTag;
@@ -13,18 +11,17 @@
/**
* @author CreeperFace
*/
-public class BlockEntityPistonArm extends BlockEntity {
+public class BlockEntityPistonArm extends BlockEntitySpawnable {
- public float progress = 1.0F;
- public float lastProgress = 1.0F;
+ public float progress;
+ public float lastProgress;
public BlockFace facing;
public boolean extending = false;
public boolean sticky = false;
- public byte state = 1;
+ public byte state;
public byte newState = 1;
public Vector3 attachedBlock = null;
public boolean isMovable = true;
- public boolean powered = false;
public BlockEntityPistonArm(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
@@ -32,6 +29,8 @@ public BlockEntityPistonArm(FullChunk chunk, CompoundTag nbt) {
@Override
protected void initBlockEntity() {
+ this.isMovable = true;
+
if (namedTag.contains("Progress")) {
this.progress = namedTag.getFloat("Progress");
}
@@ -48,8 +47,8 @@ protected void initBlockEntity() {
this.extending = namedTag.getBoolean("Extending");
}
- if (namedTag.contains("powered")) {
- this.powered = namedTag.getBoolean("powered");
+ if (namedTag.contains("State")) {
+ this.state = (byte) namedTag.getByte("State");
}
if (namedTag.contains("AttachedBlocks")) {
@@ -64,25 +63,25 @@ protected void initBlockEntity() {
super.initBlockEntity();
}
- 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 SimpleAxisAlignedBB(x, y, z, x + 1.0D, y + 1.0D, z + 1.0D);
- Entity[] entities = this.level.getCollidingEntities(bb);
- if (entities.length != 0) {
-
- }
+ public void setExtended(boolean extending) {
+ this.extending = extending;
+ this.newState = this.state;
+ this.lastProgress = this.progress;
+ this.state = (byte) (extending ? 1 : 0);
+ this.progress = extending ? 1.0f : 0;
+ }
+ public boolean isExtended() {
+ return this.extending;
}
- private float getExtendedProgress(float progress) {
- return this.extending ? progress - 1.0F : 1.0F - progress;
+ public void broadcastMove() {
+ this.level.addChunkPacket(this.getChunkX(), this.getChunkZ(), this.createSpawnPacket());
}
public boolean isBlockEntityValid() {
- return true;
+ int blockId = getBlock().getId();
+ return blockId == Block.PISTON || blockId == Block.STICKY_PISTON;
}
public void saveNBT() {
@@ -92,10 +91,19 @@ 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);
+ this.namedTag.putBoolean("Sticky", this.sticky);
}
public CompoundTag getSpawnCompound() {
- return (new CompoundTag()).putString("id", "PistonArm").putInt("x", (int) this.x).putInt("y", (int) this.y).putInt("z", (int) this.z);
+ return new CompoundTag()
+ .putString("id", 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("Sticky", this.sticky)
+ .putByte("State", this.state)
+ .putByte("NewState", this.newState);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java b/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java
index a9362fb6b43..4bcde9efb8a 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityShulkerBox.java
@@ -13,11 +13,8 @@
import cn.nukkit.nbt.tag.CompoundTag;
import cn.nukkit.nbt.tag.ListTag;
-import java.util.HashSet;
+import java.util.ArrayList;
-/**
- * Created by PetteriM1
- */
public class BlockEntityShulkerBox extends BlockEntitySpawnable implements InventoryHolder, BlockEntityContainer, BlockEntityNameable {
protected ShulkerBoxInventory inventory;
@@ -28,46 +25,55 @@ public BlockEntityShulkerBox(FullChunk chunk, CompoundTag nbt) {
@Override
protected void initBlockEntity() {
- this.inventory = new ShulkerBoxInventory(this);
+ if (!this.namedTag.contains("facing")) {
+ this.namedTag.putByte("facing", 0);
+ }
+
+ super.initBlockEntity();
+ }
+ private void initInventory() {
if (!this.namedTag.contains("Items") || !(this.namedTag.get("Items") instanceof ListTag)) {
this.namedTag.putList(new ListTag("Items"));
}
-
ListTag list = (ListTag) this.namedTag.getList("Items");
+
+ this.inventory = new ShulkerBoxInventory(this);
+
for (CompoundTag compound : list.getAll()) {
Item item = NBTIO.getItemHelper(compound);
- this.inventory.slots.put(compound.getByte("Slot"), item);
- }
-
- if (!this.namedTag.contains("facing")) {
- this.namedTag.putByte("facing", 0);
+ if (item.getId() != 0 && item.getCount() > 0) {
+ this.inventory.slots.put(compound.getByte("Slot"), item);
+ }
}
-
- super.initBlockEntity();
}
@Override
public void close() {
- if (!closed) {
- for (Player player : new HashSet<>(this.getInventory().getViewers())) {
- player.removeWindow(this.getInventory());
+ if (!this.closed && this.inventory != null) {
+ for (Player player : new ArrayList<>(this.inventory.getViewers())) {
+ player.removeWindow(this.inventory);
}
- super.close();
}
+
+ super.close();
}
@Override
public void saveNBT() {
- this.namedTag.putList(new ListTag("Items"));
- for (int index = 0; index < this.getSize(); index++) {
- this.setItem(index, this.inventory.getItem(index));
+ super.saveNBT();
+
+ if (this.inventory != null) {
+ this.namedTag.putList(new ListTag("Items"));
+ for (int index = 0; index < this.getSize(); index++) {
+ this.setItem(index, this.inventory.getItem(index));
+ }
}
}
@Override
public boolean isBlockEntityValid() {
- int blockID = this.getBlock().getId();
+ int blockID = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
return blockID == Block.SHULKER_BOX || blockID == Block.UNDYED_SHULKER_BOX;
}
@@ -117,11 +123,17 @@ public void setItem(int index, Item item) {
@Override
public BaseInventory getInventory() {
+ if (this.inventory == null) {
+ this.initInventory();
+ }
return this.inventory;
}
public ShulkerBoxInventory getRealInventory() {
- return inventory;
+ if (this.inventory == null) {
+ this.initInventory();
+ }
+ return this.inventory;
}
@Override
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java
index ca41f212770..1c50aad232f 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySign.java
@@ -1,7 +1,7 @@
package cn.nukkit.blockentity;
import cn.nukkit.Player;
-import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockSignPost;
import cn.nukkit.event.block.SignChangeEvent;
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.nbt.tag.ByteTag;
@@ -15,7 +15,7 @@
import java.util.Objects;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BlockEntitySign extends BlockEntitySpawnable {
@@ -31,26 +31,28 @@ protected void initBlockEntity() {
text = new String[4];
if (!namedTag.contains("Text")) {
-
- for (int i = 1; i <= 4; i++) {
- String key = "Text" + i;
-
- if (namedTag.contains(key)) {
- String line = namedTag.getString(key);
-
- this.text[i - 1] = line;
-
- this.namedTag.remove(key);
+ if (namedTag.contains("FrontText")) { // Bedrock vanilla
+ this.setText(((CompoundTag) namedTag.removeAndGet("FrontText")).getString("Text").split("\n", 4));
+ } else {
+ for (int i = 1; i <= 4; i++) {
+ String key = "Text" + i;
+
+ if (namedTag.contains(key)) {
+ String line = namedTag.getString(key);
+ this.text[i - 1] = line;
+ this.namedTag.remove(key);
+ }
}
}
} else {
String[] lines = namedTag.getString("Text").split("\n", 4);
for (int i = 0; i < text.length; i++) {
- if (i < lines.length)
+ if (i < lines.length) {
text[i] = lines[i];
- else
+ } else {
text[i] = "";
+ }
}
}
@@ -59,10 +61,10 @@ protected void initBlockEntity() {
sanitizeText(text);
}
- if (!this.namedTag.contains("SignTextColor") || !(this.namedTag.get("SignTextColor") instanceof IntTag)) {
+ if (!(this.namedTag.get("SignTextColor") instanceof IntTag)) {
this.setColor(DyeColor.BLACK.getSignColor());
}
- if (!this.namedTag.contains("IgnoreLighting") || !(this.namedTag.get("IgnoreLighting") instanceof ByteTag)) {
+ if (!(this.namedTag.get("IgnoreLighting") instanceof ByteTag)) {
this.setGlowing(false);
}
@@ -77,8 +79,7 @@ public void saveNBT() {
@Override
public boolean isBlockEntityValid() {
- int blockID = getBlock().getId();
- return blockID == Block.SIGN_POST || blockID == Block.WALL_SIGN;
+ return getLevelBlock() instanceof BlockSignPost;
}
public boolean setText(String... lines) {
@@ -90,12 +91,9 @@ public boolean setText(String... lines) {
}
this.namedTag.putString("Text", String.join("\n", text));
- this.spawnToAll();
-
- if (this.chunk != null) {
- setDirty();
- }
+ setDirty();
+ this.spawnToAll();
return true;
}
@@ -109,6 +107,7 @@ public BlockColor getColor() {
public void setColor(BlockColor color) {
this.namedTag.putInt("SignTextColor", color.getARGB());
+ setDirty();
}
public boolean isGlowing() {
@@ -117,6 +116,7 @@ public boolean isGlowing() {
public void setGlowing(boolean glowing) {
this.namedTag.putBoolean("IgnoreLighting", glowing);
+ setDirty();
}
@Override
@@ -126,7 +126,8 @@ public boolean updateCompoundTag(CompoundTag nbt, Player player) {
}
String[] lines = new String[4];
Arrays.fill(lines, "");
- String[] splitLines = nbt.getCompound("FrontText").getString("Text").split("\n", 4);
+ String receivedText = nbt.getCompound("FrontText").getString("Text");
+ String[] splitLines = receivedText.split("\n", 4);
System.arraycopy(splitLines, 0, lines, 0, splitLines.length);
sanitizeText(lines);
@@ -168,9 +169,9 @@ public CompoundTag getSpawnCompound() {
private static void sanitizeText(String[] lines) {
for (int i = 0; i < lines.length; i++) {
- // Don't allow excessive text per line.
+ // Don't allow excessive text per line
if (lines[i] != null) {
- lines[i] = lines[i].substring(0, Math.min(255, lines[i].length()));
+ lines[i] = lines[i].substring(0, Math.min(200, lines[i].length()));
}
}
}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySkull.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySkull.java
index 2d141e79dfd..3a70362f3d6 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntitySkull.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySkull.java
@@ -33,7 +33,7 @@ public void saveNBT() {
@Override
public boolean isBlockEntityValid() {
- return getBlock().getId() == Block.SKULL_BLOCK;
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.SKULL_BLOCK;
}
@Override
@@ -46,5 +46,4 @@ public CompoundTag getSpawnCompound() {
.putInt("z", (int) this.z)
.put("Rot", this.namedTag.get("Rot"));
}
-
}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySmoker.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySmoker.java
new file mode 100644
index 00000000000..8faa4ac7b67
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySmoker.java
@@ -0,0 +1,144 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.event.inventory.FurnaceSmeltEvent;
+import cn.nukkit.inventory.FurnaceRecipe;
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.network.protocol.LevelSoundEventPacket;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BlockEntitySmoker extends BlockEntityFurnace {
+
+ public BlockEntitySmoker(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ @Override
+ public String getName() {
+ return this.hasName() ? this.namedTag.getString("CustomName") : "Smoker";
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ int blockID = level.getBlockIdAt(chunk, (int) x, (int) y, (int) z);
+ return blockID == Block.SMOKER || blockID == Block.LIT_SMOKER;
+ }
+
+ private static final IntSet CAN_SMELT = new IntOpenHashSet(new int[]{
+ Item.RAW_PORKCHOP, Item.RAW_BEEF, Item.RAW_RABBIT, Item.RAW_FISH, Item.RAW_CHICKEN, Item.RAW_MUTTON, Item.RAW_SALMON, Item.POTATO
+ });
+
+ @Override
+ public boolean onUpdate() {
+ if (this.closed) {
+ return false;
+ }
+
+ Item raw = this.inventory.getSmelting();
+ // TODO: smoker recipes
+ if (!CAN_SMELT.contains(raw.getId())) {
+ if (burnTime > 0) {
+ burnTime--;
+ burnDuration = (int) Math.ceil((float) burnTime / maxTime * 100);
+
+ if (burnTime == 0) {
+ Block block = this.level.getBlock(this.chunk, (int) x, (int) y, (int) z, true);
+ if (block.getId() == BlockID.LIT_SMOKER) {
+ this.level.setBlock(this, Block.get(BlockID.SMOKER, block.getDamage()), true);
+ }
+ return false;
+ }
+ }
+
+ cookTime = 0;
+ sendPacket();
+ return true;
+ }
+
+ boolean ret = false;
+ Item product = this.inventory.getResult();
+ FurnaceRecipe smelt = this.server.getCraftingManager().matchFurnaceRecipe(raw);
+ boolean canSmelt = (smelt != null && raw.getCount() > 0 && ((smelt.getResult().equals(product) && product.getCount() < product.getMaxStackSize()) || product.getId() == Item.AIR));
+
+ Item fuel;
+ if (burnTime <= 0 && canSmelt && (fuel = this.inventory.getItemFast(1)).getFuelTime() != null && fuel.getCount() > 0) {
+ this.checkFuel(fuel.clone());
+ }
+
+ if (burnTime > 0) {
+ burnTime--;
+ burnDuration = (int) Math.ceil((float) burnTime / maxTime * 100);
+
+ if (this.crackledTime-- <= 0) {
+ this.crackledTime = ThreadLocalRandom.current().nextInt(30, 110);
+ this.getLevel().addLevelSoundEvent(this.add(0.5, 0.5, 0.5), LevelSoundEventPacket.SOUND_BLOCK_FURNACE_LIT);
+ }
+
+ if (smelt != null && canSmelt) {
+ cookTime++;
+ if (cookTime >= 100) {
+ product = Item.get(smelt.getResult().getId(), smelt.getResult().getDamage(), product.isNull() ? 1 : product.getCount() + 1);
+
+ FurnaceSmeltEvent ev = new FurnaceSmeltEvent(this, raw, product);
+ this.server.getPluginManager().callEvent(ev);
+ if (!ev.isCancelled()) {
+ this.inventory.setResult(ev.getResult());
+ this.experience += FURNACE_XP.getOrDefault(ev.getResult().getId(), 0d);
+ raw.setCount(raw.getCount() - 1);
+ if (raw.getCount() == 0) {
+ raw = new ItemBlock(Block.get(BlockID.AIR), 0, 0);
+ }
+ this.inventory.setSmelting(raw);
+ }
+
+ cookTime -= 100;
+ }
+ } else if (burnTime <= 0) {
+ burnTime = 0;
+ cookTime = 0;
+ burnDuration = 0;
+ } else {
+ cookTime = 0;
+ }
+ ret = true;
+ } else {
+ Block block = this.level.getBlock(this.chunk, (int) x, (int) y, (int) z, true);
+ if (block.getId() == BlockID.LIT_SMOKER) {
+ this.level.setBlock(this, Block.get(BlockID.SMOKER, block.getDamage()), true);
+ }
+ burnTime = 0;
+ cookTime = 0;
+ burnDuration = 0;
+ crackledTime = 0;
+ }
+
+ sendPacket();
+
+ return ret;
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ CompoundTag c = new CompoundTag()
+ .putString("id", BlockEntity.SMOKER)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z)
+ .putShort("BurnDuration", burnDuration)
+ .putShort("BurnTime", burnTime)
+ .putShort("CookTime", cookTime);
+
+ if (this.hasName()) {
+ c.put("CustomName", this.namedTag.get("CustomName"));
+ }
+
+ return c;
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java
index e0d8651346d..309311986ac 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawnable.java
@@ -10,7 +10,7 @@
import java.nio.ByteOrder;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class BlockEntitySpawnable extends BlockEntity {
@@ -28,22 +28,27 @@ protected void initBlockEntity() {
public abstract CompoundTag getSpawnCompound();
- public void spawnTo(Player player) {
- if (this.closed) {
- return;
- }
-
+ public BlockEntityDataPacket createSpawnPacket() {
CompoundTag tag = this.getSpawnCompound();
+
BlockEntityDataPacket pk = new BlockEntityDataPacket();
pk.x = (int) this.x;
pk.y = (int) this.y;
pk.z = (int) this.z;
+
try {
pk.namedTag = NBTIO.write(tag, ByteOrder.LITTLE_ENDIAN, true);
} catch (IOException e) {
throw new RuntimeException(e);
}
- player.dataPacket(pk);
+
+ return pk;
+ }
+
+ public void spawnTo(Player player) {
+ if (!this.closed) {
+ player.dataPacket(this.createSpawnPacket());
+ }
}
public void spawnToAll() {
diff --git a/src/main/java/cn/nukkit/blockentity/BlockEntitySpawner.java b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawner.java
new file mode 100644
index 00000000000..32433b4877c
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntitySpawner.java
@@ -0,0 +1,26 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.tag.*;
+
+public class BlockEntitySpawner extends BlockEntitySpawnable {
+
+ public BlockEntitySpawner(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ @Override
+ public CompoundTag getSpawnCompound() {
+ return new CompoundTag()
+ .putString("id", BlockEntity.MOB_SPAWNER)
+ .putInt("x", (int) this.x)
+ .putInt("y", (int) this.y)
+ .putInt("z", (int) this.z);
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return level.getBlockIdAt(chunk, (int) x, (int) y, (int) z) == Block.MONSTER_SPAWNER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/blockentity/PersistentDataContainerBlockEntity.java b/src/main/java/cn/nukkit/blockentity/PersistentDataContainerBlockEntity.java
new file mode 100644
index 00000000000..be41d5241b2
--- /dev/null
+++ b/src/main/java/cn/nukkit/blockentity/PersistentDataContainerBlockEntity.java
@@ -0,0 +1,70 @@
+package cn.nukkit.blockentity;
+
+import cn.nukkit.block.BlockID;
+import cn.nukkit.level.persistence.PersistentDataContainer;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.nbt.tag.CompoundTag;
+import lombok.ToString;
+
+@ToString(callSuper = true)
+public class PersistentDataContainerBlockEntity extends BlockEntity implements PersistentDataContainer {
+
+ public PersistentDataContainerBlockEntity(FullChunk chunk, CompoundTag nbt) {
+ super(chunk, nbt);
+ }
+
+ @Override
+ public boolean onUpdate() {
+ if (!this.isBlockEntityValid()) {
+ this.close();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isBlockEntityValid() {
+ return this.level.getBlockIdAt(this.chunk, (int) this.x, (int) this.y, (int) this.z) != BlockID.AIR;
+ }
+
+ @Override
+ public boolean isValid() {
+ return super.isValid() && this.isBlockEntityValid();
+ }
+
+ @Override
+ public CompoundTag getStorage() {
+ if (this.namedTag.contains(STORAGE_TAG)) {
+ return this.namedTag.getCompound(STORAGE_TAG);
+ }
+
+ CompoundTag storage = new CompoundTag();
+ this.setStorage(storage);
+ return storage;
+ }
+
+ @Override
+ public void setStorage(CompoundTag storage) {
+ this.namedTag.put(STORAGE_TAG, storage);
+ setDirty();
+ }
+
+ @Override
+ public PersistentDataContainer getPersistentDataContainer() {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof PersistentDataContainerBlockEntity && super.equals(obj);
+ }
+
+ @Override
+ public boolean canSaveToStorage() {
+ return !this.isEmpty();
+ }
+
+ @Override
+ public void write() {
+ setDirty();
+ }
+}
diff --git a/src/main/java/cn/nukkit/command/Command.java b/src/main/java/cn/nukkit/command/Command.java
index fbbd4954eb7..c1b81b9b192 100644
--- a/src/main/java/cn/nukkit/command/Command.java
+++ b/src/main/java/cn/nukkit/command/Command.java
@@ -7,19 +7,15 @@
import cn.nukkit.lang.TranslationContainer;
import cn.nukkit.permission.Permissible;
import cn.nukkit.utils.TextFormat;
-import co.aikar.timings.Timing;
-import co.aikar.timings.Timings;
import java.util.*;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class Command {
- private static CommandData defaultDataTemplate = null;
-
protected CommandData commandData;
private final String name;
@@ -28,15 +24,15 @@ public abstract class Command {
private String label;
- private String[] aliases = new String[0];
+ private String[] aliases;
- private String[] activeAliases = new String[0];
+ private String[] activeAliases;
private CommandMap commandMap = null;
- protected String description = "";
+ protected String description;
- protected String usageMessage = "";
+ protected String usageMessage;
private String permission = null;
@@ -44,8 +40,6 @@ public abstract class Command {
protected Map commandParameters = new HashMap<>();
- public Timing timing;
-
public Command(String name) {
this(name, "", null, new String[0]);
}
@@ -60,15 +54,14 @@ public Command(String name, String description, String usageMessage) {
public Command(String name, String description, String usageMessage, String[] aliases) {
this.commandData = new CommandData();
- this.name = name.toLowerCase(); // Uppercase letters crash the client?!?
+ this.name = name.toLowerCase(); // Prevent client crash
this.nextLabel = name;
this.label = name;
this.description = description;
- this.usageMessage = usageMessage == null ? "/" + name : usageMessage;
+ this.usageMessage = usageMessage == null ? '/' + name : usageMessage;
this.aliases = aliases;
this.activeAliases = aliases;
- this.timing = Timings.getCommandTiming(this);
- this.commandParameters.put("default", new CommandParameter[]{CommandParameter.newType("args", true, CommandParamType.RAWTEXT)});
+ this.commandParameters.put("default", new CommandParameter[]{new CommandParameter("args", CommandParamType.RAWTEXT, true)});
}
/**
@@ -104,7 +97,7 @@ public void addCommandParameters(String key, CommandParameter[] parameters) {
* @return CommandData|null
*/
public CommandDataVersions generateCustomCommandData(Player player) {
- if (!this.testPermission(player)) {
+ if (!this.testPermissionSilent(player)) {
return null;
}
@@ -125,7 +118,7 @@ public CommandDataVersions generateCustomCommandData(Player player) {
overload.input.parameters = par;
customData.overloads.put(key, overload);
});
- if (customData.overloads.size() == 0) customData.overloads.put("default", new CommandOverload());
+ if (customData.overloads.isEmpty()) customData.overloads.put("default", new CommandOverload());
CommandDataVersions versions = new CommandDataVersions();
versions.versions.add(customData);
return versions;
@@ -156,7 +149,7 @@ public boolean testPermission(CommandSender target) {
if (this.permissionMessage == null) {
target.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.unknown", this.name));
- } else if (!this.permissionMessage.equals("")) {
+ } else if (!this.permissionMessage.isEmpty()) {
target.sendMessage(this.permissionMessage.replace("", this.permission));
}
@@ -164,7 +157,7 @@ public boolean testPermission(CommandSender target) {
}
public boolean testPermissionSilent(CommandSender target) {
- if (this.permission == null || this.permission.equals("")) {
+ if (this.permission == null || this.permission.isEmpty()) {
return true;
}
@@ -186,7 +179,6 @@ public boolean setLabel(String name) {
this.nextLabel = name;
if (!this.isRegistered()) {
this.label = name;
- this.timing = Timings.getCommandTiming(this);
return true;
}
return false;
@@ -254,10 +246,7 @@ public void setUsage(String usageMessage) {
}
public static CommandData generateDefaultData() {
- if (defaultDataTemplate == null) {
- //defaultDataTemplate = new Gson().fromJson(new InputStreamReader(Server.class.getClassLoader().getResourceAsStream("command_default.json")));
- }
- return defaultDataTemplate.clone();
+ return null; //defaultDataTemplate.clone();
}
public static void broadcastCommandMessage(CommandSender source, String message) {
@@ -292,7 +281,7 @@ public static void broadcastCommandMessage(CommandSender source, TextContainer m
public static void broadcastCommandMessage(CommandSender source, TextContainer message, boolean sendToSource) {
TextContainer m = message.clone();
- String resultStr = "[" + source.getName() + ": " + (!m.getText().equals(source.getServer().getLanguage().get(m.getText())) ? "%" : "") + m.getText() + "]";
+ String resultStr = '[' + source.getName() + ": " + (!m.getText().equals(source.getServer().getLanguage().get(m.getText())) ? "%" : "") + m.getText() + ']';
Set users = source.getServer().getPluginManager().getPermissionSubscriptions(Server.BROADCAST_CHANNEL_ADMINISTRATIVE);
@@ -322,5 +311,4 @@ public static void broadcastCommandMessage(CommandSender source, TextContainer m
public String toString() {
return this.name;
}
-
}
diff --git a/src/main/java/cn/nukkit/command/CommandExecutor.java b/src/main/java/cn/nukkit/command/CommandExecutor.java
index 3ad4780b7ba..fd2066f4613 100644
--- a/src/main/java/cn/nukkit/command/CommandExecutor.java
+++ b/src/main/java/cn/nukkit/command/CommandExecutor.java
@@ -8,31 +8,30 @@
* @author 粉鞋大妈(javadoc) @ Nukkit Project
* @see cn.nukkit.plugin.PluginBase
* @see cn.nukkit.command.CommandExecutor#onCommand
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public interface CommandExecutor {
/**
* 在命令执行时会调用的方法。
* Called when a command is executed.
- *
- * 一个命令可以是{@code /a_LABEL an_arg1 AN_ARG2...}的形式,这时{@code label}变量的值为{@code "a_label"},
+ *
+ * 一个命令可以是{@code /a_LABEL an_arg1 AN_ARG2...}的形式,这时{@code label}变量的值为{@code "a_label"},
* {@code args}数组的元素有{@code "an_arg1","AN_ARG2",...}。注意到{@code label}变量会被转化成小写,
* 而{@code args}数组内字符串元素的大小写不变。
* A command can be such a form like {@code /a_LABEL an_arg1 AN_ARG2...}. At this time, the value of
* variable {@code label} is {@code "a_label"}, and the values of elements of array {@code args} are
* {@code "an_arg1","AN_ARG2",...}. Notice that the value of variable {@code label} will be converted to
- * lower case, but the cases of elements of array {@code args} won't change.
- *
- * 关于返回值,如果返回{@code false},Nukkit会给sender发送这个命令的使用方法等信息,来表示这个命令没有使用成功。
+ * lower case, but the cases of elements of array {@code args} won't change.
+ *
+ * 关于返回值,如果返回{@code false},Nukkit会给sender发送这个命令的使用方法等信息,来表示这个命令没有使用成功。
* 如果你的命令成功的发挥了作用,你应该返回{@code true}来表示这个命令已执行成功。
* If this function returns {@code false}, Nukkit will send command usages to command sender, to explain that
* the command didn't work normally. If your command works properly, a {@code true} should be returned to explain
- * that the command works.
- *
- * 如果你想测试一个命令发送者是否有权限执行这个命令,
+ * that the command works.
+ *
+ * 如果你想测试一个命令发送者是否有权限执行这个命令,
* 可以使用{@link cn.nukkit.command.Command#testPermissionSilent}。
* If you want to test whether a command sender has the permission to execute a command,
- * you can use {@link cn.nukkit.command.Command#testPermissionSilent}.
+ * you can use {@link cn.nukkit.command.Command#testPermissionSilent}.
*
* @param sender 这个命令的发送者,可以是玩家或控制台等。
* The sender of this command, this can be a player or a console.
@@ -43,7 +42,6 @@ public interface CommandExecutor {
* @param args 这个命令的参数列表。
* Arguments of this command.
* @return 这个命令执行是否执行成功。
whether this command is executed successfully.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
boolean onCommand(CommandSender sender, Command command, String label, String[] args);
}
diff --git a/src/main/java/cn/nukkit/command/CommandMap.java b/src/main/java/cn/nukkit/command/CommandMap.java
index 0bc577e6746..7d1be68a5f9 100644
--- a/src/main/java/cn/nukkit/command/CommandMap.java
+++ b/src/main/java/cn/nukkit/command/CommandMap.java
@@ -3,7 +3,7 @@
import java.util.List;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public interface CommandMap {
@@ -21,5 +21,4 @@ public interface CommandMap {
void clearCommands();
Command getCommand(String name);
-
}
diff --git a/src/main/java/cn/nukkit/command/CommandSender.java b/src/main/java/cn/nukkit/command/CommandSender.java
index 00d6e119138..6bc9ecb2c2d 100644
--- a/src/main/java/cn/nukkit/command/CommandSender.java
+++ b/src/main/java/cn/nukkit/command/CommandSender.java
@@ -7,14 +7,13 @@
/**
* 能发送命令的人。
* Who sends commands.
- *
- * 可以是一个玩家或者一个控制台。
- * That can be a player or a console.
+ *
+ * 可以是一个玩家或者一个控制台。
+ * That can be a player or a console.
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
* @see cn.nukkit.command.CommandExecutor#onCommand
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public interface CommandSender extends Permissible {
@@ -24,7 +23,6 @@ public interface CommandSender extends Permissible {
*
* @param message 要发送的信息。
Message to send.
* @see cn.nukkit.utils.TextFormat
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
void sendMessage(String message);
@@ -33,7 +31,6 @@ public interface CommandSender extends Permissible {
* Sends a message to the command sender.
*
* @param message 要发送的信息。
Message to send.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
void sendMessage(TextContainer message);
@@ -42,30 +39,27 @@ public interface CommandSender extends Permissible {
* Returns the server of the command sender.
*
* @return 命令发送者所在的服务器。
the server of the command sender.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
Server getServer();
/**
* 返回命令发送者的名称。
* Returns the name of the command sender.
- *
- * 如果命令发送者是一个玩家,将会返回他的玩家名字(name)不是显示名字(display name)。
+ *
+ * 如果命令发送者是一个玩家,将会返回他的玩家名字(name)不是显示名字(display name)。
* 如果命令发送者是控制台,将会返回{@code "CONSOLE"}。
* If this command sender is a player, will return his/her player name(not display name).
- * If it is a console, will return {@code "CONSOLE"}.
- * 当你需要判断命令的执行者是不是控制台时,可以用这个:
+ * If it is a console, will return {@code "CONSOLE"}.
+ * 当你需要判断命令的执行者是不是控制台时,可以用这个:
* When you need to determine if the sender is a console, use this:
- * {@code if(sender instanceof ConsoleCommandSender) .....;}
+ * {@code if (sender instanceof ConsoleCommandSender) .....;}
*
* @return 命令发送者的名称。
the name of the command sender.
* @see cn.nukkit.Player#getName()
* @see cn.nukkit.command.ConsoleCommandSender#getName()
* @see cn.nukkit.plugin.PluginDescription
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
String getName();
boolean isPlayer();
-
}
diff --git a/src/main/java/cn/nukkit/command/ConsoleCommandSender.java b/src/main/java/cn/nukkit/command/ConsoleCommandSender.java
index 95e363de72d..a2c001ed2f8 100644
--- a/src/main/java/cn/nukkit/command/ConsoleCommandSender.java
+++ b/src/main/java/cn/nukkit/command/ConsoleCommandSender.java
@@ -12,7 +12,7 @@
import java.util.Map;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class ConsoleCommandSender implements CommandSender {
@@ -107,6 +107,5 @@ public boolean isOp() {
@Override
public void setOp(boolean value) {
-
}
}
diff --git a/src/main/java/cn/nukkit/command/FormattedCommandAlias.java b/src/main/java/cn/nukkit/command/FormattedCommandAlias.java
index 8e84261e176..25126ccb55c 100644
--- a/src/main/java/cn/nukkit/command/FormattedCommandAlias.java
+++ b/src/main/java/cn/nukkit/command/FormattedCommandAlias.java
@@ -9,7 +9,7 @@
import java.util.List;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class FormattedCommandAlias extends Command {
@@ -55,13 +55,13 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
}
private String buildCommand(String formatString, String[] args) {
- int index = formatString.indexOf("$");
+ int index = formatString.indexOf('$');
while (index != -1) {
int start = index;
if (index > 0 && formatString.charAt(start - 1) == '\\') {
formatString = formatString.substring(0, start - 1) + formatString.substring(start);
- index = formatString.indexOf("$", index);
+ index = formatString.indexOf('$', index);
continue;
}
@@ -120,12 +120,12 @@ private String buildCommand(String formatString, String[] args) {
replacement.append(args[position]);
}
- formatString = formatString.substring(0, start) + replacement.toString() + formatString.substring(end);
+ formatString = formatString.substring(0, start) + replacement + formatString.substring(end);
// Move index past the replaced data so we don't process it again
index = start + replacement.length();
// Move to the next replacement token
- index = formatString.indexOf("$", index);
+ index = formatString.indexOf('$', index);
}
return formatString;
@@ -134,5 +134,4 @@ private String buildCommand(String formatString, String[] args) {
private static boolean inRange(int i, int j, int k) {
return i >= j && i <= k;
}
-
}
diff --git a/src/main/java/cn/nukkit/command/PluginCommand.java b/src/main/java/cn/nukkit/command/PluginCommand.java
index 597ecedfb43..07d07da0a12 100644
--- a/src/main/java/cn/nukkit/command/PluginCommand.java
+++ b/src/main/java/cn/nukkit/command/PluginCommand.java
@@ -4,7 +4,7 @@
import cn.nukkit.plugin.Plugin;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class PluginCommand extends Command implements PluginIdentifiableCommand {
@@ -32,7 +32,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
boolean success = this.executor.onCommand(sender, this, commandLabel, args);
- if (!success && !this.usageMessage.equals("")) {
+ if (!success && !this.usageMessage.isEmpty()) {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
}
diff --git a/src/main/java/cn/nukkit/command/PluginIdentifiableCommand.java b/src/main/java/cn/nukkit/command/PluginIdentifiableCommand.java
index c8aaf4c873e..a22ab9446c0 100644
--- a/src/main/java/cn/nukkit/command/PluginIdentifiableCommand.java
+++ b/src/main/java/cn/nukkit/command/PluginIdentifiableCommand.java
@@ -3,7 +3,7 @@
import cn.nukkit.plugin.Plugin;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public interface PluginIdentifiableCommand {
diff --git a/src/main/java/cn/nukkit/command/RemoteConsoleCommandSender.java b/src/main/java/cn/nukkit/command/RemoteConsoleCommandSender.java
index f0d08618309..b2864caea13 100644
--- a/src/main/java/cn/nukkit/command/RemoteConsoleCommandSender.java
+++ b/src/main/java/cn/nukkit/command/RemoteConsoleCommandSender.java
@@ -8,12 +8,13 @@
* @author Tee7even
*/
public class RemoteConsoleCommandSender extends ConsoleCommandSender {
+
private final StringBuilder messages = new StringBuilder();
@Override
public void sendMessage(String message) {
message = this.getServer().getLanguage().translateString(message);
- this.messages.append(message.trim()).append("\n");
+ this.messages.append(message.trim()).append('\n');
}
@Override
@@ -25,6 +26,10 @@ public String getMessages() {
return messages.toString();
}
+ public void clearMessages() {
+ messages.delete(0, messages.length());
+ }
+
@Override
public String getName() {
return "Rcon";
diff --git a/src/main/java/cn/nukkit/command/SimpleCommandMap.java b/src/main/java/cn/nukkit/command/SimpleCommandMap.java
index 0d5e0da2bfa..a821b53b315 100644
--- a/src/main/java/cn/nukkit/command/SimpleCommandMap.java
+++ b/src/main/java/cn/nukkit/command/SimpleCommandMap.java
@@ -5,7 +5,6 @@
import cn.nukkit.command.defaults.*;
import cn.nukkit.command.simple.*;
import cn.nukkit.lang.TranslationContainer;
-import cn.nukkit.utils.MainLogger;
import cn.nukkit.utils.TextFormat;
import cn.nukkit.utils.Utils;
@@ -15,10 +14,11 @@
import java.util.stream.Collectors;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class SimpleCommandMap implements CommandMap {
+
protected final Map knownCommands = new HashMap<>();
private final Server server;
@@ -31,51 +31,48 @@ public SimpleCommandMap(Server server) {
private void setDefaultCommands() {
this.register("nukkit", new VersionCommand("version"));
this.register("nukkit", new PluginsCommand("plugins"));
- this.register("nukkit", new SeedCommand("seed"));
this.register("nukkit", new HelpCommand("help"));
this.register("nukkit", new StopCommand("stop"));
this.register("nukkit", new TellCommand("tell"));
- this.register("nukkit", new DefaultGamemodeCommand("defaultgamemode"));
this.register("nukkit", new BanCommand("ban"));
this.register("nukkit", new BanIpCommand("ban-ip"));
this.register("nukkit", new BanListCommand("banlist"));
this.register("nukkit", new PardonCommand("pardon"));
this.register("nukkit", new PardonIpCommand("pardon-ip"));
- this.register("nukkit", new SayCommand("say"));
- this.register("nukkit", new MeCommand("me"));
this.register("nukkit", new ListCommand("list"));
- this.register("nukkit", new DifficultyCommand("difficulty"));
this.register("nukkit", new KickCommand("kick"));
this.register("nukkit", new OpCommand("op"));
this.register("nukkit", new DeopCommand("deop"));
- this.register("nukkit", new WhitelistCommand("whitelist"));
- this.register("nukkit", new SaveOnCommand("save-on"));
- this.register("nukkit", new SaveOffCommand("save-off"));
this.register("nukkit", new SaveCommand("save-all"));
this.register("nukkit", new GiveCommand("give"));
this.register("nukkit", new ClearCommand("clear"));
this.register("nukkit", new EffectCommand("effect"));
this.register("nukkit", new EnchantCommand("enchant"));
- this.register("nukkit", new ParticleCommand("particle"));
this.register("nukkit", new GamemodeCommand("gamemode"));
- this.register("nukkit", new GameruleCommand("gamerule"));
this.register("nukkit", new KillCommand("kill"));
- this.register("nukkit", new SpawnpointCommand("spawnpoint"));
this.register("nukkit", new SetWorldSpawnCommand("setworldspawn"));
this.register("nukkit", new TeleportCommand("tp"));
this.register("nukkit", new TimeCommand("time"));
- this.register("nukkit", new TitleCommand("title"));
this.register("nukkit", new ReloadCommand("reload"));
this.register("nukkit", new WeatherCommand("weather"));
this.register("nukkit", new XpCommand("xp"));
-
-// if ((boolean) this.server.getConfig("debug.commands", false)) {
this.register("nukkit", new StatusCommand("status"));
+ this.register("nukkit", new SummonCommand("summon"));
+ this.register("nukkit", new WhitelistCommand("whitelist"));
+ this.register("nukkit", new GameruleCommand("gamerule"));
+ this.register("nukkit", new ConvertCommand("convert"));
+ this.register("nukkit", new DefaultGamemodeCommand("defaultgamemode"));
+ this.register("nukkit", new SayCommand("say"));
+ this.register("nukkit", new MeCommand("me"));
+ this.register("nukkit", new SaveOnCommand("save-on"));
+ this.register("nukkit", new SaveOffCommand("save-off"));
+ this.register("nukkit", new DifficultyCommand("difficulty"));
+ this.register("nukkit", new ParticleCommand("particle"));
+ this.register("nukkit", new SpawnpointCommand("spawnpoint"));
+ this.register("nukkit", new TitleCommand("title"));
+ this.register("nukkit", new SeedCommand("seed"));
+ this.register("nukkit", new PlaySoundCommand("playsound"));
this.register("nukkit", new GarbageCollectorCommand("gc"));
- this.register("nukkit", new TimingsCommand("timings"));
- //this.register("nukkit", new DebugPasteCommand("debugpaste")); // No more unauthenticated API access, TODO: find a replacement for hastebin.com
- //this.register("nukkit", new DumpMemoryCommand("dumpmemory"));
-// }
}
@Override
@@ -111,7 +108,7 @@ public boolean register(String fallbackPrefix, Command command, String label) {
command.setAliases(aliases.toArray(new String[0]));
if (!registered) {
- command.setLabel(fallbackPrefix + ":" + label);
+ command.setLabel(fallbackPrefix + ':' + label);
}
command.register(this);
@@ -145,7 +142,7 @@ public void registerSimpleCommands(Object object) {
if (commandParameters != null) {
Map map = Arrays.stream(commandParameters.parameters())
.collect(Collectors.toMap(Parameters::name, parameters -> Arrays.stream(parameters.parameters())
- .map(parameter -> CommandParameter.newType(parameter.name(), parameter.optional(), parameter.type()))
+ .map(parameter -> new CommandParameter(parameter.name(), parameter.type(), parameter.optional()))
.distinct()
.toArray(CommandParameter[]::new)));
@@ -158,7 +155,7 @@ public void registerSimpleCommands(Object object) {
}
private boolean registerAlias(Command command, boolean isAlias, String fallbackPrefix, String label) {
- this.knownCommands.put(fallbackPrefix + ":" + label, command);
+ this.knownCommands.put(fallbackPrefix + ':' + label, command);
//if you're registering a command alias that is already registered, then return false
boolean alreadyRegistered = this.knownCommands.containsKey(label);
@@ -169,15 +166,15 @@ private boolean registerAlias(Command command, boolean isAlias, String fallbackP
return false;
}
- //if you're registering a name (alias or label) which is identical to another command who's primary name is the same
- //so basically we can't override the main name of a command, but we can override aliases if we're not an alias
+ // If you're registering a name (alias or label) which is identical to another command who's primary name is the same
+ // So basically we can't override the main name of a command, but we can override aliases if we're not an alias
- //added the last statement which will allow us to override a VanillaCommand unconditionally
+ // Added the last statement which will allow us to override a VanillaCommand unconditionally
if (alreadyRegistered && existingCommand.getLabel() != null && existingCommand.getLabel().equals(label) && existingCommandIsNotVanilla) {
return false;
}
- //you can now assume that the command is either uniquely named, or overriding another command's alias (and is not itself, an alias)
+ // You can now assume that the command is either uniquely named, or overriding another command's alias (and is not itself, an alias)
if (!isAlias) {
command.setLabel(label);
@@ -206,7 +203,7 @@ private boolean registerAlias(Command command, boolean isAlias, String fallbackP
return true;
}
- private ArrayList parseArguments(String cmdLine) {
+ private static ArrayList parseArguments(String cmdLine) {
StringBuilder sb = new StringBuilder(cmdLine);
ArrayList args = new ArrayList<>();
boolean notQuoted = true;
@@ -241,7 +238,7 @@ private ArrayList parseArguments(String cmdLine) {
@Override
public boolean dispatch(CommandSender sender, String cmdLine) {
ArrayList parsed = parseArguments(cmdLine);
- if (parsed.size() == 0) {
+ if (parsed.isEmpty()) {
return false;
}
@@ -253,18 +250,12 @@ public boolean dispatch(CommandSender sender, String cmdLine) {
return false;
}
- target.timing.startTiming();
try {
target.execute(sender, sentCommandLabel, args);
} catch (Exception e) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.exception"));
- this.server.getLogger().critical(this.server.getLanguage().translateString("nukkit.command.exception", cmdLine, target.toString(), Utils.getExceptionMessage(e)));
- MainLogger logger = sender.getServer().getLogger();
- if (logger != null) {
- logger.logException(e);
- }
+ this.server.getLogger().critical(this.server.getLanguage().translateString("nukkit.command.exception", cmdLine, target.toString(), Utils.getExceptionMessage(e)), e);
}
- target.timing.stopTiming();
return true;
}
@@ -280,10 +271,7 @@ public void clearCommands() {
@Override
public Command getCommand(String name) {
- if (this.knownCommands.containsKey(name)) {
- return this.knownCommands.get(name);
- }
- return null;
+ return this.knownCommands.get(name);
}
public Map getCommands() {
diff --git a/src/main/java/cn/nukkit/command/data/CommandArgs.java b/src/main/java/cn/nukkit/command/data/CommandArgs.java
index 3c369db69f9..1a67906822b 100644
--- a/src/main/java/cn/nukkit/command/data/CommandArgs.java
+++ b/src/main/java/cn/nukkit/command/data/CommandArgs.java
@@ -5,5 +5,4 @@
import java.util.HashMap;
public class CommandArgs extends HashMap {
-
}
diff --git a/src/main/java/cn/nukkit/command/data/CommandDataVersions.java b/src/main/java/cn/nukkit/command/data/CommandDataVersions.java
index 0434d8542e4..6950dee9917 100644
--- a/src/main/java/cn/nukkit/command/data/CommandDataVersions.java
+++ b/src/main/java/cn/nukkit/command/data/CommandDataVersions.java
@@ -6,5 +6,4 @@
public class CommandDataVersions {
public List versions = new ArrayList<>();
-
}
diff --git a/src/main/java/cn/nukkit/command/data/CommandEnum.java b/src/main/java/cn/nukkit/command/data/CommandEnum.java
index 6e18e36039b..9ef7ea7bf52 100644
--- a/src/main/java/cn/nukkit/command/data/CommandEnum.java
+++ b/src/main/java/cn/nukkit/command/data/CommandEnum.java
@@ -14,8 +14,7 @@
public class CommandEnum {
public static final CommandEnum ENUM_BOOLEAN = new CommandEnum("Boolean", ImmutableList.of("true", "false"));
- public static final CommandEnum ENUM_GAMEMODE = new CommandEnum("GameMode",
- ImmutableList.of("survival", "creative", "s", "c", "adventure", "a", "spectator", "view", "v", "spc"));
+ public static final CommandEnum ENUM_GAMEMODE = new CommandEnum("GameMode", ImmutableList.of("survival", "creative", "s", "c", "adventure", "a", "spectator", "view", "v", "spc"));
public static final CommandEnum ENUM_BLOCK;
public static final CommandEnum ENUM_ITEM;
@@ -25,7 +24,6 @@ public class CommandEnum {
blocks.add(field.getName().toLowerCase());
}
ENUM_BLOCK = new CommandEnum("Block", blocks.build());
-
ImmutableList.Builder items = ImmutableList.builder();
for (Field field : ItemID.class.getDeclaredFields()) {
items.add(field.getName().toLowerCase());
@@ -34,8 +32,8 @@ public class CommandEnum {
ENUM_ITEM = new CommandEnum("Item", items.build());
}
- private String name;
- private List values;
+ private final String name;
+ private final List values;
public CommandEnum(String name, String... values) {
this(name, Arrays.asList(values));
diff --git a/src/main/java/cn/nukkit/command/data/CommandInput.java b/src/main/java/cn/nukkit/command/data/CommandInput.java
index 9fb056dc5a0..124e916320d 100644
--- a/src/main/java/cn/nukkit/command/data/CommandInput.java
+++ b/src/main/java/cn/nukkit/command/data/CommandInput.java
@@ -3,5 +3,4 @@
public class CommandInput {
public CommandParameter[] parameters = new CommandParameter[0];
-
}
diff --git a/src/main/java/cn/nukkit/command/data/CommandOutput.java b/src/main/java/cn/nukkit/command/data/CommandOutput.java
index cc168f58506..b3ff8811c66 100644
--- a/src/main/java/cn/nukkit/command/data/CommandOutput.java
+++ b/src/main/java/cn/nukkit/command/data/CommandOutput.java
@@ -3,5 +3,4 @@
public class CommandOutput {
public String[] format_strings = new String[0];
-
}
diff --git a/src/main/java/cn/nukkit/command/data/CommandOverload.java b/src/main/java/cn/nukkit/command/data/CommandOverload.java
index 7b2bbd31696..ab7c819083e 100644
--- a/src/main/java/cn/nukkit/command/data/CommandOverload.java
+++ b/src/main/java/cn/nukkit/command/data/CommandOverload.java
@@ -4,5 +4,4 @@ public class CommandOverload {
public CommandInput input = new CommandInput();
public CommandOutput output = new CommandOutput();
-
}
diff --git a/src/main/java/cn/nukkit/command/data/CommandParamType.java b/src/main/java/cn/nukkit/command/data/CommandParamType.java
index 8f109fa4850..a4e05316377 100644
--- a/src/main/java/cn/nukkit/command/data/CommandParamType.java
+++ b/src/main/java/cn/nukkit/command/data/CommandParamType.java
@@ -6,6 +6,7 @@
* @author CreeperFace
*/
public enum CommandParamType {
+
INT(ARG_TYPE_INT),
FLOAT(ARG_TYPE_FLOAT),
VALUE(ARG_TYPE_VALUE),
@@ -37,4 +38,4 @@ public enum CommandParamType {
public int getId() {
return id;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/command/data/CommandParameter.java b/src/main/java/cn/nukkit/command/data/CommandParameter.java
index a657455d1b0..82d3cc71dd6 100644
--- a/src/main/java/cn/nukkit/command/data/CommandParameter.java
+++ b/src/main/java/cn/nukkit/command/data/CommandParameter.java
@@ -1,9 +1,28 @@
package cn.nukkit.command.data;
+
import java.util.ArrayList;
+import java.util.Arrays;
public class CommandParameter {
+ public final static String ARG_TYPE_STRING = "string";
+ public final static String ARG_TYPE_STRING_ENUM = "stringenum";
+ public final static String ARG_TYPE_BOOL = "bool";
+ public final static String ARG_TYPE_TARGET = "target";
+ public final static String ARG_TYPE_PLAYER = "target";
+ public final static String ARG_TYPE_BLOCK_POS = "blockpos";
+ public final static String ARG_TYPE_RAW_TEXT = "rawtext";
+ public final static String ARG_TYPE_INT = "int";
+
+ public static final String ENUM_TYPE_ITEM_LIST = "Item";
+ public static final String ENUM_TYPE_BLOCK_LIST = "Block";
+ public static final String ENUM_TYPE_COMMAND_LIST = "commandName";
+ public static final String ENUM_TYPE_ENCHANTMENT_LIST = "enchantmentType";
+ public static final String ENUM_TYPE_ENTITY_LIST = "entityType";
+ public static final String ENUM_TYPE_EFFECT_LIST = "effectType";
+ public static final String ENUM_TYPE_PARTICLE_LIST = "particleType";
+
public String name;
public CommandParamType type;
public boolean optional;
@@ -12,44 +31,24 @@ public class CommandParameter {
public CommandEnum enumData;
public String postFix;
- /**
- * @deprecated use {@link #newType(String, boolean, CommandParamType)} instead
- */
- @Deprecated
public CommandParameter(String name, String type, boolean optional) {
this(name, fromString(type), optional);
}
- /**
- * @deprecated use {@link #newType(String, boolean, CommandParamType)} instead
- */
- @Deprecated
public CommandParameter(String name, CommandParamType type, boolean optional) {
this.name = name;
this.type = type;
this.optional = optional;
}
- /**
- * @deprecated use {@link #newType(String, boolean, CommandParamType)} instead
- */
- @Deprecated
public CommandParameter(String name, boolean optional) {
this(name, CommandParamType.RAWTEXT, optional);
}
- /**
- * @deprecated use {@link #newType(String, CommandParamType)} instead
- */
- @Deprecated
public CommandParameter(String name) {
this(name, false);
}
- /**
- * @deprecated use {@link #newEnum(String, boolean, String)} instead
- */
- @Deprecated
public CommandParameter(String name, boolean optional, String enumType) {
this.name = name;
this.type = CommandParamType.RAWTEXT;
@@ -57,29 +56,17 @@ public CommandParameter(String name, boolean optional, String enumType) {
this.enumData = new CommandEnum(enumType, new ArrayList<>());
}
- /**
- * @deprecated use {@link #newEnum(String, boolean, String[])} instead
- */
- @Deprecated
public CommandParameter(String name, boolean optional, String[] enumValues) {
this.name = name;
this.type = CommandParamType.RAWTEXT;
this.optional = optional;
- this.enumData = new CommandEnum(name + "Enums", enumValues);
+ this.enumData = new CommandEnum(name + "Enums", Arrays.asList(enumValues));
}
- /**
- * @deprecated use {@link #newEnum(String, String)} instead
- */
- @Deprecated
public CommandParameter(String name, String enumType) {
this(name, false, enumType);
}
- /**
- * @deprecated use {@link #newEnum(String, String[])} instead
- */
- @Deprecated
public CommandParameter(String name, String[] enumValues) {
this(name, false, enumValues);
}
diff --git a/src/main/java/cn/nukkit/command/data/args/CommandArg.java b/src/main/java/cn/nukkit/command/data/args/CommandArg.java
deleted file mode 100644
index c2f8a888b5f..00000000000
--- a/src/main/java/cn/nukkit/command/data/args/CommandArg.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package cn.nukkit.command.data.args;
-
-public class CommandArg {
-
- private CommandArgRules[] rules;
- private String selector;
-
- public CommandArgRules[] getRules() {
- return rules;
- }
-
- public String getSelector() {
- return selector;
- }
-}
diff --git a/src/main/java/cn/nukkit/command/data/args/CommandArgBlockVector.java b/src/main/java/cn/nukkit/command/data/args/CommandArgBlockVector.java
deleted file mode 100644
index 83cc78b7ca8..00000000000
--- a/src/main/java/cn/nukkit/command/data/args/CommandArgBlockVector.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package cn.nukkit.command.data.args;
-
-public class CommandArgBlockVector {
-
- private int x;
- private int y;
- private int z;
- private boolean xrelative;
- private boolean yrelative;
- private boolean zrelative;
-
- public int getX() {
- return x;
- }
-
- public int getY() {
- return y;
- }
-
- public int getZ() {
- return z;
- }
-
- public boolean isXrelative() {
- return xrelative;
- }
-
- public boolean isYrelative() {
- return yrelative;
- }
-
- public boolean isZrelative() {
- return zrelative;
- }
-}
diff --git a/src/main/java/cn/nukkit/command/data/args/CommandArgRules.java b/src/main/java/cn/nukkit/command/data/args/CommandArgRules.java
deleted file mode 100644
index e719c44c2a1..00000000000
--- a/src/main/java/cn/nukkit/command/data/args/CommandArgRules.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package cn.nukkit.command.data.args;
-
-public class CommandArgRules {
-
- private boolean inverted;
- private String name;
- private String value;
-
- public boolean isInverted() {
- return inverted;
- }
-
- public String getName() {
- return name;
- }
-
- public String getValue() {
- return value;
- }
-
-}
diff --git a/src/main/java/cn/nukkit/command/defaults/BanCommand.java b/src/main/java/cn/nukkit/command/defaults/BanCommand.java
index 33282b8c4c4..6147eda08fc 100644
--- a/src/main/java/cn/nukkit/command/defaults/BanCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/BanCommand.java
@@ -9,7 +9,7 @@
import cn.nukkit.lang.TranslationContainer;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BanCommand extends VanillaCommand {
@@ -20,8 +20,8 @@ public BanCommand(String name) {
this.commandParameters.clear();
this.commandParameters.put("default",
new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newType("reason", true, CommandParamType.STRING)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("reason", CommandParamType.STRING, true)
});
}
@@ -40,7 +40,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
String name = args[0].replace("@s", sender.getName());
StringBuilder reason = new StringBuilder();
for (int i = 1; i < args.length; i++) {
- reason.append(args[i]).append(" ");
+ reason.append(args[i]).append(' ');
}
if (reason.length() > 0) {
@@ -51,7 +51,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
Player player = sender.getServer().getPlayerExact(name);
if (player != null) {
- player.kick(PlayerKickEvent.Reason.NAME_BANNED, (reason.length() > 0) ? "Banned by admin. Reason: " + reason : "Banned by admin");
+ player.kick(PlayerKickEvent.Reason.NAME_BANNED, (reason.length() > 0) ? "You are banned! Reason: " + reason : "You are banned!", true);
}
Command.broadcastCommandMessage(sender, new TranslationContainer("%commands.ban.success", player != null ? player.getName() : name));
diff --git a/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java b/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java
index 7a1b48bb89e..1a6369a97ed 100644
--- a/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java
@@ -11,15 +11,14 @@
import cn.nukkit.nbt.tag.CompoundTag;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.util.ArrayList;
+import java.nio.file.Files;
import java.util.regex.Pattern;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class BanIpCommand extends VanillaCommand {
@@ -30,12 +29,8 @@ public BanIpCommand(String name) {
this.setAliases(new String[]{"banip"});
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newType("reason", true, CommandParamType.STRING)
- });
- this.commandParameters.put("byIp", new CommandParameter[]{
- CommandParameter.newType("ip", CommandParamType.STRING),
- CommandParameter.newType("reason", true, CommandParamType.STRING)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("reason", CommandParamType.STRING, true)
});
}
@@ -54,7 +49,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
String value = args[0];
StringBuilder reason = new StringBuilder();
for (int i = 1; i < args.length; i++) {
- reason.append(args[i]).append(" ");
+ reason.append(args[i]).append(' ');
}
if (reason.length() > 0) {
@@ -62,13 +57,13 @@ 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)) {
- this.processIPBan(value, sender, reason.toString());
+ processIPBan(value, sender, reason.toString());
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.banip.success", value));
} else {
- Player player = sender.getServer().getPlayer(value);
+ Player player = sender.getServer().getPlayerExact(value);
if (player != null) {
- this.processIPBan(player.getAddress(), sender, reason.toString());
+ processIPBan(player.getAddress(), sender, reason.toString());
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.banip.success.players", player.getAddress(), player.getName()));
} else {
@@ -78,14 +73,14 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
CompoundTag nbt = null;
if (file.exists()) {
try {
- nbt = NBTIO.readCompressed(new FileInputStream(file));
+ nbt = NBTIO.readCompressed(Files.newInputStream(file.toPath()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (nbt != null && nbt.contains("lastIP") && 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 = nbt.getString("lastIP")))) {
- this.processIPBan(value, sender, reason.toString());
+ processIPBan(value, sender, reason.toString());
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.banip.success", value));
} else {
@@ -98,19 +93,17 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
- private void processIPBan(String ip, CommandSender sender, String reason) {
+ private static void processIPBan(String ip, CommandSender sender, String reason) {
sender.getServer().getIPBans().addBan(ip, reason, null, sender.getName());
- for (Player player : new ArrayList<>(sender.getServer().getOnlinePlayers().values())) {
+ for (Player player : /*new ArrayList<>(*/sender.getServer().getOnlinePlayers().values()/*)*/) {
if (player.getAddress().equals(ip)) {
- player.kick(PlayerKickEvent.Reason.IP_BANNED, !reason.isEmpty() ? reason : "IP banned");
+ player.kick(PlayerKickEvent.Reason.IP_BANNED, !reason.isEmpty() ? reason : "IP banned", true);
}
}
try {
- sender.getServer().getNetwork().blockAddress(InetAddress.getByName(ip), -1);
- } catch (UnknownHostException e) {
- // ignore
- }
+ sender.getServer().getNetwork().blockAddress(InetAddress.getByName(ip));
+ } catch (UnknownHostException ignore) {}
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/BanListCommand.java b/src/main/java/cn/nukkit/command/defaults/BanListCommand.java
index 0ca654c4e88..3da8e639593 100644
--- a/src/main/java/cn/nukkit/command/defaults/BanListCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/BanListCommand.java
@@ -1,7 +1,6 @@
package cn.nukkit.command.defaults;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.lang.TranslationContainer;
import cn.nukkit.permission.BanEntry;
@@ -14,12 +13,13 @@
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class BanListCommand extends VanillaCommand {
+
public BanListCommand(String name) {
super(name, "%nukkit.command.banlist.description", "%commands.banlist.usage");
this.setPermission("nukkit.command.ban.list");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newEnum("type", true, new CommandEnum("BanListType", "ips", "players"))
+ new CommandParameter("ips|players", true)
});
}
diff --git a/src/main/java/cn/nukkit/command/defaults/ClearCommand.java b/src/main/java/cn/nukkit/command/defaults/ClearCommand.java
index fad7ba1074e..5a7cc71c5ca 100644
--- a/src/main/java/cn/nukkit/command/defaults/ClearCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/ClearCommand.java
@@ -9,14 +9,14 @@
import cn.nukkit.lang.TranslationContainer;
import cn.nukkit.utils.TextFormat;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
/**
- * Created on 2024/26/2 by Sherko231.
- * Package cn.nukkit.command.defaults in project Nukkit .
+ * @author Sherko231
*/
public class ClearCommand extends VanillaCommand {
+
public ClearCommand(String name) {
super(name, "%nukkit.command.clear.description", "%nukkit.command.clear.usage");
this.setPermission("nukkit.command.clear");
@@ -38,16 +38,14 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return false;
}
- List targets = new ArrayList<>();
+ Collection targets;
if (args[0].equals("@a")) {
- targets.addAll(Server.getInstance().getOnlinePlayers().values());
- }
- else {
- Player target = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ targets = Server.getInstance().getOnlinePlayers().values();
+ } else {
+ Player target = sender.getServer().getPlayerExact(args[0].replace("@s", sender.getName()));
if (target != null) {
- targets.add(target);
- }
- else {
+ targets = Collections.singletonList(target);
+ } else {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.player.notFound"));
return false;
}
diff --git a/src/main/java/cn/nukkit/command/defaults/ConvertCommand.java b/src/main/java/cn/nukkit/command/defaults/ConvertCommand.java
new file mode 100644
index 00000000000..6d01514de3e
--- /dev/null
+++ b/src/main/java/cn/nukkit/command/defaults/ConvertCommand.java
@@ -0,0 +1,80 @@
+package cn.nukkit.command.defaults;
+
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.command.CommandSender;
+import cn.nukkit.command.data.CommandParamType;
+import cn.nukkit.command.data.CommandParameter;
+import cn.nukkit.lang.TranslationContainer;
+import cn.nukkit.level.Level;
+import cn.nukkit.level.format.generic.Anvil2LevelDBConverter;
+import cn.nukkit.level.format.leveldb.LevelDBProvider;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ConvertCommand extends VanillaCommand {
+
+ private static final Set conversionInProgress = ConcurrentHashMap.newKeySet();
+
+ public ConvertCommand(String name) {
+ super(name, "%nukkit.command.world.convert.description", "%nukkit.command.world.convert.usage");
+ this.setPermission("nukkit.command.world.convert");
+ this.commandParameters.clear();
+ this.commandParameters.put("default", new CommandParameter[]{
+ new CommandParameter("world", CommandParamType.STRING, false)
+ });
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
+ if (!this.testPermission(sender)) {
+ return true;
+ }
+
+ if (sender instanceof Player) {
+ sender.sendMessage("§cThis command can be used only via console");
+ return true;
+ }
+
+ if (args.length < 1) {
+ sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
+ return false;
+ }
+
+ String worldName = String.join(" ", args);
+ Level level = Server.getInstance().getLevelByName(worldName);
+ if (level == null) {
+ sender.sendMessage("Unknown level: " + worldName);
+ return true;
+ }
+
+ if (level.getProvider() instanceof LevelDBProvider) {
+ sender.sendMessage(worldName + " is already in LevelDB format");
+ return true;
+ }
+
+ if (!level.getPlayers().isEmpty()) {
+ sender.sendMessage(worldName + " has players in it! Make sure the world is not used while it's being converted");
+ return true;
+ }
+
+ if (conversionInProgress.contains(worldName)) {
+ sender.sendMessage(worldName + " is already being converted");
+ return true;
+ }
+
+ conversionInProgress.add(worldName);
+
+ Anvil2LevelDBConverter converter = new Anvil2LevelDBConverter(level);
+ converter.convert().whenComplete((ignore, error) -> {
+ if (error != null) {
+ sender.sendMessage("Error during conversion!");
+ Server.getInstance().getLogger().logException(error);
+ }
+
+ conversionInProgress.remove(worldName);
+ });
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/command/defaults/DebugPasteCommand.java b/src/main/java/cn/nukkit/command/defaults/DebugPasteCommand.java
deleted file mode 100644
index 57e765d08bb..00000000000
--- a/src/main/java/cn/nukkit/command/defaults/DebugPasteCommand.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package cn.nukkit.command.defaults;
-
-import cn.nukkit.Server;
-import cn.nukkit.command.CommandSender;
-import cn.nukkit.network.protocol.ProtocolInfo;
-import cn.nukkit.plugin.Plugin;
-import cn.nukkit.plugin.PluginDescription;
-import cn.nukkit.scheduler.AsyncTask;
-import cn.nukkit.utils.HastebinUtility;
-import cn.nukkit.utils.MainLogger;
-import cn.nukkit.utils.Utils;
-import java.io.File;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-
-public class DebugPasteCommand extends VanillaCommand {
-
- public DebugPasteCommand(String name) {
- super(name, "%nukkit.command.debug.description", "%nukkit.command.debug.usage");
- this.setPermission("nukkit.command.debug.perform");
- this.commandParameters.clear();
- }
-
- @Override
- public boolean execute(CommandSender sender, String commandLabel, String[] args) {
- if (!this.testPermission(sender)) {
- return true;
- }
- Server server = Server.getInstance();
- server.getScheduler().scheduleAsyncTask(new AsyncTask() {
- @Override
- public void onRun() {
- try {
- new StatusCommand("status").execute(server.getConsoleSender(), "status", new String[]{});
- String dataPath = server.getDataPath();
- String nukkitYML = HastebinUtility.upload(new File(dataPath, "nukkit.yml"));
- String serverProperties = HastebinUtility.upload(new File(dataPath, "server.properties"));
- String latestLog = HastebinUtility.upload(new File(dataPath, "/logs/server.log"));
- String threadDump = HastebinUtility.upload(Utils.getAllThreadDumps());
-
- StringBuilder b = new StringBuilder();
- b.append("# Files\n");
- b.append("links.nukkit_yml: ").append(nukkitYML).append('\n');
- b.append("links.server_properties: ").append(serverProperties).append('\n');
- b.append("links.server_log: ").append(latestLog).append('\n');
- b.append("links.thread_dump: ").append(threadDump).append('\n');
- b.append("\n# Server Information\n");
-
- b.append("version.api: ").append(server.getApiVersion()).append('\n');
- b.append("version.nukkit: ").append(server.getNukkitVersion()).append('\n');
- b.append("version.minecraft: ").append(server.getVersion()).append('\n');
- b.append("version.protocol: ").append(ProtocolInfo.CURRENT_PROTOCOL).append('\n');
- b.append("plugins:");
- for (Plugin plugin : server.getPluginManager().getPlugins().values()) {
- boolean enabled = plugin.isEnabled();
- String name = plugin.getName();
- PluginDescription desc = plugin.getDescription();
- String version = desc.getVersion();
- b.append("\n ")
- .append(name)
- .append(":\n ")
- .append("version: '")
- .append(version)
- .append('\'')
- .append("\n enabled: ")
- .append(enabled);
- }
- b.append("\n\n# Java Details\n");
- Runtime runtime = Runtime.getRuntime();
- b.append("memory.free: ").append(runtime.freeMemory()).append('\n');
- b.append("memory.max: ").append(runtime.maxMemory()).append('\n');
- b.append("cpu.runtime: ").append(ManagementFactory.getRuntimeMXBean().getUptime()).append('\n');
- b.append("cpu.processors: ").append(runtime.availableProcessors()).append('\n');
- b.append("java.specification.version: '").append(System.getProperty("java.specification.version")).append("'\n");
- b.append("java.vendor: '").append(System.getProperty("java.vendor")).append("'\n");
- b.append("java.version: '").append(System.getProperty("java.version")).append("'\n");
- b.append("os.arch: '").append(System.getProperty("os.arch")).append("'\n");
- b.append("os.name: '").append(System.getProperty("os.name")).append("'\n");
- b.append("os.version: '").append(System.getProperty("os.version")).append("'\n\n");
- b.append("\n# Create a ticket: https://github.com/NukkitX/Nukkit/issues/new");
- String link = HastebinUtility.upload(b.toString());
- sender.sendMessage(link);
- } catch (IOException e) {
- MainLogger.getLogger().logException(e);
- }
- }
- });
- return true;
- }
-}
diff --git a/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java b/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java
index 73604ce3cd3..a9940d858e6 100644
--- a/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/DefaultGamemodeCommand.java
@@ -1,11 +1,12 @@
package cn.nukkit.command.defaults;
import cn.nukkit.Server;
+import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.lang.TranslationContainer;
+import cn.nukkit.network.protocol.SetDefaultGameTypePacket;
/**
* Created on 2015/11/12 by xtypr.
@@ -18,10 +19,11 @@ public DefaultGamemodeCommand(String name) {
this.setPermission("nukkit.command.defaultgamemode");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("gameMode", CommandParamType.INT)
+ new CommandParameter("mode", CommandParamType.INT, false)
});
this.commandParameters.put("byString", new CommandParameter[]{
- CommandParameter.newEnum("gameMode", CommandEnum.ENUM_GAMEMODE)
+ new CommandParameter("mode", new String[]{"survival", "creative", "s", "c",
+ "adventure", "a", "spectator", "view", "v"})
});
}
@@ -31,15 +33,21 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
if (args.length == 0) {
- sender.sendMessage(new TranslationContainer("commands.generic.usage", new String[]{this.usageMessage}));
+ sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return false;
}
int gameMode = Server.getGamemodeFromString(args[0]);
if (gameMode != -1) {
sender.getServer().setPropertyInt("gamemode", gameMode);
+ sender.getServer().gamemode = gameMode;
sender.sendMessage(new TranslationContainer("commands.defaultgamemode.success", new String[]{Server.getGamemodeString(gameMode)}));
+ Command.broadcastCommandMessage(sender, new TranslationContainer("commands.defaultgamemode.success", new String[]{Server.getGamemodeString(sender.getServer().getDefaultGamemode())}));
+
+ SetDefaultGameTypePacket gameTypePacket = new SetDefaultGameTypePacket();
+ gameTypePacket.gamemode = sender.getServer().getDefaultGamemode();
+ Server.broadcastPacket(sender.getServer().getOnlinePlayers().values(), gameTypePacket);
} else {
- sender.sendMessage("Unknown game mode"); //
+ sender.sendMessage("Unknown game mode");
}
return true;
}
diff --git a/src/main/java/cn/nukkit/command/defaults/DeopCommand.java b/src/main/java/cn/nukkit/command/defaults/DeopCommand.java
index 08ba7b58c16..c3c7a6cd353 100644
--- a/src/main/java/cn/nukkit/command/defaults/DeopCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/DeopCommand.java
@@ -14,11 +14,13 @@
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class DeopCommand extends VanillaCommand {
+
public DeopCommand(String name) {
super(name, "%nukkit.command.deop.description", "%commands.deop.description");
this.setPermission("nukkit.command.op.take");
+ this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET)
+ new CommandParameter("player", CommandParamType.TARGET, false)
});
}
@@ -34,16 +36,16 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return false;
}
- String playerName = args[0].replace("@s", sender.getName());
- IPlayer player = sender.getServer().getOfflinePlayer(playerName);
- player.setOp(false);
-
+ String name = args[0].replace("@s", sender.getName());
+ IPlayer player = sender.getServer().getOfflinePlayer(name);
if (player instanceof Player) {
+ player.setOp(false);
((Player) player).sendMessage(new TranslationContainer(TextFormat.GRAY + "%commands.deop.message"));
+ } else {
+ sender.getServer().removeOp(name);
}
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.deop.success", new String[]{player.getName()}));
-
return true;
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java b/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java
index 1b0dcf97a53..4cb03129bb9 100644
--- a/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/DifficultyCommand.java
@@ -3,14 +3,11 @@
import cn.nukkit.Server;
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.lang.TranslationContainer;
import cn.nukkit.network.protocol.SetDifficultyPacket;
-import java.util.ArrayList;
-
/**
* Created on 2015/11/12 by xtypr.
* Package cn.nukkit.command.defaults in project Nukkit .
@@ -22,10 +19,11 @@ public DifficultyCommand(String name) {
this.setPermission("nukkit.command.difficulty");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("difficulty", CommandParamType.INT)
+ new CommandParameter("difficulty", CommandParamType.INT, false)
});
this.commandParameters.put("byString", new CommandParameter[]{
- CommandParameter.newEnum("difficulty", new CommandEnum("Difficulty", "peaceful", "p", "easy", "e", "normal", "n", "hard", "h"))
+ new CommandParameter("difficulty", new String[]{"peaceful", "p", "easy", "e",
+ "normal", "n", "hard", "h"})
});
}
@@ -51,7 +49,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
SetDifficultyPacket pk = new SetDifficultyPacket();
pk.difficulty = sender.getServer().getDifficulty();
- Server.broadcastPacket(new ArrayList<>(sender.getServer().getOnlinePlayers().values()), pk);
+ Server.broadcastPacket(sender.getServer().getOnlinePlayers().values(), pk);
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.difficulty.success", String.valueOf(difficulty)));
} else {
diff --git a/src/main/java/cn/nukkit/command/defaults/EffectCommand.java b/src/main/java/cn/nukkit/command/defaults/EffectCommand.java
index d60f51d49a2..e5365cf811e 100644
--- a/src/main/java/cn/nukkit/command/defaults/EffectCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/EffectCommand.java
@@ -12,38 +12,26 @@
import cn.nukkit.utils.ServerException;
import cn.nukkit.utils.TextFormat;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Created by Snake1999 and Pub4Game on 2016/1/23.
* Package cn.nukkit.command.defaults in project nukkit.
*/
public class EffectCommand extends Command {
+
public EffectCommand(String name) {
super(name, "%nukkit.command.effect.description", "%commands.effect.usage");
this.setPermission("nukkit.command.effect");
this.commandParameters.clear();
-
- List effects = new ArrayList<>();
- for (Field field : Effect.class.getDeclaredFields()) {
- if (field.getType() == int.class && field.getModifiers() == (Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL)) {
- effects.add(field.getName().toLowerCase());
- }
- }
-
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("effect", new CommandEnum("Effect", effects)),
- CommandParameter.newType("seconds", true, CommandParamType.INT),
- CommandParameter.newType("amplifier", true, CommandParamType.INT),
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("effect", CommandParamType.STRING, false), //Do not use Enum here because of buggy behavior
+ new CommandParameter("seconds", CommandParamType.INT, true),
+ new CommandParameter("amplifier", true),
CommandParameter.newEnum("hideParticle", true, CommandEnum.ENUM_BOOLEAN)
});
this.commandParameters.put("clear", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("clear", new CommandEnum("ClearEffects", "clear"))
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("clear", new String[]{"clear"})
});
}
@@ -56,7 +44,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return true;
}
- Player player = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ Player player = sender.getServer().getPlayerExact(args[0].replace("@s", sender.getName()));
if (player == null) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.player.notFound"));
return true;
@@ -79,7 +67,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
}
- int duration = 300;
+ int duration = 600;
int amplification = 0;
if (args.length >= 3) {
try {
@@ -110,7 +98,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
}
if (duration == 0) {
if (!player.hasEffect(effect.getId())) {
- if (player.getEffects().size() == 0) {
+ if (player.getEffects().isEmpty()) {
sender.sendMessage(new TranslationContainer("commands.effect.failure.notActive.all", player.getDisplayName()));
} else {
sender.sendMessage(new TranslationContainer("commands.effect.failure.notActive", effect.getName(), player.getDisplayName()));
diff --git a/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java b/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java
index 77228636b95..7886a5254bb 100644
--- a/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/EnchantCommand.java
@@ -3,7 +3,6 @@
import cn.nukkit.Player;
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.item.Item;
@@ -21,19 +20,14 @@ public EnchantCommand(String name) {
this.setPermission("nukkit.command.enchant");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newType("enchantmentId", CommandParamType.INT),
- CommandParameter.newType("level", true, CommandParamType.INT)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("enchantment ID", CommandParamType.INT, false),
+ new CommandParameter("level", CommandParamType.INT, true)
});
this.commandParameters.put("byName", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("enchantmentName", new CommandEnum("Enchant",
- "protection", "fire_protection", "feather_falling", "blast_protection", "projectile_projection", "thorns", "respiration",
- "aqua_affinity", "depth_strider", "sharpness", "smite", "bane_of_arthropods", "knockback", "fire_aspect", "looting", "efficiency",
- "silk_touch", "durability", "fortune", "power", "punch", "flame", "infinity", "luck_of_the_sea", "lure", "frost_walker", "mending",
- "binding_curse", "vanishing_curse", "impaling", "loyalty", "riptide", "channeling", "multishot", "piercing", "quick_charge",
- "soul_speed")),
- CommandParameter.newType("level", true, CommandParamType.INT)
+ new CommandParameter("player", CommandParameter.ARG_TYPE_TARGET, false),
+ new CommandParameter("id", false, CommandParameter.ENUM_TYPE_ENCHANTMENT_LIST),
+ new CommandParameter("level", CommandParamType.INT, true)
});
}
@@ -46,7 +40,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return true;
}
- Player player = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ Player player = sender.getServer().getPlayerExact(args[0].replace("@s", sender.getName()));
if (player == null) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.player.notFound"));
return true;
@@ -73,7 +67,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
}
item.addEnchantment(enchantment);
player.getInventory().setItemInHand(item);
- Command.broadcastCommandMessage(sender, new TranslationContainer("%commands.enchant.success"));
+ Command.broadcastCommandMessage(sender, new TranslationContainer("%commands.enchant.success", player.getDisplayName()));
return true;
}
@@ -88,7 +82,7 @@ public int getIdByName(String value) throws NumberFormatException {
return 2;
case "blast_protection":
return 3;
- case "projectile_projection":
+ case "projectile_protection":
return 4;
case "thorns":
return 5;
diff --git a/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java b/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java
index ef43d0476fb..89a6130f53a 100644
--- a/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/GamemodeCommand.java
@@ -27,11 +27,11 @@ public GamemodeCommand(String name) {
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
CommandParameter.newType("gameMode", CommandParamType.INT),
- CommandParameter.newType("player", true, CommandParamType.TARGET)
+ new CommandParameter("player", CommandParamType.TARGET, true)
});
this.commandParameters.put("byString", new CommandParameter[]{
CommandParameter.newEnum("gameMode", CommandEnum.ENUM_GAMEMODE),
- CommandParameter.newType("player", true, CommandParamType.TARGET)
+ new CommandParameter("player", CommandParamType.TARGET, true)
});
}
@@ -51,7 +51,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
CommandSender target = sender;
if (args.length > 1) {
if (sender.hasPermission("nukkit.command.gamemode.other")) {
- target = sender.getServer().getPlayer(args[1].replace("@s", sender.getName()));
+ target = sender.getServer().getPlayerExact(args[1].replace("@s", sender.getName()));
if (target == null) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.player.notFound"));
return true;
@@ -79,7 +79,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (target.equals(sender)) {
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.gamemode.success.self", Server.getGamemodeString(gameMode)));
} else {
- target.sendMessage(new TranslationContainer("gameMode.changed", Server.getGamemodeString(gameMode)));
+ target.sendMessage(new TranslationContainer("gameMode.changed"));
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.gamemode.success.other", target.getName(), Server.getGamemodeString(gameMode)));
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java b/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java
index 1015a4cc662..be736ce86f2 100644
--- a/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/GameruleCommand.java
@@ -9,16 +9,12 @@
import cn.nukkit.level.GameRule;
import cn.nukkit.level.GameRules;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-import java.util.StringJoiner;
+import java.util.*;
public class GameruleCommand extends VanillaCommand {
public GameruleCommand(String name) {
- super(name, "%nukkit.command.gamerule.description", "%nukkit.command.gamerule.usage");
+ super(name, "%nukkit.command.gamerule.description", "%commands.gamerule.usage");
this.setPermission("nukkit.command.gamerule");
this.commandParameters.clear();
@@ -79,7 +75,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
}
if (!sender.isPlayer()) {
- sender.sendMessage(new TranslationContainer("%commands.generic.ingame"));
+ sender.sendMessage(new TranslationContainer("commands.generic.ingame"));
return true;
}
GameRules rules = ((Player) sender).getLevel().getGameRules();
@@ -99,14 +95,13 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
- sender.sendMessage(gameRule.get().getName() .toLowerCase()+ " = " + rules.getString(gameRule.get()));
+ sender.sendMessage(gameRule.get().getName().toLowerCase() + " = " + rules.getString(gameRule.get()));
return true;
default:
Optional optionalRule = GameRule.parseString(args[0]);
if (!optionalRule.isPresent()) {
- sender.sendMessage(new TranslationContainer("commands.generic.syntax",
- "/gamerule ", args[0], " " + String.join(" ", Arrays.copyOfRange(args, 1, args.length))));
+ sender.sendMessage(new TranslationContainer("commands.generic.syntax", "/gamerule ", args[0], ' ' + String.join(" ", Arrays.copyOfRange(args, 1, args.length))));
return true;
}
@@ -114,9 +109,9 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
rules.setGameRules(optionalRule.get(), args[1]);
sender.sendMessage(new TranslationContainer("commands.gamerule.success", optionalRule.get().getName().toLowerCase(), args[1]));
} catch (IllegalArgumentException e) {
- sender.sendMessage(new TranslationContainer("commands.generic.syntax", "/gamerule " + args[0] + " ", args[1], " " + String.join(" ", Arrays.copyOfRange(args, 2, args.length))));
+ sender.sendMessage(new TranslationContainer("commands.generic.syntax", "/gamerule " + args[0] + ' ', args[1], ' ' + String.join(" ", Arrays.copyOfRange(args, 2, args.length))));
}
return true;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/command/defaults/GarbageCollectorCommand.java b/src/main/java/cn/nukkit/command/defaults/GarbageCollectorCommand.java
index 94cb5c126c9..adc74d229e5 100644
--- a/src/main/java/cn/nukkit/command/defaults/GarbageCollectorCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/GarbageCollectorCommand.java
@@ -30,12 +30,12 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
for (Level level : sender.getServer().getLevels().values()) {
int chunksCount = level.getChunks().size();
- int entitiesCount = level.getEntities().length;
+ int entitiesCount = level.entities.size();
int tilesCount = level.getBlockEntities().size();
level.doChunkGarbageCollection();
level.unloadChunks(true);
chunksCollected += chunksCount - level.getChunks().size();
- entitiesCollected += entitiesCount - level.getEntities().length;
+ entitiesCollected += entitiesCount - level.entities.size();
tilesCollected += tilesCount - level.getBlockEntities().size();
}
diff --git a/src/main/java/cn/nukkit/command/defaults/GiveCommand.java b/src/main/java/cn/nukkit/command/defaults/GiveCommand.java
index 0c325aea344..e5fe2a5724f 100644
--- a/src/main/java/cn/nukkit/command/defaults/GiveCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/GiveCommand.java
@@ -4,83 +4,88 @@
import cn.nukkit.Server;
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.item.Item;
import cn.nukkit.lang.TranslationContainer;
import cn.nukkit.utils.TextFormat;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
/**
* Created on 2015/12/9 by xtypr.
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class GiveCommand extends VanillaCommand {
+
public GiveCommand(String name) {
super(name, "%nukkit.command.give.description", "%nukkit.command.give.usage");
this.setPermission("nukkit.command.give");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("itemName", CommandEnum.ENUM_ITEM),
- CommandParameter.newType("amount", true, CommandParamType.INT),
- CommandParameter.newType("tags", true, CommandParamType.RAWTEXT)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("itemName", false, CommandParameter.ENUM_TYPE_ITEM_LIST),
+ new CommandParameter("amount", CommandParamType.INT, true),
+ new CommandParameter("meta", CommandParamType.INT, true),
+ new CommandParameter("tags...", CommandParamType.RAWTEXT, true)
});
this.commandParameters.put("toPlayerById", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newType("itemId", CommandParamType.INT),
- CommandParameter.newType("amount", true, CommandParamType.INT),
- CommandParameter.newType("tags", true, CommandParamType.RAWTEXT)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("item ID", CommandParamType.INT, false),
+ new CommandParameter("amount", CommandParamType.INT, true),
+ new CommandParameter("tags...", CommandParamType.RAWTEXT, true)
});
this.commandParameters.put("toPlayerByIdMeta", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newType("itemAndData", CommandParamType.STRING),
- CommandParameter.newType("amount", true, CommandParamType.INT),
- CommandParameter.newType("tags", true, CommandParamType.RAWTEXT)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("item ID:meta", CommandParamType.RAWTEXT, false),
+ new CommandParameter("amount", CommandParamType.INT, true),
+ new CommandParameter("tags...", CommandParamType.RAWTEXT, true)
});
}
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
if (!this.testPermission(sender)) {
- return false;
+ return true;
}
if (args.length < 2) {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
- return false;
+ return true;
}
- List targets = new ArrayList<>();
+ Collection targets;
if (args[0].equals("@a")) {
- targets.addAll(Server.getInstance().getOnlinePlayers().values());
- }
- else {
- Player target = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ targets = Server.getInstance().getOnlinePlayers().values();
+ } else {
+ Player target = sender.getServer().getPlayerExact(args[0].replace("@s", sender.getName()));
if (target != null) {
- targets.add(target);
- }
- else {
+ targets = Collections.singletonList(target);
+ } else {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.player.notFound"));
return false;
}
}
Item item;
+
try {
- item = Item.fromString(args[1]);
+ item = Item.fromString(args[1].replace("minecraft:", ""));
} catch (Exception e) {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
- return false;
+ return true;
+ }
+
+ if (item.getDamage() < 0) {
+ sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
+ return true;
}
try {
item.setCount(Integer.parseInt(args[2]));
} catch (Exception e) {
- item.setCount(item.getMaxStackSize());
+ item.setCount(1);
}
if (item.getId() == 0) {
diff --git a/src/main/java/cn/nukkit/command/defaults/HelpCommand.java b/src/main/java/cn/nukkit/command/defaults/HelpCommand.java
index 124c35b46f1..206f31c0003 100644
--- a/src/main/java/cn/nukkit/command/defaults/HelpCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/HelpCommand.java
@@ -12,7 +12,7 @@
import java.util.TreeMap;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class HelpCommand extends VanillaCommand {
@@ -22,7 +22,7 @@ public HelpCommand(String name) {
this.setPermission("nukkit.command.help");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("page", true, CommandParamType.INT)
+ new CommandParameter("page", CommandParamType.INT, true)
});
}
@@ -50,16 +50,16 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
args = new String[0];
}*/
for (String arg : args) {
- if (!command.toString().equals("")) {
- command.append(" ");
+ if (command.length() > 0) {
+ command.append(' ');
}
command.append(arg);
}
} catch (NumberFormatException e) {
pageNumber = 1;
for (String arg : args) {
- if (!command.toString().equals("")) {
- command.append(" ");
+ if (command.length() > 0) {
+ command.append(' ');
}
command.append(arg);
}
@@ -70,7 +70,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
pageHeight = Integer.MAX_VALUE;
}
- if (command.toString().equals("")) {
+ if (command.length() == 0) {
Map commands = new TreeMap<>();
for (Command cmd : sender.getServer().getCommandMap().getCommands().values()) {
if (cmd.testPermissionSilent(sender)) {
@@ -98,16 +98,16 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (cmd != null) {
if (cmd.testPermissionSilent(sender)) {
String message = TextFormat.YELLOW + "--------- " + TextFormat.WHITE + " Help: /" + cmd.getName() + TextFormat.YELLOW + " ---------\n";
- message += TextFormat.GOLD + "Description: " + TextFormat.WHITE + cmd.getDescription() + "\n";
+ message += TextFormat.GOLD + "Description: " + TextFormat.WHITE + cmd.getDescription() + '\n';
StringBuilder usage = new StringBuilder();
String[] usages = cmd.getUsage().split("\n");
for (String u : usages) {
- if (!usage.toString().equals("")) {
+ if (usage.length() > 0) {
usage.append("\n" + TextFormat.WHITE);
}
usage.append(u);
}
- message += TextFormat.GOLD + "Usage: " + TextFormat.WHITE + usage + "\n";
+ message += TextFormat.GOLD + "Usage: " + TextFormat.WHITE + usage + '\n';
sender.sendMessage(message);
return true;
}
diff --git a/src/main/java/cn/nukkit/command/defaults/KickCommand.java b/src/main/java/cn/nukkit/command/defaults/KickCommand.java
index a73db169b5d..737ab8f1894 100644
--- a/src/main/java/cn/nukkit/command/defaults/KickCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/KickCommand.java
@@ -20,8 +20,8 @@ public KickCommand(String name) {
this.setPermission("nukkit.command.kick");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newType("reason", true, CommandParamType.MESSAGE)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("reason", CommandParamType.STRING, false),
});
}
@@ -39,16 +39,16 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
StringBuilder reason = new StringBuilder();
for (int i = 1; i < args.length; i++) {
- reason.append(args[i]).append(" ");
+ reason.append(args[i]).append(' ');
}
if (reason.length() > 0) {
reason = new StringBuilder(reason.substring(0, reason.length() - 1));
}
- Player player = sender.getServer().getPlayer(name);
+ Player player = sender.getServer().getPlayerExact(name);
if (player != null) {
- player.kick(PlayerKickEvent.Reason.KICKED_BY_ADMIN, reason.toString());
+ player.kick(PlayerKickEvent.Reason.KICKED_BY_ADMIN, reason.toString(), true);
if (reason.length() >= 1) {
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.kick.success.reason", player.getName(), reason.toString())
);
diff --git a/src/main/java/cn/nukkit/command/defaults/KillCommand.java b/src/main/java/cn/nukkit/command/defaults/KillCommand.java
index e51262dcec1..f44cb3cb690 100644
--- a/src/main/java/cn/nukkit/command/defaults/KillCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/KillCommand.java
@@ -23,11 +23,10 @@ public class KillCommand extends VanillaCommand {
public KillCommand(String name) {
super(name, "%nukkit.command.kill.description", "%nukkit.command.kill.usage", new String[]{"suicide"});
- this.setPermission("nukkit.command.kill.self;"
- + "nukkit.command.kill.other");
+ this.setPermission("nukkit.command.kill.self;nukkit.command.kill.other");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", true, CommandParamType.TARGET)
+ new CommandParameter("player", CommandParamType.TARGET, true)
});
}
@@ -45,20 +44,15 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.permission"));
return true;
}
- Player player = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ Player player = sender.getServer().getPlayerExact(args[0]);
if (player != null) {
if (player.isCreative() || player.isSpectator()) {
sender.sendMessage(TextFormat.RED + "No targets matched selector");
return true;
}
- EntityDamageEvent ev = new EntityDamageEvent(player, DamageCause.SUICIDE, 1000);
- sender.getServer().getPluginManager().callEvent(ev);
- if (ev.isCancelled()) {
- return true;
+ if (resetHealth(player)) {
+ Command.broadcastCommandMessage(sender, new TranslationContainer("commands.kill.successful", player.getName()));
}
- player.setLastDamageCause(ev);
- player.setHealth(0);
- Command.broadcastCommandMessage(sender, new TranslationContainer("commands.kill.successful", player.getName()));
} else if (args[0].equals("@e")) {
StringJoiner joiner = new StringJoiner(", ");
for (Level level : Server.getInstance().getLevels().values()) {
@@ -91,33 +85,22 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(TextFormat.RED + "No targets matched selector");
return true;
}
- EntityDamageEvent ev = new EntityDamageEvent(p, DamageCause.SUICIDE, 1000);
- sender.getServer().getPluginManager().callEvent(ev);
- if (ev.isCancelled()) {
- return true;
+ if (resetHealth(p)) {
+ sender.sendMessage(new TranslationContainer("commands.kill.successful", sender.getName()));
}
- p.setLastDamageCause(ev);
- p.setHealth(0);
- sender.sendMessage(new TranslationContainer("commands.kill.successful", sender.getName()));
} else if (args[0].equals("@a")) {
if (!sender.hasPermission("nukkit.command.kill.other")) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.permission"));
return true;
}
for (Level level : Server.getInstance().getLevels().values()) {
- for (Entity entity : level.getEntities()) {
+ for (Entity entity : level.entities.values()) {
if (entity instanceof Player) {
Player p = (Player) entity;
if (p.isCreative() || p.isSpectator()) {
continue;
}
- EntityDamageEvent ev = new EntityDamageEvent(entity, DamageCause.SUICIDE, 1000);
- sender.getServer().getPluginManager().callEvent(ev);
- if (ev.isCancelled()) {
- continue;
- }
- entity.setLastDamageCause(ev);
- entity.setHealth(0);
+ resetHealth(p);
}
}
}
@@ -126,8 +109,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.player.notFound"));
}
return true;
- }
- if (sender instanceof Player) {
+ } else if (sender instanceof Player) {
if (!sender.hasPermission("nukkit.command.kill.self")) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.permission"));
return true;
@@ -137,18 +119,24 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(TextFormat.RED + "No targets matched selector");
return true;
}
- EntityDamageEvent ev = new EntityDamageEvent(player, DamageCause.SUICIDE, 1000);
- sender.getServer().getPluginManager().callEvent(ev);
- if (ev.isCancelled()) {
- return true;
+ if (resetHealth(player)) {
+ sender.sendMessage(new TranslationContainer("commands.kill.successful", sender.getName()));
}
- player.setLastDamageCause(ev);
- player.setHealth(0);
- sender.sendMessage(new TranslationContainer("commands.kill.successful", sender.getName()));
} else {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
+ }
+ return true;
+ }
+
+ private boolean resetHealth(Entity entity) {
+ EntityDamageEvent ev = new EntityDamageEvent(entity, DamageCause.SUICIDE, 1000);
+ entity.getServer().getPluginManager().callEvent(ev);
+ if (ev.isCancelled()) {
return false;
}
+ entity.setLastDamageCause(ev);
+ entity.removeAllEffects(); // Fix issue with absorption
+ entity.setHealth(0);
return true;
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/ListCommand.java b/src/main/java/cn/nukkit/command/defaults/ListCommand.java
index 7417211d0c2..60a0ca31ad5 100644
--- a/src/main/java/cn/nukkit/command/defaults/ListCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/ListCommand.java
@@ -34,8 +34,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
online = new StringBuilder(online.substring(0, online.length() - 2));
}
- sender.sendMessage(new TranslationContainer("commands.players.list",
- String.valueOf(onlineCount), String.valueOf(sender.getServer().getMaxPlayers())));
+ sender.sendMessage(new TranslationContainer("commands.players.list", String.valueOf(onlineCount), String.valueOf(sender.getServer().getMaxPlayers())));
sender.sendMessage(online.toString());
return true;
}
diff --git a/src/main/java/cn/nukkit/command/defaults/MeCommand.java b/src/main/java/cn/nukkit/command/defaults/MeCommand.java
index 57a35cfc010..70f4f81c850 100644
--- a/src/main/java/cn/nukkit/command/defaults/MeCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/MeCommand.java
@@ -14,11 +14,11 @@
public class MeCommand extends VanillaCommand {
public MeCommand(String name) {
- super(name, "%nukkit.command.me.description", "%commands.me.usage");
+ super(name, "%nukkit.command.me.description", "%nukkit.command.me.usage");
this.setPermission("nukkit.command.me");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("message", CommandParamType.MESSAGE)
+ new CommandParameter("action ...", CommandParamType.RAWTEXT, false)
});
}
@@ -43,7 +43,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
StringBuilder msg = new StringBuilder();
for (String arg : args) {
- msg.append(arg).append(" ");
+ msg.append(arg).append(' ');
}
if (msg.length() > 0) {
diff --git a/src/main/java/cn/nukkit/command/defaults/OpCommand.java b/src/main/java/cn/nukkit/command/defaults/OpCommand.java
index 6afc9fdcc6c..1b46b1c5465 100644
--- a/src/main/java/cn/nukkit/command/defaults/OpCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/OpCommand.java
@@ -16,11 +16,11 @@
public class OpCommand extends VanillaCommand {
public OpCommand(String name) {
- super(name, "%nukkit.command.op.description", "%commands.op.description");
+ super(name, "%nukkit.command.op.description", "%nukkit.command.op.usage");
this.setPermission("nukkit.command.op.give");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET)
+ new CommandParameter("player", CommandParamType.TARGET, false)
});
}
@@ -29,21 +29,22 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (!this.testPermission(sender)) {
return true;
}
+
if (args.length == 0) {
- sender.sendMessage(new TranslationContainer("commands.op.usage", this.usageMessage));
+ sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return false;
}
String name = args[0];
IPlayer player = sender.getServer().getOfflinePlayer(name);
-
- Command.broadcastCommandMessage(sender, new TranslationContainer("commands.op.success", player.getName()));
if (player instanceof Player) {
+ player.setOp(true);
((Player) player).sendMessage(new TranslationContainer(TextFormat.GRAY + "%commands.op.message"));
+ } else {
+ sender.getServer().addOp(name);
}
- player.setOp(true);
-
+ Command.broadcastCommandMessage(sender, new TranslationContainer("commands.op.success", player.getName()));
return true;
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/PardonCommand.java b/src/main/java/cn/nukkit/command/defaults/PardonCommand.java
index 0ffd5296b72..5f471969213 100644
--- a/src/main/java/cn/nukkit/command/defaults/PardonCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/PardonCommand.java
@@ -7,7 +7,7 @@
import cn.nukkit.lang.TranslationContainer;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class PardonCommand extends VanillaCommand {
@@ -18,7 +18,7 @@ public PardonCommand(String name) {
this.setAliases(new String[]{"unban"});
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET)
+ new CommandParameter("player", CommandParamType.TARGET, false)
});
}
diff --git a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java
index d3ae0695a85..91c8a113a88 100644
--- a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java
@@ -2,7 +2,6 @@
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.lang.TranslationContainer;
@@ -11,7 +10,7 @@
import java.util.regex.Pattern;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class PardonIpCommand extends VanillaCommand {
@@ -22,7 +21,7 @@ public PardonIpCommand(String name) {
this.setAliases(new String[]{"unbanip", "unban-ip", "pardonip"});
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("ip", CommandParamType.STRING)
+ new CommandParameter("ip")
});
}
diff --git a/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java b/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java
index aea1e78c51e..478593f7c09 100644
--- a/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/ParticleCommand.java
@@ -3,7 +3,6 @@
import cn.nukkit.Player;
import cn.nukkit.block.Block;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.item.Item;
@@ -12,7 +11,6 @@
import cn.nukkit.level.particle.*;
import cn.nukkit.math.Vector3;
-import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -20,19 +18,21 @@
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class ParticleCommand extends VanillaCommand {
- private static final String[] ENUM_VALUES = new String[]{"explode", "hugeexplosion", "hugeexplosionseed", "bubble"
+
+ private static final String[] ENUM_VALUES = {"explode", "hugeexplosion", "hugeexplosionseed", "bubble"
, "splash", "wake", "water", "crit", "smoke", "spell", "instantspell", "dripwater", "driplava", "townaura"
, "spore", "portal", "flame", "lava", "reddust", "snowballpoof", "slime", "itembreak", "terrain", "heart"
, "ink", "droplet", "enchantmenttable", "happyvillager", "angryvillager", "forcefield"};
+
public ParticleCommand(String name) {
super(name, "%nukkit.command.particle.description", "%nukkit.command.particle.usage");
this.setPermission("nukkit.command.particle");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newEnum("effect", new CommandEnum("Particle", ENUM_VALUES)),
- CommandParameter.newType("position", CommandParamType.POSITION),
- CommandParameter.newType("count", true, CommandParamType.INT),
- CommandParameter.newType("data", true, CommandParamType.INT)
+ new CommandParameter("name", false, ENUM_VALUES),
+ new CommandParameter("position", CommandParamType.POSITION, false),
+ new CommandParameter("count", CommandParamType.INT, true),
+ new CommandParameter("data", true)
});
}
@@ -44,7 +44,6 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (args.length < 4) {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
-
return true;
}
@@ -75,23 +74,18 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
try {
double c = Double.parseDouble(args[4]);
count = (int) c;
- } catch (Exception e) {
- //ignore
- }
+ } catch (Exception ignored) {}
}
count = Math.max(1, count);
int data = -1;
if (args.length > 5) {
try {
- double d = Double.parseDouble(args[5]);
+ double d = Double.parseDouble(args[8]);
data = (int) d;
- } catch (Exception e) {
- //ignore
- }
+ } catch (Exception ignored) {}
}
-
- Particle particle = this.getParticle(name, position, data);
+ Particle particle = getParticle(name, position, data);
if (particle == null) {
position.level.addParticleEffect(position.asVector3f(), args[0], -1, position.level.getDimension());
@@ -100,7 +94,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.particle.success", name, String.valueOf(count)));
- Random random = ThreadLocalRandom.current();
+ ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < count; i++) {
particle.setComponents(
@@ -114,7 +108,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
- private Particle getParticle(String name, Vector3 pos, int data) {
+ private static Particle getParticle(String name, Vector3 pos, int data) {
switch (name) {
case "explode":
return new ExplodeParticle(pos);
@@ -202,7 +196,7 @@ private Particle getParticle(String name, Vector3 pos, int data) {
return null;
}
- private static double getDouble(String arg, double defaultValue) throws Exception {
+ private static double getDouble(String arg, double defaultValue) {
if (arg.startsWith("~")) {
String relativePos = arg.substring(1);
if (relativePos.isEmpty()) {
diff --git a/src/main/java/cn/nukkit/command/defaults/PlaySoundCommand.java b/src/main/java/cn/nukkit/command/defaults/PlaySoundCommand.java
new file mode 100644
index 00000000000..f5ad9567ab5
--- /dev/null
+++ b/src/main/java/cn/nukkit/command/defaults/PlaySoundCommand.java
@@ -0,0 +1,78 @@
+package cn.nukkit.command.defaults;
+
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.command.CommandSender;
+import cn.nukkit.command.data.CommandParamType;
+import cn.nukkit.command.data.CommandParameter;
+import cn.nukkit.lang.TranslationContainer;
+
+public class PlaySoundCommand extends VanillaCommand {
+
+ public PlaySoundCommand(String name) {
+ super(name, "%nukkit.command.playsound.description", "%commands.playsound.usage");
+ this.setPermission("nukkit.command.playsound");
+ this.commandParameters.clear();
+ this.commandParameters.put("default", new CommandParameter[]{
+ new CommandParameter("sound", CommandParamType.STRING, false),
+ new CommandParameter("player", CommandParamType.TARGET, true)
+ });
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
+ if (!this.testPermission(sender)) {
+ return true;
+ }
+
+ if (args.length == 0) {
+ sender.sendMessage(new TranslationContainer("%commands.playsound.usage", this.usageMessage));
+ return false;
+ }
+
+ if (args.length == 1) {
+ if (!(sender instanceof Player)) {
+ sender.sendMessage(new TranslationContainer("commands.generic.ingame"));
+ return true;
+ }
+
+ Player p = (Player) sender;
+
+ p.getLevel().addSound(p, args[0], p);
+ p.sendMessage(new TranslationContainer("commands.playsound.success", args[0], p.getName()));
+
+ return true;
+ }
+
+ if (args[1].equalsIgnoreCase("@a")) {
+ for (Player p : Server.getInstance().getOnlinePlayers().values()) {
+ p.getLevel().addSound(p, args[0], p);
+ }
+
+ sender.sendMessage(new TranslationContainer("commands.playsound.success", args[0], "@a"));
+
+ return true;
+ }
+
+ if (args[1].equalsIgnoreCase("@s") && sender instanceof Player) {
+ Player p = (Player) sender;
+
+ p.getLevel().addSound(p, args[0], p);
+ sender.sendMessage(new TranslationContainer("commands.playsound.success", args[0], p.getName()));
+
+ return true;
+ }
+
+ Player p = Server.getInstance().getPlayerExact(args[1]);
+
+ if (p == null) {
+ sender.sendMessage(new TranslationContainer("commands.generic.player.notFound"));
+ return true;
+ }
+
+ p.getLevel().addSound(p, args[0], p);
+ sender.sendMessage(new TranslationContainer("commands.playsound.success", args[0], p.getName()));
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/command/defaults/PluginsCommand.java b/src/main/java/cn/nukkit/command/defaults/PluginsCommand.java
index b3377bfd251..52438e9dc02 100644
--- a/src/main/java/cn/nukkit/command/defaults/PluginsCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/PluginsCommand.java
@@ -14,12 +14,9 @@
public class PluginsCommand extends VanillaCommand {
public PluginsCommand(String name) {
- super(name,
- "%nukkit.command.plugins.description",
- "%nukkit.command.plugins.usage",
- new String[]{"pl"}
- );
+ super(name, "%nukkit.command.plugins.description", "%nukkit.command.plugins.usage");
this.setPermission("nukkit.command.plugins");
+ this.setAliases(new String[]{"pl"});
this.commandParameters.clear();
}
@@ -29,18 +26,18 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
- this.sendPluginList(sender);
+ sendPluginList(sender);
return true;
}
- private void sendPluginList(CommandSender sender) {
+ private static void sendPluginList(CommandSender sender) {
StringBuilder list = new StringBuilder();
Map plugins = sender.getServer().getPluginManager().getPlugins();
for (Plugin plugin : plugins.values()) {
if (list.length() > 0) {
list.append(TextFormat.WHITE + ", ");
}
- list.append(plugin.isEnabled() ? TextFormat.GREEN : TextFormat.RED);
+ list.append(plugin.isEnabled() ? TextFormat.GREEN : TextFormat.RED + "*");
list.append(plugin.getDescription().getFullName());
}
diff --git a/src/main/java/cn/nukkit/command/defaults/ReloadCommand.java b/src/main/java/cn/nukkit/command/defaults/ReloadCommand.java
index 718c140b5b5..cf584839a73 100644
--- a/src/main/java/cn/nukkit/command/defaults/ReloadCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/ReloadCommand.java
@@ -6,7 +6,7 @@
import cn.nukkit.utils.TextFormat;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class ReloadCommand extends VanillaCommand {
diff --git a/src/main/java/cn/nukkit/command/defaults/SaveOffCommand.java b/src/main/java/cn/nukkit/command/defaults/SaveOffCommand.java
index 762cd3140bb..4967c22d8a0 100644
--- a/src/main/java/cn/nukkit/command/defaults/SaveOffCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/SaveOffCommand.java
@@ -21,6 +21,11 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (!this.testPermission(sender)) {
return true;
}
+ if (args.length == 1 && args[0].equalsIgnoreCase("hold")) {
+ sender.getServer().holdWorldSave = true;
+ Command.broadcastCommandMessage(sender, new TranslationContainer("commands.save.hold-on"));
+ return true;
+ }
sender.getServer().setAutoSave(false);
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.save.disabled"));
return true;
diff --git a/src/main/java/cn/nukkit/command/defaults/SaveOnCommand.java b/src/main/java/cn/nukkit/command/defaults/SaveOnCommand.java
index a3f6a5dfdd3..d9818a32cc3 100644
--- a/src/main/java/cn/nukkit/command/defaults/SaveOnCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/SaveOnCommand.java
@@ -21,6 +21,11 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (!this.testPermission(sender)) {
return true;
}
+ if (args.length == 1 && args[0].equalsIgnoreCase("hold")) {
+ sender.getServer().holdWorldSave = false;
+ Command.broadcastCommandMessage(sender, new TranslationContainer("commands.save.hold-off"));
+ return true;
+ }
sender.getServer().setAutoSave(true);
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.save.enabled"));
return true;
diff --git a/src/main/java/cn/nukkit/command/defaults/SayCommand.java b/src/main/java/cn/nukkit/command/defaults/SayCommand.java
index a55d3c459cf..ae234c5f085 100644
--- a/src/main/java/cn/nukkit/command/defaults/SayCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/SayCommand.java
@@ -3,7 +3,6 @@
import cn.nukkit.Player;
import cn.nukkit.command.CommandSender;
import cn.nukkit.command.ConsoleCommandSender;
-import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.lang.TranslationContainer;
import cn.nukkit.utils.TextFormat;
@@ -19,7 +18,7 @@ public SayCommand(String name) {
this.setPermission("nukkit.command.say");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("message", CommandParamType.MESSAGE)
+ new CommandParameter("message")
});
}
@@ -45,7 +44,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
StringBuilder msg = new StringBuilder();
for (String arg : args) {
- msg.append(arg).append(" ");
+ msg.append(arg).append(' ');
}
if (msg.length() > 0) {
msg = new StringBuilder(msg.substring(0, msg.length() - 1));
diff --git a/src/main/java/cn/nukkit/command/defaults/SeedCommand.java b/src/main/java/cn/nukkit/command/defaults/SeedCommand.java
index 3c70e7dd5f1..b269df22c97 100644
--- a/src/main/java/cn/nukkit/command/defaults/SeedCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/SeedCommand.java
@@ -5,7 +5,7 @@
import cn.nukkit.lang.TranslationContainer;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class SeedCommand extends VanillaCommand {
diff --git a/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java b/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java
index fcb93688eb2..2da5ba32e78 100644
--- a/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/SetWorldSpawnCommand.java
@@ -16,12 +16,13 @@
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class SetWorldSpawnCommand extends VanillaCommand {
+
public SetWorldSpawnCommand(String name) {
super(name, "%nukkit.command.setworldspawn.description", "%commands.setworldspawn.usage");
this.setPermission("nukkit.command.setworldspawn");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("spawnPoint", true, CommandParamType.POSITION)
+ new CommandParameter("blockPos", CommandParamType.POSITION, true)
});
}
@@ -54,9 +55,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
}
level.setSpawnLocation(pos);
DecimalFormat round2 = new DecimalFormat("##0.00");
- Command.broadcastCommandMessage(sender, new TranslationContainer("commands.setworldspawn.success", round2.format(pos.x),
- round2.format(pos.y),
- round2.format(pos.z)));
+ Command.broadcastCommandMessage(sender, new TranslationContainer("commands.setworldspawn.success", round2.format(pos.x), round2.format(pos.y), round2.format(pos.z)));
return true;
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java b/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java
index 1afd5d68e3d..3919114c731 100644
--- a/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/SpawnpointCommand.java
@@ -17,6 +17,7 @@
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class SpawnpointCommand extends VanillaCommand {
+
public SpawnpointCommand(String name) {
super(name, "%nukkit.command.spawnpoint.description", "%commands.spawnpoint.usage");
this.setPermission("nukkit.command.spawnpoint");
@@ -41,7 +42,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
} else {
- target = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ target = sender.getServer().getPlayerExact(args[0].replace("@s", sender.getName()));
if (target == null) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.player.notFound"));
return true;
@@ -62,8 +63,8 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return true;
}
- if (y < 0) y = 0;
- if (y > 256) y = 256;
+ if (y < target.getLevel().getMinBlockY()) y = target.getLevel().getMinBlockY();
+ if (y > target.getLevel().getMaxBlockY()) y = target.getLevel().getMaxBlockY();
target.setSpawn(new Position(x, y, z, level));
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.spawnpoint.success", target.getName(),
round2.format(x),
@@ -88,4 +89,4 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return true;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/command/defaults/StatusCommand.java b/src/main/java/cn/nukkit/command/defaults/StatusCommand.java
index 7dc9c054975..2c2c6498ea3 100644
--- a/src/main/java/cn/nukkit/command/defaults/StatusCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/StatusCommand.java
@@ -15,6 +15,7 @@
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class StatusCommand extends VanillaCommand {
+
private static final String UPTIME_FORMAT = TextFormat.RED + "%d" + TextFormat.GOLD + " days " +
TextFormat.RED + "%d" + TextFormat.GOLD + " hours " +
TextFormat.RED + "%d" + TextFormat.GOLD + " minutes " +
@@ -47,13 +48,13 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
tpsColor = TextFormat.GOLD;
}
- sender.sendMessage(TextFormat.GOLD + "Current TPS: " + tpsColor + NukkitMath.round(tps, 2));
+ sender.sendMessage(TextFormat.GOLD + "TPS / Average: " + tpsColor + NukkitMath.round(tps, 2) + " / " + NukkitMath.round(server.getTicksPerSecondAverage(), 2));
- sender.sendMessage(TextFormat.GOLD + "Load: " + tpsColor + server.getTickUsage() + "%");
+ sender.sendMessage(TextFormat.GOLD + "Load / Average: " + tpsColor + server.getTickUsage() + "% / " + server.getTickUsageAverage() + '%');
- sender.sendMessage(TextFormat.GOLD + "Network upload: " + TextFormat.GREEN + NukkitMath.round((server.getNetwork().getUpload() / 1024 * 1000), 2) + " kB/s");
+ //sender.sendMessage(TextFormat.GOLD + "Network upload: " + TextFormat.GREEN + NukkitMath.round((server.getNetwork().getUpload() / 1024 * 1000), 2) + " kB/s");
- sender.sendMessage(TextFormat.GOLD + "Network download: " + TextFormat.GREEN + NukkitMath.round((server.getNetwork().getDownload() / 1024 * 1000), 2) + " kB/s");
+ //sender.sendMessage(TextFormat.GOLD + "Network download: " + TextFormat.GREEN + NukkitMath.round((server.getNetwork().getDownload() / 1024 * 1000), 2) + " kB/s");
sender.sendMessage(TextFormat.GOLD + "Thread count: " + TextFormat.GREEN + Thread.getAllStackTraces().size());
@@ -69,38 +70,40 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
usageColor = TextFormat.GOLD;
}
- sender.sendMessage(TextFormat.GOLD + "Used memory: " + usageColor + usedMB + " MB. (" + NukkitMath.round(usage, 2) + "%)");
+ sender.sendMessage(TextFormat.GOLD + "Used memory: " + usageColor + usedMB + " MB (" + NukkitMath.round(usage, 2) + "%)");
- sender.sendMessage(TextFormat.GOLD + "Total memory: " + TextFormat.RED + totalMB + " MB.");
+ sender.sendMessage(TextFormat.GOLD + "Total memory: " + TextFormat.RED + totalMB + " MB");
- sender.sendMessage(TextFormat.GOLD + "Maximum VM memory: " + TextFormat.RED + maxMB + " MB.");
+ sender.sendMessage(TextFormat.GOLD + "Maximum VM memory: " + TextFormat.RED + maxMB + " MB");
sender.sendMessage(TextFormat.GOLD + "Available processors: " + TextFormat.GREEN + runtime.availableProcessors());
+ int players = server.getOnlinePlayersCount();
+
TextFormat playerColor = TextFormat.GREEN;
- if (((float) server.getOnlinePlayers().size() / (float) server.getMaxPlayers()) > 0.85) {
+ if (((float) players / (float) server.getMaxPlayers()) > 0.85) {
playerColor = TextFormat.GOLD;
}
- sender.sendMessage(TextFormat.GOLD + "Players: " + playerColor + server.getOnlinePlayers().size() + TextFormat.GREEN + " online, " +
- TextFormat.RED + server.getMaxPlayers() + TextFormat.GREEN + " max. ");
+ sender.sendMessage(TextFormat.GOLD + "Players: " + playerColor + players + TextFormat.GREEN + " online, " +
+ TextFormat.RED + server.getMaxPlayers() + TextFormat.GREEN + " max");
for (Level level : server.getLevels().values()) {
sender.sendMessage(
- TextFormat.GOLD + "World \"" + level.getFolderName() + "\"" + (!Objects.equals(level.getFolderName(), level.getName()) ? " (" + level.getName() + ")" : "") + ": " +
+ TextFormat.GOLD + "World \"" + level.getFolderName() + '"' + (!Objects.equals(level.getFolderName(), level.getName()) ? " (" + level.getName() + ')' : "") + ": " +
TextFormat.RED + level.getChunks().size() + TextFormat.GREEN + " chunks, " +
- TextFormat.RED + level.getEntities().length + TextFormat.GREEN + " entities, " +
- TextFormat.RED + level.getBlockEntities().size() + TextFormat.GREEN + " blockEntities." +
+ TextFormat.RED + level.entities.size() + TextFormat.GREEN + " entities, " +
+ TextFormat.RED + level.getBlockEntities().size() + TextFormat.GREEN + " block entities." +
" Time " + ((level.getTickRate() > 1 || level.getTickRateTime() > 40) ? TextFormat.RED : TextFormat.YELLOW) + NukkitMath.round(level.getTickRateTime(), 2) + "ms" +
- (level.getTickRate() > 1 ? " (tick rate " + level.getTickRate() + ")" : "")
+ (level.getTickRate() > 1 ? " (tick rate " + level.getTickRate() + ')' : "")
);
}
return true;
}
- private static String formatUptime(long uptime) {
+ public static String formatUptime(long uptime) {
long days = TimeUnit.MILLISECONDS.toDays(uptime);
uptime -= TimeUnit.DAYS.toMillis(days);
long hours = TimeUnit.MILLISECONDS.toHours(uptime);
@@ -110,4 +113,4 @@ private static String formatUptime(long uptime) {
long seconds = TimeUnit.MILLISECONDS.toSeconds(uptime);
return String.format(UPTIME_FORMAT, days, hours, minutes, seconds);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/command/defaults/StopCommand.java b/src/main/java/cn/nukkit/command/defaults/StopCommand.java
index 0c89133e00f..e4c22b0601d 100644
--- a/src/main/java/cn/nukkit/command/defaults/StopCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/StopCommand.java
@@ -5,7 +5,7 @@
import cn.nukkit.lang.TranslationContainer;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public class StopCommand extends VanillaCommand {
diff --git a/src/main/java/cn/nukkit/command/defaults/SummonCommand.java b/src/main/java/cn/nukkit/command/defaults/SummonCommand.java
new file mode 100644
index 00000000000..74744eeea64
--- /dev/null
+++ b/src/main/java/cn/nukkit/command/defaults/SummonCommand.java
@@ -0,0 +1,75 @@
+package cn.nukkit.command.defaults;
+
+import cn.nukkit.Player;
+import cn.nukkit.Server;
+import cn.nukkit.command.Command;
+import cn.nukkit.command.CommandSender;
+import cn.nukkit.command.data.CommandParamType;
+import cn.nukkit.command.data.CommandParameter;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.level.Position;
+import cn.nukkit.network.protocol.AddEntityPacket;
+import cn.nukkit.utils.TextFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SummonCommand extends Command {
+
+ public SummonCommand(String name) {
+ super(name, "%nukkit.command.summon.description", "%nukkit.command.summon.usage");
+ this.setPermission("nukkit.command.summon");
+ this.commandParameters.clear();
+ List entityNames = new ArrayList<>();
+ for (String key : AddEntityPacket.LEGACY_IDS.values()) {
+ entityNames.add(key.substring(10));
+ }
+ this.commandParameters.put("default", new CommandParameter[]{
+ CommandParameter.newEnum("entityType", false, entityNames.toArray(new String[0])),
+ new CommandParameter("player", CommandParamType.TARGET, true)
+ });
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
+ if (!this.testPermission(sender)) {
+ return true;
+ }
+
+ if (args.length == 0 || (args.length == 1 && !(sender instanceof Player))) {
+ return false;
+ }
+
+ // Convert Minecraft format to the format what Nukkit uses
+ String mob = Character.toUpperCase(args[0].charAt(0)) + args[0].substring(1);
+ int max = mob.length() - 1;
+ for (int x = 2; x < max; x++) {
+ if (mob.charAt(x) == '_') {
+ mob = mob.substring(0, x) + Character.toUpperCase(mob.charAt(x + 1)) + mob.substring(x + 2);
+ }
+ }
+
+ Player playerThatSpawns;
+
+ if (args.length == 2) {
+ playerThatSpawns = Server.getInstance().getPlayerExact(args[1].replace("@s", sender.getName()));
+ } else {
+ playerThatSpawns = (Player) sender;
+ }
+
+ if (playerThatSpawns != null) {
+ Position pos = playerThatSpawns.getPosition().floor().add(0.5, 0, 0.5);
+ Entity ent;
+ if ((ent = Entity.createEntity(mob, pos)) != null) {
+ ent.spawnToAll();
+ sender.sendMessage("\u00A76Spawned " + mob + " to " + playerThatSpawns.getName());
+ } else {
+ sender.sendMessage(TextFormat.RED + "Unable to spawn " + mob);
+ }
+ } else {
+ sender.sendMessage(TextFormat.RED + "Unknown player " + (args.length == 2 ? args[1] : sender.getName()));
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java b/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java
index e69aa948808..f025e56fa5d 100644
--- a/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/TeleportCommand.java
@@ -16,27 +16,24 @@
* Package cn.nukkit.command.defaults in project Nukkit .
*/
public class TeleportCommand extends VanillaCommand {
+
public TeleportCommand(String name) {
super(name, "%nukkit.command.tp.description", "%commands.tp.usage");
this.setPermission("nukkit.command.teleport");
this.commandParameters.clear();
this.commandParameters.put("->Player", new CommandParameter[]{
- CommandParameter.newType("destination", CommandParamType.TARGET),
+ new CommandParameter("player", CommandParamType.TARGET, false),
});
this.commandParameters.put("Player->Player", new CommandParameter[]{
- CommandParameter.newType("victim", CommandParamType.TARGET),
- CommandParameter.newType("destination", CommandParamType.TARGET)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("target", CommandParamType.TARGET, false),
});
this.commandParameters.put("Player->Pos", new CommandParameter[]{
- CommandParameter.newType("victim", CommandParamType.TARGET),
- CommandParameter.newType("destination", CommandParamType.POSITION),
- CommandParameter.newType("yRot", true, CommandParamType.VALUE),
- CommandParameter.newType("xRot", true, CommandParamType.VALUE)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("blockPos", CommandParamType.POSITION, false),
});
this.commandParameters.put("->Pos", new CommandParameter[]{
- CommandParameter.newType("destination", CommandParamType.POSITION),
- CommandParameter.newType("yRot", true, CommandParamType.VALUE),
- CommandParameter.newType("xRot", true, CommandParamType.VALUE)
+ new CommandParameter("blockPos", CommandParamType.POSITION, false),
});
}
@@ -59,21 +56,21 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
if (args.length == 1) {
- target = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ target = sender.getServer().getPlayerExact(args[0].replace("@s", sender.getName()));
if (target == null) {
sender.sendMessage(TextFormat.RED + "Can't find player " + args[0]);
return true;
}
}
} else {
- target = sender.getServer().getPlayer(args[0].replace("@s", sender.getName()));
+ target = sender.getServer().getPlayerExact(args[0].replace("@s", sender.getName()));
if (target == null) {
sender.sendMessage(TextFormat.RED + "Can't find player " + args[0]);
return true;
}
if (args.length == 2) {
origin = target;
- target = sender.getServer().getPlayer(args[1].replace("@s", sender.getName()));
+ target = sender.getServer().getPlayerExact(args[1].replace("@s", sender.getName()));
if (target == null) {
sender.sendMessage(TextFormat.RED + "Can't find player " + args[1]);
return true;
@@ -106,11 +103,17 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return true;
}
- if (y < 0) y = 0;
- if (y > 256) y = 256;
+
+ if (x < -30000000) x = -30000000;
+ if (x > 30000000) x = 30000000;
+ if (y < -30000000) y = -30000000;
+ if (y > 30000000) y = 30000000;
+ if (z < -30000000) z = -30000000;
+ if (z > 30000000) z = 30000000;
+
if (args.length == 6 || (args.length == 5 && pos == 3)) {
yaw = Integer.parseInt(args[pos++]);
- pitch = Integer.parseInt(args[pos++]);
+ pitch = Integer.parseInt(args[pos]);
}
((Player) target).teleport(new Location(x, y, z, yaw, pitch, ((Player) target).getLevel()), PlayerTeleportEvent.TeleportCause.COMMAND);
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.tp.success.coordinates", target.getName(), String.valueOf(NukkitMath.round(x, 2)), String.valueOf(NukkitMath.round(y, 2)), String.valueOf(NukkitMath.round(z, 2))));
diff --git a/src/main/java/cn/nukkit/command/defaults/TellCommand.java b/src/main/java/cn/nukkit/command/defaults/TellCommand.java
index 58cf3004cf4..383205c202e 100644
--- a/src/main/java/cn/nukkit/command/defaults/TellCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/TellCommand.java
@@ -20,8 +20,8 @@ public TellCommand(String name) {
this.setPermission("nukkit.command.tell");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newType("message", CommandParamType.MESSAGE)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("message", CommandParamType.TEXT, false)
});
}
@@ -33,13 +33,12 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (args.length < 2) {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
-
return false;
}
String name = args[0].replace("@s", sender.getName());
- Player player = sender.getServer().getPlayer(name);
+ Player player = sender.getServer().getPlayerExact(name);
if (player == null) {
sender.sendMessage(new TranslationContainer("commands.generic.player.notFound"));
return true;
@@ -52,16 +51,22 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
StringBuilder msg = new StringBuilder();
for (int i = 1; i < args.length; i++) {
- msg.append(args[i]).append(" ");
+ msg.append(args[i]).append(' ');
}
- if (msg.length() > 0) {
+ if (msg.length() > 512) {
+ sender.sendMessage(TextFormat.RED + "The message is too long");
+ return true;
+ } else if (msg.length() > 0) {
msg = new StringBuilder(msg.substring(0, msg.length() - 1));
}
String displayName = (sender instanceof Player ? ((Player) sender).getDisplayName() : sender.getName());
- sender.sendMessage("[" + sender.getName() + " -> " + player.getDisplayName() + "] " + msg);
- player.sendMessage("[" + displayName + " -> " + player.getName() + "] " + msg);
+ sender.sendMessage('[' + sender.getName() + " -> " + player.getDisplayName() + "] " + msg);
+ player.sendMessage('[' + displayName + " -> " + player.getName() + "] " + msg);
+ if (sender instanceof Player) {
+ sender.getServer().getLogger().info('[' + sender.getName() + " -> " + player.getDisplayName() + "] " + msg);
+ }
return true;
}
diff --git a/src/main/java/cn/nukkit/command/defaults/TimeCommand.java b/src/main/java/cn/nukkit/command/defaults/TimeCommand.java
index 5d6f39ad95d..b180aa6bf27 100644
--- a/src/main/java/cn/nukkit/command/defaults/TimeCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/TimeCommand.java
@@ -18,10 +18,7 @@ public class TimeCommand extends VanillaCommand {
public TimeCommand(String name) {
super(name, "%nukkit.command.time.description", "%nukkit.command.time.usage");
- this.setPermission("nukkit.command.time.add;" +
- "nukkit.command.time.set;" +
- "nukkit.command.time.start;" +
- "nukkit.command.time.stop");
+ this.setPermission("nukkit.command.time.add;nukkit.command.time.set;nukkit.command.time.start;nukkit.command.time.stop");
this.commandParameters.clear();
this.commandParameters.put("1arg", new CommandParameter[]{
CommandParameter.newEnum("mode", new CommandEnum("TimeMode", "query", "start", "stop"))
diff --git a/src/main/java/cn/nukkit/command/defaults/TimingsCommand.java b/src/main/java/cn/nukkit/command/defaults/TimingsCommand.java
deleted file mode 100644
index beb9354e2ba..00000000000
--- a/src/main/java/cn/nukkit/command/defaults/TimingsCommand.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package cn.nukkit.command.defaults;
-
-import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
-import cn.nukkit.command.data.CommandParameter;
-import cn.nukkit.lang.TranslationContainer;
-import co.aikar.timings.Timings;
-import co.aikar.timings.TimingsExport;
-
-/**
- * @author fromgate
- * @author Pub4Game
- */
-public class TimingsCommand extends VanillaCommand {
-
- public TimingsCommand(String name) {
- super(name, "%nukkit.command.timings.description", "%nukkit.command.timings.usage");
- this.setPermission("nukkit.command.timings");
- this.commandParameters.clear();
- this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newEnum("action", new CommandEnum("TimingsAction", "on", "off", "paste", "verbon", "verboff", "reset", "report"))
- });
- }
-
- @Override
- public boolean execute(CommandSender sender, String commandLabel, String[] args) {
- if (!this.testPermission(sender)) {
- return true;
- }
-
- if (args.length != 1) {
- sender.sendMessage(new TranslationContainer("commands.generic.usage", usageMessage));
- return true;
- }
-
- String mode = args[0].toLowerCase();
-
- if (mode.equals("on")) {
- Timings.setTimingsEnabled(true);
- Timings.reset();
- sender.sendMessage(new TranslationContainer("nukkit.command.timings.enable"));
- return true;
- } else if (mode.equals("off")) {
- Timings.setTimingsEnabled(false);
- sender.sendMessage(new TranslationContainer("nukkit.command.timings.disable"));
- return true;
- }
-
- if (!Timings.isTimingsEnabled()) {
- sender.sendMessage(new TranslationContainer("nukkit.command.timings.timingsDisabled"));
- return true;
- }
-
- switch (mode) {
- case "verbon":
- sender.sendMessage(new TranslationContainer("nukkit.command.timings.verboseEnable"));
- Timings.setVerboseEnabled(true);
- break;
- case "verboff":
- sender.sendMessage(new TranslationContainer("nukkit.command.timings.verboseDisable"));
- Timings.setVerboseEnabled(true);
- break;
- case "reset":
- Timings.reset();
- sender.sendMessage(new TranslationContainer("nukkit.command.timings.reset"));
- break;
- case "report":
- case "paste":
- TimingsExport.reportTimings(sender);
- break;
- }
- return true;
- }
-}
diff --git a/src/main/java/cn/nukkit/command/defaults/TitleCommand.java b/src/main/java/cn/nukkit/command/defaults/TitleCommand.java
index b7be1a97d31..cc6f2085bb0 100644
--- a/src/main/java/cn/nukkit/command/defaults/TitleCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/TitleCommand.java
@@ -3,7 +3,6 @@
import cn.nukkit.Player;
import cn.nukkit.Server;
import cn.nukkit.command.CommandSender;
-import cn.nukkit.command.data.CommandEnum;
import cn.nukkit.command.data.CommandParamType;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.lang.TranslationContainer;
@@ -13,30 +12,41 @@
* @author Tee7even
*/
public class TitleCommand extends VanillaCommand {
+
public TitleCommand(String name) {
super(name, "%nukkit.command.title.description", "%nukkit.command.title.usage");
this.setPermission("nukkit.command.title");
this.commandParameters.clear();
this.commandParameters.put("clear", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("clear", new CommandEnum("TitleClear", "clear"))
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("clear", new String[]{"clear"})
});
this.commandParameters.put("reset", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("reset", new CommandEnum("TitleReset", "reset"))
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("reset", new String[]{"reset"})
+ });
+ this.commandParameters.put("title", new CommandParameter[]{
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("title", new String[]{"title"}),
+ new CommandParameter("titleText", CommandParamType.STRING, false)
});
- this.commandParameters.put("set", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("titleLocation", new CommandEnum("TitleSet", "title", "subtitle", "actionbar")),
- CommandParameter.newType("titleText", CommandParamType.MESSAGE)
+ this.commandParameters.put("subtitle", new CommandParameter[]{
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("subtitle", new String[]{"subtitle"}),
+ new CommandParameter("titleText", CommandParamType.STRING, false)
+ });
+ this.commandParameters.put("actionbar", new CommandParameter[]{
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("actionbar", new String[]{"actionbar"}),
+ new CommandParameter("titleText", CommandParamType.STRING, false)
});
this.commandParameters.put("times", new CommandParameter[]{
- CommandParameter.newType("player", CommandParamType.TARGET),
- CommandParameter.newEnum("times", new CommandEnum("TitleTimes", "times")),
- CommandParameter.newType("fadeIn", CommandParamType.INT),
- CommandParameter.newType("stay", CommandParamType.INT),
- CommandParameter.newType("fadeOut", CommandParamType.INT)
+ new CommandParameter("player", CommandParamType.TARGET, false),
+ new CommandParameter("times", new String[]{"times"}),
+ new CommandParameter("fadeIn", CommandParamType.INT, false),
+ new CommandParameter("stay", CommandParamType.INT, false),
+ new CommandParameter("fadeOut", CommandParamType.INT, false)
});
}
@@ -81,10 +91,10 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
player.setSubtitle(args[2]);
sender.sendMessage(new TranslationContainer("nukkit.command.title.subtitle", 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;*/
+ case "actionbar":
+ player.sendActionBar(args[2]);
+ sender.sendMessage(new TranslationContainer("nukkit.command.title.actionbar", TextFormat.clean(args[2]), player.getName()));
+ break;
default:
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return false;
@@ -111,4 +121,4 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/command/defaults/VanillaCommand.java b/src/main/java/cn/nukkit/command/defaults/VanillaCommand.java
index 10d4276fd58..ee701172696 100644
--- a/src/main/java/cn/nukkit/command/defaults/VanillaCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/VanillaCommand.java
@@ -3,7 +3,7 @@
import cn.nukkit.command.Command;
/**
- * author: MagicDroidX
+ * @author MagicDroidX
* Nukkit Project
*/
public abstract class VanillaCommand extends Command {
diff --git a/src/main/java/cn/nukkit/command/defaults/VersionCommand.java b/src/main/java/cn/nukkit/command/defaults/VersionCommand.java
index 5c4e9234aa1..73896c5059b 100644
--- a/src/main/java/cn/nukkit/command/defaults/VersionCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/VersionCommand.java
@@ -26,7 +26,7 @@ public VersionCommand(String name) {
this.setPermission("nukkit.command.version");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("pluginName", true, CommandParamType.STRING)
+ new CommandParameter("pluginName", CommandParamType.STRING, true)
});
}
@@ -35,7 +35,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (!this.testPermission(sender)) {
return true;
}
- if (args.length == 0) {
+ if (args.length == 0 || !sender.hasPermission("nukkit.command.version.plugins")) {
sender.sendMessage(new TranslationContainer("nukkit.server.info.extended", sender.getServer().getName(),
sender.getServer().getNukkitVersion(),
sender.getServer().getCodename(),
@@ -44,7 +44,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
String.valueOf(ProtocolInfo.CURRENT_PROTOCOL)));
} else {
StringBuilder pluginName = new StringBuilder();
- for (String arg : args) pluginName.append(arg).append(" ");
+ for (String arg : args) pluginName.append(arg).append(' ');
pluginName = new StringBuilder(pluginName.toString().trim());
final boolean[] found = {false};
final Plugin[] exactPlugin = {sender.getServer().getPluginManager().getPlugin(pluginName.toString())};
diff --git a/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java b/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java
index f85de3c6d19..b65753f6b85 100644
--- a/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/WeatherCommand.java
@@ -10,7 +10,7 @@
import cn.nukkit.level.Level;
/**
- * author: Angelic47
+ * @author Angelic47
* Nukkit Project
*/
public class WeatherCommand extends VanillaCommand {
@@ -81,6 +81,5 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.weather.usage", this.usageMessage));
return false;
}
-
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java b/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java
index 39b3a487e21..45a5ec175a3 100644
--- a/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/WhitelistCommand.java
@@ -34,6 +34,7 @@ public WhitelistCommand(String name) {
});
}
+
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
if (!this.testPermission(sender)) {
@@ -46,24 +47,23 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
}
if (args.length == 1) {
- if (this.badPerm(sender, args[0].toLowerCase())) {
+ if (badPerm(sender, args[0].toLowerCase())) {
return false;
}
switch (args[0].toLowerCase()) {
case "reload":
sender.getServer().reloadWhitelist();
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.whitelist.reloaded"));
-
return true;
case "on":
sender.getServer().setPropertyBoolean("white-list", true);
+ sender.getServer().whitelistEnabled = true;
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.whitelist.enabled"));
-
return true;
case "off":
sender.getServer().setPropertyBoolean("white-list", false);
+ sender.getServer().whitelistEnabled = false;
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.whitelist.disabled"));
-
return true;
case "list":
StringBuilder result = new StringBuilder();
@@ -85,8 +85,8 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
sender.sendMessage(new TranslationContainer("commands.generic.usage", "%commands.whitelist.remove.usage"));
return true;
}
- } else if (args.length == 2) {
- if (this.badPerm(sender, args[0].toLowerCase())) {
+ } else {
+ if (badPerm(sender, args[0].toLowerCase())) {
return false;
}
switch (args[0].toLowerCase()) {
@@ -106,7 +106,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
return true;
}
- private boolean badPerm(CommandSender sender, String perm) {
+ private static boolean badPerm(CommandSender sender, String perm) {
if (!sender.hasPermission("nukkit.command.whitelist." + perm)) {
sender.sendMessage(new TranslationContainer(TextFormat.RED + "%commands.generic.permission"));
diff --git a/src/main/java/cn/nukkit/command/defaults/XpCommand.java b/src/main/java/cn/nukkit/command/defaults/XpCommand.java
index 9ca01f11b92..c5038a77cb3 100644
--- a/src/main/java/cn/nukkit/command/defaults/XpCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/XpCommand.java
@@ -13,17 +13,14 @@
* Package cn.nukkit.command.defaults in project nukkit.
*/
public class XpCommand extends Command {
+
public XpCommand(String name) {
super(name, "%nukkit.command.xp.description", "%commands.xp.usage");
this.setPermission("nukkit.command.xp");
this.commandParameters.clear();
this.commandParameters.put("default", new CommandParameter[]{
- CommandParameter.newType("amount", CommandParamType.INT),
- CommandParameter.newType("player", true, CommandParamType.TARGET)
- });
- this.commandParameters.put("level", new CommandParameter[]{
- CommandParameter.newPostfix("amount", "l"),
- CommandParameter.newType("player", true, CommandParamType.TARGET)
+ new CommandParameter("amount|level", CommandParamType.INT, false),
+ new CommandParameter("player", CommandParamType.TARGET, true)
});
}
@@ -45,7 +42,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
}
amountString = args[0];
playerName = args[1];
- player = sender.getServer().getPlayer(playerName);
+ player = sender.getServer().getPlayerExact(playerName);
} else {
if (args.length == 1) {
amountString = args[0];
@@ -53,7 +50,7 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
} else if (args.length == 2) {
amountString = args[0];
playerName = args[1].replace("@s", sender.getName());
- player = sender.getServer().getPlayer(playerName);
+ player = sender.getServer().getPlayerExact(playerName);
} else {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
return true;
diff --git a/src/main/java/cn/nukkit/command/simple/SimpleCommand.java b/src/main/java/cn/nukkit/command/simple/SimpleCommand.java
index 6fae8ab119f..d111773dcd0 100644
--- a/src/main/java/cn/nukkit/command/simple/SimpleCommand.java
+++ b/src/main/java/cn/nukkit/command/simple/SimpleCommand.java
@@ -12,8 +12,8 @@
* @author Tee7even
*/
public class SimpleCommand extends Command {
- private Object object;
- private Method method;
+ private final Object object;
+ private final Method method;
private boolean forbidConsole;
private int maxArgs;
private int minArgs;
@@ -37,7 +37,7 @@ public void setMinArgs(int minArgs) {
}
public void sendUsageMessage(CommandSender sender) {
- if (!this.usageMessage.equals("")) {
+ if (!this.usageMessage.isEmpty()) {
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.usageMessage));
}
}
diff --git a/src/main/java/cn/nukkit/console/NukkitConsole.java b/src/main/java/cn/nukkit/console/NukkitConsole.java
index a83a589b770..41e005d89a7 100644
--- a/src/main/java/cn/nukkit/console/NukkitConsole.java
+++ b/src/main/java/cn/nukkit/console/NukkitConsole.java
@@ -2,7 +2,6 @@
import cn.nukkit.Server;
import cn.nukkit.event.server.ServerCommandEvent;
-import co.aikar.timings.Timings;
import lombok.RequiredArgsConstructor;
import net.minecrell.terminalconsole.SimpleTerminalConsole;
import org.jline.reader.LineReader;
@@ -14,27 +13,25 @@
@RequiredArgsConstructor
public class NukkitConsole extends SimpleTerminalConsole {
- private final Server server;
+
private final BlockingQueue consoleQueue = new LinkedBlockingQueue<>();
- private AtomicBoolean executingCommands = new AtomicBoolean(false);
+ private final AtomicBoolean executingCommands = new AtomicBoolean(false);
@Override
protected boolean isRunning() {
- return server.isRunning();
+ return Server.getInstance().isRunning();
}
@Override
protected void runCommand(String command) {
if (executingCommands.get()) {
- Timings.serverCommandTimer.startTiming();
- ServerCommandEvent event = new ServerCommandEvent(server.getConsoleSender(), command);
- if (server.getPluginManager() != null) {
- server.getPluginManager().callEvent(event);
+ ServerCommandEvent event = new ServerCommandEvent(Server.getInstance().getConsoleSender(), command);
+ if (Server.getInstance().getPluginManager() != null) {
+ Server.getInstance().getPluginManager().callEvent(event);
}
if (!event.isCancelled()) {
- Server.getInstance().getScheduler().scheduleTask(() -> server.dispatchCommand(event.getSender(), event.getCommand()));
+ Server.getInstance().getScheduler().scheduleTask(() -> Server.getInstance().dispatchCommand(event.getSender(), event.getCommand()));
}
- Timings.serverCommandTimer.stopTiming();
} else {
consoleQueue.add(command);
}
@@ -50,12 +47,12 @@ public String readLine() {
@Override
protected void shutdown() {
- server.shutdown();
+ Server.getInstance().shutdown();
}
@Override
protected LineReader buildReader(LineReaderBuilder builder) {
- builder.completer(new NukkitConsoleCompleter(server));
+ builder.completer(new NukkitConsoleCompleter());
builder.appName("Nukkit");
builder.option(LineReader.Option.HISTORY_BEEP, false);
builder.option(LineReader.Option.HISTORY_IGNORE_DUPS, true);
diff --git a/src/main/java/cn/nukkit/console/NukkitConsoleCompleter.java b/src/main/java/cn/nukkit/console/NukkitConsoleCompleter.java
index 60d4943ed46..e898cea8cc7 100644
--- a/src/main/java/cn/nukkit/console/NukkitConsoleCompleter.java
+++ b/src/main/java/cn/nukkit/console/NukkitConsoleCompleter.java
@@ -14,7 +14,6 @@
@RequiredArgsConstructor
public class NukkitConsoleCompleter implements Completer {
- private final Server server;
@Override
public void complete(LineReader lineReader, ParsedLine parsedLine, List candidates) {
@@ -35,7 +34,7 @@ public void complete(LineReader lineReader, ParsedLine parsedLine, List 0 && !parsedLine.word().isEmpty()) {
String word = parsedLine.word();
SortedSet names = new TreeSet<>();
- server.getOnlinePlayers().values().forEach((p) -> names.add(p.getName()));
+ Server.getInstance().getOnlinePlayers().values().forEach((p) -> names.add(p.getName()));
for (String match : names) {
if (!match.toLowerCase().startsWith(word.toLowerCase())) {
continue;
@@ -46,8 +45,8 @@ public void complete(LineReader lineReader, ParsedLine parsedLine, List commandConsumer) {
- for (String command : server.getCommandMap().getCommands().keySet()) {
+ private static void addCandidates(Consumer commandConsumer) {
+ for (String command : Server.getInstance().getCommandMap().getCommands().keySet()) {
if (!command.contains(":")) {
commandConsumer.accept(command);
}
diff --git a/src/main/java/cn/nukkit/customblock/CustomBlockDefinition.java b/src/main/java/cn/nukkit/customblock/CustomBlockDefinition.java
new file mode 100644
index 00000000000..c3727230159
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/CustomBlockDefinition.java
@@ -0,0 +1,13 @@
+package cn.nukkit.customblock;
+
+import cn.nukkit.customblock.container.BlockContainer;
+import com.nukkitx.nbt.NbtMap;
+import lombok.Data;
+
+@Data
+public class CustomBlockDefinition {
+ private final String identifier;
+ private final NbtMap networkData;
+ private final int legacyId;
+ private final Class extends BlockContainer> typeOf;
+}
diff --git a/src/main/java/cn/nukkit/customblock/CustomBlockManager.java b/src/main/java/cn/nukkit/customblock/CustomBlockManager.java
new file mode 100644
index 00000000000..cae9d1c47b2
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/CustomBlockManager.java
@@ -0,0 +1,316 @@
+package cn.nukkit.customblock;
+
+import cn.nukkit.Server;
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.customblock.comparator.HashedPaletteComparator;
+import cn.nukkit.customblock.container.BlockContainer;
+import cn.nukkit.customblock.container.BlockContainerFactory;
+import cn.nukkit.customblock.container.BlockStorageContainer;
+import cn.nukkit.customblock.properties.BlockProperties;
+import cn.nukkit.customblock.properties.BlockProperty;
+import cn.nukkit.customblock.properties.EnumBlockProperty;
+import cn.nukkit.customblock.properties.exception.InvalidBlockPropertyMetaException;
+import cn.nukkit.item.RuntimeItems;
+import cn.nukkit.level.BlockPalette;
+import cn.nukkit.level.GlobalBlockPalette;
+import cn.nukkit.level.format.leveldb.BlockStateMapping;
+import cn.nukkit.level.format.leveldb.LevelDBConstants;
+import cn.nukkit.level.format.leveldb.NukkitLegacyMapper;
+import cn.nukkit.nbt.NBTIO;
+import cn.nukkit.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.*;
+import it.unimi.dsi.fastutil.ints.*;
+import it.unimi.dsi.fastutil.objects.*;
+import lombok.extern.log4j.Log4j2;
+
+import java.io.*;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+
+@Log4j2
+public class CustomBlockManager {
+
+ public static final Path BIN_PATH = Paths.get("bin/");
+ public static final int LOWEST_CUSTOM_BLOCK_ID = 5000;
+
+ private static CustomBlockManager instance;
+
+ public static CustomBlockManager init(Server server) {
+ if (instance == null) {
+ return instance = new CustomBlockManager(server);
+ }
+ throw new IllegalStateException("CustomBlockManager was already initialized!");
+ }
+
+ public static CustomBlockManager get() {
+ return instance;
+ }
+
+ private final Server server;
+
+ private final Int2ObjectMap blockDefinitions = new Int2ObjectOpenHashMap<>();
+ private final Int2ObjectMap legacy2CustomState = new Int2ObjectOpenHashMap<>();
+
+ private volatile boolean closed = false;
+
+ private CustomBlockManager(Server server) {
+ this.server = server;
+
+ Path filesPath = this.getBinPath();
+ if (!Files.isDirectory(filesPath)) {
+ try {
+ Files.createDirectories(filesPath);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to create BIN_DIRECTORY", e);
+ }
+ }
+ }
+
+ public void registerCustomBlock(String identifier, int nukkitId, Supplier factory) {
+ this.registerCustomBlock(identifier, nukkitId, NbtMap.EMPTY, factory);
+ }
+
+ public void registerCustomBlock(String identifier, int nukkitId, NbtMap networkData, Supplier factory) {
+ this.registerCustomBlock(identifier, nukkitId, null, networkData, meta -> factory.get());
+ }
+
+ public void registerCustomBlock(String identifier, int nukkitId, BlockProperties properties, NbtMap networkData, BlockContainerFactory factory) {
+ if (this.closed) {
+ throw new IllegalStateException("Block registry was already closed");
+ }
+
+ if (nukkitId < LOWEST_CUSTOM_BLOCK_ID) {
+ throw new IllegalArgumentException("Block ID can not be lower than " + LOWEST_CUSTOM_BLOCK_ID);
+ }
+
+ BlockContainer blockSample = factory.create(0);
+ if (blockSample instanceof BlockStorageContainer && properties == null) {
+ properties = ((BlockStorageContainer) blockSample).getBlockProperties();
+ log.warn("Custom block {} was registered using wrong method! Trying to use sample properties!", identifier);
+ }
+
+ if (properties != null && networkData.isEmpty()) {
+ throw new IllegalArgumentException("Block network data can not be empty for block with more permutations: " + identifier);
+ }
+
+ CustomBlockState defaultState = this.createBlockState(identifier, nukkitId << Block.DATA_BITS, properties, factory);
+ this.legacy2CustomState.put(defaultState.getLegacyId(), defaultState);
+
+ // TODO: unsure if this is per state or not
+ CustomBlockDefinition definition = new CustomBlockDefinition(identifier, networkData, defaultState.getLegacyId(), blockSample.getClass());
+ this.blockDefinitions.put(defaultState.getLegacyId(), definition);
+
+ int itemId = 255 - nukkitId;
+ RuntimeItems.getMapping().registerItem(identifier, nukkitId, itemId, 0);
+
+ if (properties != null) {
+ for (int meta = 1; meta < (1 << Block.DATA_BITS); meta++) {
+ CustomBlockState state;
+ try {
+ state = this.createBlockState(identifier, (nukkitId << Block.DATA_BITS) | meta, properties, factory);
+ } catch (InvalidBlockPropertyMetaException e) {
+ break; // Nukkit has more states than our block
+ }
+ this.legacy2CustomState.put(state.getLegacyId(), state);
+ }
+ }
+ }
+
+ private CustomBlockState createBlockState(String identifier, int legacyId, BlockProperties properties, BlockContainerFactory factory) {
+ int meta = legacyId & Block.DATA_MASK;
+
+ NbtMapBuilder statesBuilder = NbtMap.builder();
+ if (properties != null) {
+ for (String propertyName : properties.getNames()) {
+ BlockProperty> property = properties.getBlockProperty(propertyName);
+ if (property instanceof EnumBlockProperty) {
+ statesBuilder.put(property.getPersistenceName(), properties.getPersistenceValue(meta, propertyName));
+ } else {
+ statesBuilder.put(property.getPersistenceName(), properties.getValue(meta, propertyName));
+ }
+ }
+ }
+
+ NbtMap state = NbtMap.builder()
+ .putString("name", identifier)
+ .putCompound("states", statesBuilder.build())
+ .putInt("version", LevelDBConstants.STATE_VERSION)
+ .build();
+ return new CustomBlockState(identifier, legacyId, state, factory);
+ }
+
+ public boolean closeRegistry() throws IOException {
+ if (this.closed) {
+ throw new IllegalStateException("Block registry was already closed");
+ }
+
+ this.closed = true;
+ if (this.legacy2CustomState.isEmpty()) {
+ return false;
+ }
+
+ long startTime = System.currentTimeMillis();
+
+ BlockPalette storagePalette = GlobalBlockPalette.getLeveldbBlockPalette();
+ BlockPalette palette = GlobalBlockPalette.getCurrentBlockPalette();
+ if (palette.getProtocol() == storagePalette.getProtocol()) {
+ this.recreateBlockPalette(palette, new ObjectArrayList<>(NukkitLegacyMapper.loadBlockPalette()));
+ } else {
+ Path path = this.getVanillaPalettePath(palette.getProtocol());
+ if (!Files.exists(path)) {
+ log.warn("No vanilla palette found for");
+ return false;
+ }
+ this.recreateBlockPalette(palette);
+ }
+
+ log.info("Custom block registry closed in {}ms", (System.currentTimeMillis() - startTime));
+ return true;
+ }
+
+ private void recreateBlockPalette(BlockPalette palette) throws IOException {
+ List vanillaPalette = new ObjectArrayList<>(this.loadVanillaPalette(palette.getProtocol()));
+ this.recreateBlockPalette(palette, vanillaPalette);
+ }
+
+ private void recreateBlockPalette(BlockPalette palette, List vanillaPalette) {
+ Object2ObjectMap state2Legacy = new Object2ObjectLinkedOpenHashMap<>();
+
+ int paletteVersion = vanillaPalette.get(0).getInt("version");
+
+ for (Int2IntMap.Entry entry : palette.getLegacyToRuntimeIdMap().int2IntEntrySet()) {
+ int runtimeId = entry.getIntValue();
+ NbtMap state = vanillaPalette.get(runtimeId);
+ if (state == null) {
+ log.info("Unknown runtime ID {}! protocol={}", runtimeId, palette.getProtocol());
+ continue;
+ }
+ IntSet legacyIds = state2Legacy.computeIfAbsent(state, s -> new IntOpenHashSet());
+ legacyIds.add(entry.getIntKey());
+ }
+
+ for (CustomBlockState definition : this.legacy2CustomState.values()) {
+ NbtMap state = definition.getBlockState();
+ if (state.getInt("version") != paletteVersion) {
+ state = state.toBuilder().putInt("version", paletteVersion).build();
+ }
+ vanillaPalette.add(state);
+ state2Legacy.computeIfAbsent(state, s -> new IntOpenHashSet()).add(legacyToFullId(definition.getLegacyId()));
+ }
+
+ vanillaPalette.sort(HashedPaletteComparator.INSTANCE);
+
+ palette.clearStates();
+ boolean levelDb = palette.getProtocol() == GlobalBlockPalette.getLeveldbBlockPalette().getProtocol();
+ if (levelDb) {
+ BlockStateMapping.get().clearMapping();
+ }
+
+ for (int runtimeId = 0; runtimeId < vanillaPalette.size(); runtimeId++) {
+ NbtMap state = vanillaPalette.get(runtimeId);
+ if (levelDb) {
+ BlockStateMapping.get().registerState(runtimeId, state);
+ }
+
+ IntSet legacyIds = state2Legacy.get(state);
+ if (legacyIds == null) {
+ continue;
+ }
+
+ CompoundTag nukkitState = convertNbtMap(state);
+ for (Integer fullId : legacyIds) {
+ palette.registerState(fullId >> 6, (fullId & 0xf), runtimeId, nukkitState);
+ }
+ }
+ }
+
+ private List loadVanillaPalette(int version) throws FileNotFoundException {
+ Path path = this.getVanillaPalettePath(version);
+ if (!Files.exists(path)) {
+ throw new FileNotFoundException("Missing vanilla palette for version " + version);
+ }
+
+ try (InputStream stream = Files.newInputStream(path)) {
+ return ((NbtMap) NbtUtils.createGZIPReader(stream).readTag()).getList("blocks", NbtType.COMPOUND);
+ } catch (Exception e) {
+ throw new AssertionError("Error loading block palette leveldb_palette.nbt", e);
+ }
+ }
+
+ private Path getVanillaPalettePath(int version) {
+ return this.getBinPath().resolve("vanilla_palette_" + version + ".nbt");
+ }
+
+ public Block getBlock(int legacyId) {
+ CustomBlockState state = this.legacy2CustomState.get(legacyId);
+ if (state == null) {
+ return Block.get(BlockID.INFO_UPDATE);
+ }
+
+ BlockContainer block = state.getFactory().create(legacyId & Block.DATA_MASK);
+ if (block instanceof Block) {
+ return (Block) block;
+ }
+ return null;
+ }
+
+ public Block getBlock(int id, int meta) {
+ int legacyId = id << Block.DATA_BITS | meta;
+ CustomBlockState state = this.legacy2CustomState.get(legacyId);
+ if (state == null) {
+ state = this.legacy2CustomState.get(id << Block.DATA_BITS);
+ if (state == null) {
+ return Block.get(BlockID.INFO_UPDATE);
+ }
+ }
+
+ BlockContainer block = state.getFactory().create(meta);
+ if (block instanceof Block) {
+ return (Block) block;
+ }
+ return null;
+ }
+
+ public Class> getClassType(int blockId) {
+ CustomBlockDefinition definition = this.blockDefinitions.get(blockId << Block.DATA_BITS);
+ if (definition == null) {
+ return null;
+ }
+ return definition.getTypeOf();
+ }
+
+ private Path getBinPath() {
+ return Paths.get(this.server.getDataPath()).resolve(BIN_PATH);
+ }
+
+ public Collection getBlockDefinitions() {
+ return Collections.unmodifiableCollection(this.blockDefinitions.values());
+ }
+
+ private static int legacyToFullId(int legacyId) {
+ int blockId = legacyId >> Block.DATA_BITS;
+ int meta = legacyId & Block.DATA_MASK;
+ return (blockId << 6) | meta;
+ }
+
+ public static CompoundTag convertNbtMap(NbtMap nbt) {
+ try {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try (NBTOutputStream nbtOutputStream = NbtUtils.createWriter(stream)) {
+ nbtOutputStream.writeTag(nbt);
+ } finally {
+ stream.close();
+ }
+ return NBTIO.read(stream.toByteArray(), ByteOrder.BIG_ENDIAN, false);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to convert NbtMap: " + nbt, e);
+ }
+ }
+}
diff --git a/src/main/java/cn/nukkit/customblock/CustomBlockState.java b/src/main/java/cn/nukkit/customblock/CustomBlockState.java
new file mode 100644
index 00000000000..626d6c38537
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/CustomBlockState.java
@@ -0,0 +1,22 @@
+package cn.nukkit.customblock;
+
+import cn.nukkit.customblock.container.BlockContainerFactory;
+import cn.nukkit.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.NbtMap;
+import lombok.Data;
+
+@Data
+public class CustomBlockState {
+ private final String identifier;
+ private final int legacyId;
+ private final NbtMap blockState;
+ private final BlockContainerFactory factory;
+ private CompoundTag nukkitBlockState;
+
+ public CompoundTag getNukkitBlockState() {
+ if (this.nukkitBlockState == null) {
+ this.nukkitBlockState = CustomBlockManager.convertNbtMap(this.blockState);
+ }
+ return this.nukkitBlockState;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/customblock/GsonNBTMapper.java b/src/main/java/cn/nukkit/customblock/GsonNBTMapper.java
new file mode 100644
index 00000000000..5f1420f87e9
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/GsonNBTMapper.java
@@ -0,0 +1,186 @@
+package cn.nukkit.customblock;
+
+import com.google.gson.*;
+import com.google.gson.internal.LazilyParsedNumber;
+import com.nukkitx.nbt.NbtList;
+import com.nukkitx.nbt.NbtMap;
+import com.nukkitx.nbt.NbtMapBuilder;
+import com.nukkitx.nbt.NbtType;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+public class GsonNBTMapper {
+
+ public static final Gson GSON;
+
+ static {
+ GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapter(NbtMap.class, new NbtMapDeserializer());
+ builder.registerTypeAdapter(NbtList.class, new NbtArrayDeserializer());
+ GSON = builder.create();
+ }
+
+ public static NbtMap objectToNbtMap(Object object) {
+ JsonObject json = GSON.toJsonTree(object).getAsJsonObject();
+ return GSON.fromJson(json, NbtMap.class);
+ }
+
+ public static class NbtMapDeserializer implements JsonDeserializer {
+ @Override
+ public NbtMap deserialize(JsonElement json, Type typeOf, JsonDeserializationContext context) throws JsonParseException {
+ if (!json.isJsonObject()) {
+ throw new IllegalStateException("Expected JsonObject but got: " + json.getClass().getSimpleName());
+ }
+
+ JsonObject jsonObject = json.getAsJsonObject();
+ NbtMapBuilder builder = NbtMap.builder();
+
+ for (String key : jsonObject.keySet()) {
+ JsonElement element = jsonObject.get(key);
+ if (element.isJsonObject()) {
+ builder.putCompound(key, context.deserialize(element, NbtMap.class));
+ } else if (element.isJsonArray()) {
+ NbtList list = context.deserialize(element, NbtList.class);
+ builder.putList(key, list.getType(), list);
+ } else if (element.isJsonPrimitive()) {
+ builder.put(key, getPrimitiveObject(element));
+ } else if (element.isJsonNull()) {
+ builder.putCompound(key, NbtMap.builder().build());
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ public static class NbtArrayDeserializer implements JsonDeserializer> {
+ @Override
+ public NbtList> deserialize(JsonElement json, Type typeOf, JsonDeserializationContext context) throws JsonParseException {
+ if (!json.isJsonArray()) {
+ throw new IllegalStateException("Expected JsonObject but got: " + json.getClass().getSimpleName());
+ }
+
+ JsonArray jsonArray = json.getAsJsonArray();
+ NbtType type = getListType(jsonArray);
+ return new NbtList<>(type, getList(jsonArray, type, context));
+ }
+ }
+
+ public static NbtType> getListType(JsonArray array) {
+ NbtType> type = null;
+ for (JsonElement element : array) {
+ NbtType> elementType;
+ if (element.isJsonObject()) {
+ elementType = NbtType.COMPOUND;
+ } else if (element.isJsonArray()) {
+ elementType = NbtType.LIST;
+ } else {
+ elementType = getPrimitiveType(element);
+ }
+
+ if (type != null && elementType != type) {
+ throw new IllegalArgumentException("Can not create array of mixed types");
+ } else {
+ type = elementType;
+ }
+ }
+ return type;
+ }
+
+ public static List getList(JsonArray array, NbtType type, JsonDeserializationContext context) {
+ List list = new ObjectArrayList<>();
+ for (JsonElement element : array) {
+ if (type == NbtType.COMPOUND) {
+ list.add(context.deserialize(element, NbtMap.class));
+ } else if (type == NbtType.LIST) {
+ list.add(context.deserialize(element, NbtList.class));
+ } else {
+ list.add((T) getPrimitiveObject(element));
+ }
+ }
+ return list;
+ }
+
+ public static Object getPrimitiveObject(JsonElement element) {
+ if (element.getAsJsonPrimitive().isBoolean()) {
+ return element.getAsBoolean();
+ } else if (element.getAsJsonPrimitive().isNumber()) {
+ Number number = element.getAsNumber();
+ if (number instanceof Byte) {
+ return number.byteValue();
+ } else if (number instanceof Short) {
+ return number.shortValue();
+ } else if (number instanceof Integer) {
+ return number.intValue();
+ } else if (number instanceof Long) {
+ return number.longValue();
+ } else if (number instanceof Float) {
+ return number.floatValue();
+ } else if (number instanceof Double) {
+ return number.doubleValue();
+ } else {
+ String str = number.toString();
+ try {
+ return Integer.parseInt(str);
+ } catch(NumberFormatException e) {
+ try {
+ return Long.parseLong(str);
+ } catch(NumberFormatException e1) {
+ try {
+ return Float.parseFloat(str);
+ } catch(NumberFormatException e2) {
+ return Double.parseDouble(str);
+ }
+ }
+ }
+ }
+ } else if (element.getAsJsonPrimitive().isString()) {
+ return element.getAsString();
+ }
+ throw new IllegalArgumentException("Unknown type of primitive: " + element);
+ }
+
+ public static NbtType> getPrimitiveType(JsonElement element) {
+ if (element.getAsJsonPrimitive().isBoolean()) {
+ return NbtType.BYTE;
+ } else if (element.getAsJsonPrimitive().isNumber()) {
+ Number number = element.getAsNumber();
+ if (number instanceof Byte) {
+ return NbtType.BYTE;
+ } else if (number instanceof Short) {
+ return NbtType.SHORT;
+ } else if (number instanceof Integer) {
+ return NbtType.INT;
+ } else if (number instanceof Long) {
+ return NbtType.LONG;
+ } else if (number instanceof Float) {
+ return NbtType.FLOAT;
+ } else if (number instanceof Double) {
+ return NbtType.DOUBLE;
+ } else if (number instanceof LazilyParsedNumber) {
+ String str = number.toString();
+ try {
+ Integer.parseInt(str);
+ return NbtType.INT;
+ } catch(NumberFormatException e) {
+ try {
+ Long.parseLong(str);
+ return NbtType.LONG;
+ } catch(NumberFormatException e1) {
+ try {
+ Float.parseFloat(str);
+ return NbtType.FLOAT;
+ } catch(NumberFormatException e2) {
+ Double.parseDouble(str);
+ return NbtType.DOUBLE;
+ }
+ }
+ }
+ }
+ } else if (element.getAsJsonPrimitive().isString()) {
+ return NbtType.STRING;
+ }
+ throw new IllegalArgumentException("Unknown type of primitive: " + element);
+ }
+}
diff --git a/src/main/java/cn/nukkit/customblock/comparator/AlphabetPaletteComparator.java b/src/main/java/cn/nukkit/customblock/comparator/AlphabetPaletteComparator.java
new file mode 100644
index 00000000000..a0513aef90a
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/comparator/AlphabetPaletteComparator.java
@@ -0,0 +1,18 @@
+package cn.nukkit.customblock.comparator;
+
+import com.nukkitx.nbt.NbtMap;
+
+import java.util.Comparator;
+
+public class AlphabetPaletteComparator implements Comparator {
+ public static final AlphabetPaletteComparator INSTANCE = new AlphabetPaletteComparator();
+
+ @Override
+ public int compare(NbtMap o1, NbtMap o2) {
+ return getIdentifier(o1).compareToIgnoreCase(getIdentifier(o2));
+ }
+
+ private String getIdentifier(NbtMap state) {
+ return state.getString("name");
+ }
+}
diff --git a/src/main/java/cn/nukkit/customblock/comparator/HashedPaletteComparator.java b/src/main/java/cn/nukkit/customblock/comparator/HashedPaletteComparator.java
new file mode 100644
index 00000000000..ec75d6bbf58
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/comparator/HashedPaletteComparator.java
@@ -0,0 +1,37 @@
+package cn.nukkit.customblock.comparator;
+
+
+import com.nukkitx.nbt.NbtMap;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Comparator;
+
+public class HashedPaletteComparator implements Comparator {
+ public static final HashedPaletteComparator INSTANCE = new HashedPaletteComparator();
+
+ private static final long FNV1_64_INIT = 0xcbf29ce484222325L;
+ private static final long FNV1_PRIME_64 = 1099511628211L;
+
+ @Override
+ public int compare(NbtMap o1, NbtMap o2) {
+ byte[] b1 = getIdentifier(o1).getBytes(StandardCharsets.UTF_8);
+ byte[] b2 = getIdentifier(o2).getBytes(StandardCharsets.UTF_8);
+ long hash1 = fnv164(b1);
+ long hash2 = fnv164(b2);
+ return Long.compareUnsigned(hash1, hash2);
+ }
+
+ private String getIdentifier(NbtMap state) {
+ return state.getString("name");
+ }
+
+ public static long fnv164(byte[] data) {
+ long hash = FNV1_64_INIT;
+ for (byte datum : data) {
+ hash *= FNV1_PRIME_64;
+ hash ^= (datum & 0xff);
+ }
+
+ return hash;
+ }
+}
diff --git a/src/main/java/cn/nukkit/customblock/container/BlockContainer.java b/src/main/java/cn/nukkit/customblock/container/BlockContainer.java
new file mode 100644
index 00000000000..676637b2315
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/container/BlockContainer.java
@@ -0,0 +1,16 @@
+package cn.nukkit.customblock.container;
+
+import cn.nukkit.level.GlobalBlockPalette;
+
+public interface BlockContainer {
+
+ int getNukkitId();
+
+ default int getNukkitDamage() {
+ return 0;
+ }
+
+ default int getRuntimeId() {
+ return GlobalBlockPalette.getOrCreateRuntimeId(this.getNukkitId(), this.getNukkitDamage());
+ }
+}
diff --git a/src/main/java/cn/nukkit/customblock/container/BlockContainerFactory.java b/src/main/java/cn/nukkit/customblock/container/BlockContainerFactory.java
new file mode 100644
index 00000000000..da6f0763e63
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/container/BlockContainerFactory.java
@@ -0,0 +1,16 @@
+package cn.nukkit.customblock.container;
+
+import cn.nukkit.customblock.properties.BlockProperties;
+
+public interface BlockContainerFactory {
+
+ BlockContainer create(int meta);
+
+ static BlockContainer createSimple(String blockName, int blockId) {
+ return new CustomBlock(blockName, blockId);
+ }
+
+ static BlockContainer createMeta(String blockName, int blockId, int meta, BlockProperties properties) {
+ return new CustomBlockMeta(blockName, blockId, properties, meta);
+ }
+}
diff --git a/src/main/java/cn/nukkit/customblock/container/BlockStorageContainer.java b/src/main/java/cn/nukkit/customblock/container/BlockStorageContainer.java
new file mode 100644
index 00000000000..74afdd042ce
--- /dev/null
+++ b/src/main/java/cn/nukkit/customblock/container/BlockStorageContainer.java
@@ -0,0 +1,78 @@
+package cn.nukkit.customblock.container;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.customblock.properties.BlockProperties;
+import cn.nukkit.customblock.properties.BlockProperty;
+
+import java.io.Serializable;
+
+public interface BlockStorageContainer extends BlockContainer {
+
+ int getStorage();
+ void setStorage(int damage);
+
+ BlockProperties getBlockProperties();
+
+ default int getNukkitDamage() {
+ return getStorage() & Block.DATA_MASK;
+ }
+
+ default void setBooleanValue(BlockProperty