diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
deleted file mode 100644
index cbd9470d3ad..00000000000
--- a/.github/CONTRIBUTING.md
+++ /dev/null
@@ -1,107 +0,0 @@
-How to submit a bug report
----
-
-Before creating an issue, make sure:
- 1. Your title and content is not confusing or content-less.
- 2. All text is written in proper English.
-
-If it's a bug or problem:
- 1. This bug can be reproduced.
- 2. This bug can be found in the latest build.
- 3. Dumps, backtraces or files are provided.
- 4. It's you yourself who first found this bug.
-
-If it's advice or a feature request:
- 1. This feature does not exist in the latest build.
- 2. This feature is logical and clear-cut.
- 3. It's you yourself who first came up with the idea.
-
-Nukkit will create a bug report for EVERY exception and error detected, and there are some columns you need to fill out in the report. If multiple exceptions are triggered, you should combine the stacktrace into one report and then submit the report.
-
-In the report, you can see if the error is caused by Nukkit or a plugin. However, when "PLUGIN ERROR" is "false" and there are plugins running, it does not necessarily indicate that the error is caused by Nukkit.
-
-To submit bugs and problems, please upload the automatically generated report. Make sure you have filled in all blanks in the template. Please provide **as much information as you could**, or our developers might got stuck or confused when looking into your issue.
-
-To submit feature requests and suggestions, please explicitly describe the feature you want or your suggestion.
-
-**Note that the Issues section on GitHub is not for contents that are not related to the two categories listed above. Irrelevant issues will be closed. Please visit our forums for other kinds of discussions.**
-
-Example
----
-
-### Issue Description
-
-It seems that the player you are manipulating does not seem to be moving from other people, and it seems that you are not moving from others.
-
-I do not know because I have not logged in to anything other than my server, but it works normally with Wi-Fi multi.
-
-### OS and Versions
-
-* Nukkit Version: https://github.com/Nukkit/Nukkit/pull/1517
-
-* Java Version:
-```
-java version "9"
-Java(TM) SE Runtime Environment (build 9+175)
-Java HotSpot(TM) 64-Bit Server VM (build 9+175, mixed mode)
-```
-
-* Host Configuration:
-
-
-| Item | Value |
-|:----:|:-----:|
-| Host OS | Microsoft Windows [10.0.10240] |
-| Memory(RAM) | 4 GB |
-| Storage Size | 1 TB |
-| Storage Type | SSD |
-| CPU Type | Intel Xeon X5650 |
-| CPU Core Count | 12 cores 24 threads |
-| Upstream Bandwidth | 100 Mbps |
-
-* Client Configuration:
-
-| Item | Value |
-|:----:|:-----:|
-| Client Edition | Android |
-| Client Version | 1.0.4 |
-
-```
-### Issue Description
-
-It seems that the player you are manipulating does not seem to be moving from other people, and it seems that you are not moving from others.
-
-I do not know because I have not logged in to anything other than my server, but it works normally with Wi-Fi multi.
-
-### OS and Versions
-
-* Nukkit Version: https://github.com/Nukkit/Nukkit/pull/1517
-
-* Java Version:
-
-java version "9"
-Java(TM) SE Runtime Environment (build 9+175)
-Java HotSpot(TM) 64-Bit Server VM (build 9+175, mixed mode)
-
-
-* Host Configuration:
-
-
-| Item | Value |
-|:----:|:-----:|
-| Host OS | Microsoft Windows [10.0.10240] |
-| Memory(RAM) | 4 GB |
-| Storage Size | 1 TB |
-| Storage Type | SSD |
-| CPU Type | Intel Xeon X5650 |
-| CPU Core Count | 12 cores 24 threads |
-| Upstream Bandwidth | 100 Mbps |
-
-* Client Configuration:
-
-| Item | Value |
-|:----:|:-----:|
-| Client Edition | Android |
-| Client Version | 1.0.4 |
-
-```
diff --git a/.gitignore b/.gitignore
index 201025d9bd4..5ff03a35fde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -246,7 +246,7 @@ rebel-remote.xml
# Nukkit generated files
banned-ips.json
banned-players.json
-nukkit.yml
+/nukkit.yml
ops.txt
server.log
server.properties
@@ -265,6 +265,9 @@ creativeitems.json
recipes.json
data/
data/*
+material_tags.txt
+item_types.txt
+block_types.txt
run/
diff --git a/.gitmodules b/.gitmodules
index febb0db9cb2..e69de29bb2d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "src/main/resources/lang"]
- path = src/main/resources/lang
- url = https://github.com/NukkitX/Languages.git
diff --git a/README.md b/README.md
index a87814192a7..620b263e8c4 100644
--- a/README.md
+++ b/README.md
@@ -7,29 +7,24 @@
Introduction
-------------
-Nukkit is nuclear-powered server software for Minecraft: Pocket Edition.
+Nukkit is nuclear-powered server software for Minecraft Bedrock Edition.
It has a few key advantages over other server software:
* Written in Java, Nukkit is faster and more stable.
* Having a friendly structure, it's easy to contribute to Nukkit's development and rewrite plugins from other platforms into Nukkit plugins.
-Nukkit is **under improvement** yet, we welcome contributions.
+Nukkit is under improvement yet, we welcome contributions.
Links
--------------------
-* __[News](https://nukkitx.com)__
-* __[Forums](https://nukkitx.com/forums)__
+* __[Forums](https://cloudburstmc.org/forums/)__
* __[Discord](https://discord.gg/5PzMkyK)__
-* __[Download](https://ci.nukkitx.com/job/NukkitX/job/Nukkit/job/master)__
-* __[Plugins](https://nukkitx.com/resources/categories/nukkit-plugins.1)__
-* __[Wiki](https://nukkitx.com/wiki/nukkit)__
+* __[Wiki](https://cloudburstmc.org/wiki/nukkit)__
+* __[Download Nukkit](https://ci.opencollab.dev/job/NukkitX/job/Nukkit/job/master/)__
+* __[Download Plugins](https://cloudburstmc.org/resources/categories/nukkit-plugins.1/)__
-Contributing
--------------
-Please read the [CONTRIBUTING](.github/CONTRIBUTING.md) guide before submitting any issue. Issues with insufficient information or in the wrong format will be closed and will not be reviewed.
-
-Build JAR file
+Compile Nukkit
-------------
- `git clone https://github.com/CloudburstMC/Nukkit`
- `cd Nukkit`
@@ -38,6 +33,8 @@ Build JAR file
The compiled JAR can be found in the `target/` directory.
+Note: You don't need to compile Nukkit yourself if you don't intend to modify the code. You can find precompiled JARs on Jenkins.
+
Running
-------------
Simply run `java -jar nukkit-1.0-SNAPSHOT.jar`.
@@ -114,3 +111,8 @@ Testing after deployment:
Completely remove the chart:
`helm uninstall nukkit`
+
+Pterodactyl Panel
+-------------
+
+[Download the official egg](https://raw.githubusercontent.com/parkervcp/eggs/master/game_eggs/minecraft/bedrock/nukkit/egg-nukkit.json)
diff --git a/build.gradle.kts b/build.gradle.kts
index e2b8dda8c2f..7f4ac433fc1 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,4 +1,5 @@
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
+import org.gradle.internal.impldep.org.junit.experimental.categories.Categories.CategoryFilter.exclude
@Suppress("DSL_SCOPE_VIOLATION") // https://youtrack.jetbrains.com/issue/IDEA-262280
@@ -12,7 +13,7 @@ plugins {
group = "cn.nukkit"
version = "1.0-SNAPSHOT"
-description = "Nuclear powered server software for Minecraft: Bedrock Edition"
+description = "Nuclear powered server software for Minecraft Bedrock Edition"
repositories {
mavenLocal()
@@ -23,16 +24,32 @@ repositories {
dependencies {
api(libs.network)
- api(libs.natives)
+ api(libs.epoll)
api(libs.fastutil)
+ api(libs.bundles.fastutilmaps)
api(libs.guava)
api(libs.gson)
api(libs.snakeyaml)
api(libs.leveldb)
+ api(libs.leveldbjni) {
+ exclude(group = "com.google.guava", module = "guava")
+ exclude(group = "io.netty", module = "netty-buffer")
+ exclude(group = "org.iq80.snappy", module = "snappy")
+ exclude(group = "org.iq80.leveldb", module = "leveldb")
+ }
+ api(libs.snappy)
api(libs.jwt)
api(libs.bundles.terminal)
api(libs.bundles.log4j)
api(libs.jopt.simple)
+ api(libs.blockstateupdater)
+ api(libs.lmbda) {
+ exclude(group = "org.checkerframework", module = "checker-qual")
+ }
+ api(libs.noise) {
+ exclude(group = "net.daporkchop.lib", module = "common")
+ exclude(group = "net.daporkchop.lib", module = "math")
+ }
compileOnly(libs.lombok)
annotationProcessor(libs.lombok)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index bd557a11fc9..69c8c43f68f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,18 +2,28 @@
junit = "5.9.2"
log4j = "2.20.0"
jline = "3.22.0"
+fastutilmaps = "8.5.13-SNAPSHOT"
[libraries]
-network = { group = "com.nukkitx.network", name = "raknet", version = "1.6.28-SNAPSHOT" }
-natives = { group = "com.nukkitx", name = "natives", version = "1.0.3" }
+network = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version = "1.0.0.CR3-SNAPSHOT" }
+epoll = { group = "io.netty", name = "netty-transport-native-epoll", version = "4.1.101.Final" }
fastutil = { group = "com.nukkitx", name = "fastutil-lite", version = "8.1.1" }
-guava = { group = "com.google.guava", name = "guava", version = "30.1.1-jre" }
+fastutil-long-long-maps = { group = "org.cloudburstmc.fastutil.maps", name = "long-long-maps", version.ref = "fastutilmaps" }
+fastutil-int-short-maps = { group = "org.cloudburstmc.fastutil.maps", name = "int-short-maps", version.ref = "fastutilmaps" }
+fastutil-object-int-maps = { group = "org.cloudburstmc.fastutil.maps", name = "object-int-maps", version.ref = "fastutilmaps" }
+fastutil-object-object-maps = { group = "org.cloudburstmc.fastutil.maps", name = "object-object-maps", version.ref = "fastutilmaps" }
+guava = { group = "com.google.guava", name = "guava", version = "33.2.1-jre" }
gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" }
snakeyaml = { group = "org.yaml", name = "snakeyaml", version = "1.33" }
-leveldb = { group = "org.iq80.leveldb", name = "leveldb", version = "0.11-SNAPSHOT" }
-jwt = { group = "com.nimbusds", name = "nimbus-jose-jwt", version = "9.13" }
+leveldb = { group = "org.iq80.leveldb", name = "leveldb", version = "0.11.1-SNAPSHOT" }
+leveldbjni = { group = "net.daporkchop", name = "leveldb-mcpe-jni", version = "0.0.10-SNAPSHOT" }
+snappy = { group = "org.xerial.snappy", name = "snappy-java", version = "1.1.10.7" }
+jwt = { group = "com.nimbusds", name = "nimbus-jose-jwt", version = "9.23" }
jopt-simple = { group = "net.sf.jopt-simple", name = "jopt-simple", version = "5.0.4" }
-lombok = { group = "org.projectlombok", name = "lombok", version = "1.18.26" }
+blockstateupdater = { group = "org.cloudburstmc", name = "block-state-updater", version = "1.21.30-SNAPSHOT" }
+lmbda = { group = "org.lanternpowered", name = "lmbda", version = "2.0.0" }
+noise = { group = "net.daporkchop.lib", name = "noise", version = "0.5.6-SNAPSHOT" }
+lombok = { group = "org.projectlombok", name = "lombok", version = "1.18.34" }
# Logging dependencies
log4j-api = { group = "org.apache.logging.log4j", name = "log4j-api", version.ref = "log4j" }
@@ -33,6 +43,7 @@ junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engi
log4j = [ "log4j-api", "log4j-core" ]
terminal = [ "jline-terminal", "jline-terminal-jna", "jline-reader", "terminal-console" ]
junit = [ "junit-jupiter-api", "junit-jupiter-engine" ]
+fastutilmaps = [ "fastutil-long-long-maps", "fastutil-int-short-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ]
[plugins]
shadow = { id = "com.github.johnrengelman.shadow", version = "8.0.0" }
diff --git a/nukkit.yml.default b/nukkit.yml.default
index 3d4d2aae20f..1d1e15fcf88 100644
--- a/nukkit.yml.default
+++ b/nukkit.yml.default
@@ -1,4 +1,2 @@
settings:
- # Multi-language setting
- # Available: eng, chs, cht, jpn, rus, spa, pol, bra, kor, ukr, deu
language: "eng"
diff --git a/src/main/java/cn/nukkit/Achievement.java b/src/main/java/cn/nukkit/Achievement.java
index 2adcd738f0e..cb59c31130f 100644
--- a/src/main/java/cn/nukkit/Achievement.java
+++ b/src/main/java/cn/nukkit/Achievement.java
@@ -5,13 +5,21 @@
import java.util.HashMap;
/**
- * Created by CreeperFace on 9. 11. 2016.
+ * Achievement list and functions
+ *
+ * @author CreeperFace
*/
public class Achievement {
+ /**
+ * All known achievements.
+ *
+ * Based on ...
+ */
public static final HashMap achievements = new HashMap() {
{
- put("mineWood", new Achievement("Getting Wood"));
+ put("openInventory", new Achievement("Taking Inventory"));
+ put("mineWood", new Achievement("Getting Wood", "openInventory"));
put("buildWorkBench", new Achievement("Benchmarking", "mineWood"));
put("buildPickaxe", new Achievement("Time to Mine!", "buildWorkBench"));
put("buildFurnace", new Achievement("Hot Topic", "buildPickaxe"));
@@ -20,18 +28,44 @@ public class Achievement {
put("makeBread", new Achievement("Bake Bread", "buildHoe"));
put("bakeCake", new Achievement("The Lie", "buildHoe"));
put("buildBetterPickaxe", new Achievement("Getting an Upgrade", "buildPickaxe"));
+ put("cookFish", new Achievement("Delicious Fish", "buildFurnace"));
+ put("onARail", new Achievement("On A Rail", "acquireIron"));
put("buildSword", new Achievement("Time to Strike!", "buildWorkBench"));
+ put("killEnemy", new Achievement("Monster Hunter", "buildSword"));
+ put("killCow", new Achievement("Cow Tipper", "buildSword"));
+ put("flyPig", new Achievement("When Pigs Fly", "killCow"));
+ put("snipeSkeleton", new Achievement("Sniper Duel", "killEnemy"));
put("diamonds", new Achievement("DIAMONDS!", "acquireIron"));
+ put("portal", new Achievement("We Need to Go Deeper", "diamonds"));
+ put("ghast", new Achievement("Return to Sender", "portal"));
+ put("blazeRod", new Achievement("Into Fire", "portal"));
+ put("potion", new Achievement("Local Brewery", "blazeRod"));
+ put("theEnd", new Achievement("The End?", "blazeRod"));
+ put("theEnd2", new Achievement("The End.", "theEnd"));
+ put("enchantments", new Achievement("Enchanter", "diamonds"));
+ put("overkill", new Achievement("Overkill", "enchantments"));
+ put("bookcase", new Achievement("Librarian", "enchantments"));
+ put("exploreAllBiomes", new Achievement("Adventuring Time", "theEnd")); //TODO
+ put("spawnWither", new Achievement("The Beginning?", "theEnd"));
+ put("killWither", new Achievement("The Beginning.", "spawnWither"));
+ put("fullBeacon", new Achievement("Beaconator", "killWither"));
+ put("breedCow", new Achievement("Repopulation", "killCow"));
+ put("diamondsToYou", new Achievement("Diamonds to you!", "diamonds"));
+ put("overpowered", new Achievement("Overpowered", "buildBetterPickaxe"));
}
};
+ /**
+ * Broadcasts achievement get message if player does not have the achievement yet. Returns true if broadcast.
+ */
public static boolean broadcast(Player player, String achievementId) {
if (!achievements.containsKey(achievementId)) {
return false;
}
- String translation = Server.getInstance().getLanguage().translateString("chat.type.achievement", player.getDisplayName(), TextFormat.GREEN + achievements.get(achievementId).getMessage() + TextFormat.RESET);
- if (Server.getInstance().getPropertyBoolean("announce-player-achievements", true)) {
+ String translation = TextFormat.WHITE + Server.getInstance().getLanguage().translateString("chat.type.achievement", player.getDisplayName(), TextFormat.GREEN + "[" + achievements.get(achievementId).message + "]", null);
+
+ if (Server.getInstance().announceAchievements) {
Server.getInstance().broadcastMessage(translation);
} else {
player.sendMessage(translation);
@@ -39,6 +73,12 @@ public static boolean broadcast(Player player, String achievementId) {
return true;
}
+ /**
+ * Register an achievement
+ * @param name save id
+ * @param achievement achievement
+ * @return true if successful, false if save id is already in use
+ */
public static boolean add(String name, Achievement achievement) {
if (achievements.containsKey(name)) {
return false;
@@ -51,6 +91,10 @@ public static boolean add(String name, Achievement achievement) {
public final String message;
public final String[] requires;
+ /**
+ * @param message achievement name displayed in achievement get message
+ * @param requires save IDs of achievements player must complete before this achievement can be completed
+ */
public Achievement(String message, String... requires) {
this.message = message;
this.requires = requires;
@@ -60,13 +104,16 @@ public String getMessage() {
return message;
}
+ /**
+ * Broadcasts achievement get message
+ */
public void broadcast(Player player) {
- String translation = Server.getInstance().getLanguage().translateString("chat.type.achievement", player.getDisplayName(), TextFormat.GREEN + this.getMessage(), null);
+ String translation = TextFormat.WHITE + Server.getInstance().getLanguage().translateString("chat.type.achievement", player.getDisplayName(), TextFormat.GREEN + "[" + this.message + "]", null);
- if (Server.getInstance().getPropertyBoolean("announce-player-achievements", true)) {
+ if (Server.getInstance().announceAchievements) {
Server.getInstance().broadcastMessage(translation);
} else {
player.sendMessage(translation);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/cn/nukkit/AdventureSettings.java b/src/main/java/cn/nukkit/AdventureSettings.java
index a862ca762da..c5b2f0de7e2 100644
--- a/src/main/java/cn/nukkit/AdventureSettings.java
+++ b/src/main/java/cn/nukkit/AdventureSettings.java
@@ -9,8 +9,10 @@
import java.util.Map;
/**
+ * Adventure settings
+ *
+ * @author MagicDroidX
* Nukkit Project
- * Author: MagicDroidX
*/
public class AdventureSettings implements Cloneable {
@@ -20,7 +22,7 @@ public class AdventureSettings implements Cloneable {
public static final int PERMISSION_AUTOMATION = 3;
public static final int PERMISSION_ADMIN = 4;
- private Map values = new EnumMap<>(Type.class);
+ private final Map values = new EnumMap<>(Type.class);
private Player player;
@@ -38,11 +40,24 @@ public AdventureSettings clone(Player newPlayer) {
}
}
+ /**
+ * Set an adventure setting value
+ *
+ * @param type adventure setting
+ * @param value new value
+ * @return AdventureSettings
+ */
public AdventureSettings set(Type type, boolean value) {
this.values.put(type, value);
return this;
}
+ /**
+ * Get an adventure setting value
+ *
+ * @param type adventure setting
+ * @return value
+ */
public boolean get(Type type) {
Boolean value = this.values.get(type);
return value == null ? type.getDefaultValue() : value;
@@ -63,7 +78,7 @@ void update(boolean reset) {
UpdateAbilitiesPacket packet = new UpdateAbilitiesPacket();
packet.setEntityId(player.getId());
packet.setCommandPermission(player.isOp() ? UpdateAbilitiesPacket.CommandPermission.OPERATOR : UpdateAbilitiesPacket.CommandPermission.NORMAL);
- packet.setPlayerPermission(player.isOp() && !player.isSpectator() ? UpdateAbilitiesPacket.PlayerPermission.OPERATOR : UpdateAbilitiesPacket.PlayerPermission.MEMBER);
+ packet.setPlayerPermission(player.isOp() && !player.isSpectator() ? UpdateAbilitiesPacket.PlayerPermission.OPERATOR : UpdateAbilitiesPacket.PlayerPermission.MEMBER); // Spectator: fix operators being able to break blocks on spectator mode
AbilityLayer layer = new AbilityLayer();
layer.setLayerType(AbilityLayer.Type.BASE);
@@ -91,17 +106,25 @@ void update(boolean reset) {
layer.setFlySpeed(Player.DEFAULT_FLY_SPEED);
packet.getAbilityLayers().add(layer);
- if (this.get(Type.NO_CLIP)) {
- AbilityLayer layer2 = new AbilityLayer();
- layer2.setLayerType(AbilityLayer.Type.SPECTATOR);
+ if (player.isSpectator()) {
+ AbilityLayer spectator = new AbilityLayer();
+ spectator.setLayerType(AbilityLayer.Type.SPECTATOR);
+
+ spectator.getAbilitiesSet().addAll(PlayerAbility.VALUES);
+ spectator.getAbilitiesSet().remove(PlayerAbility.FLY_SPEED);
+ spectator.getAbilitiesSet().remove(PlayerAbility.WALK_SPEED);
+
+ for (Type type : Type.values()) {
+ if (type.isAbility() && this.get(type)) {
+ spectator.getAbilityValues().add(type.getAbility());
+ }
+ }
- layer2.getAbilitiesSet().addAll(PlayerAbility.VALUES);
- layer2.getAbilitiesSet().remove(PlayerAbility.FLY_SPEED);
- layer2.getAbilitiesSet().remove(PlayerAbility.WALK_SPEED);
+ if (player.isOp()) {
+ layer.getAbilityValues().add(PlayerAbility.OPERATOR_COMMANDS);
+ }
- layer2.getAbilityValues().add(PlayerAbility.FLYING);
- layer2.getAbilityValues().add(PlayerAbility.NO_CLIP);
- packet.getAbilityLayers().add(layer2);
+ packet.getAbilityLayers().add(spectator);
}
UpdateAdventureSettingsPacket adventurePacket = new UpdateAdventureSettingsPacket();
@@ -113,17 +136,21 @@ void update(boolean reset) {
player.dataPacket(packet);
player.dataPacket(adventurePacket);
+
if (reset) {
player.resetInAirTicks();
}
}
+ /**
+ * List of adventure settings
+ */
public enum Type {
- WORLD_IMMUTABLE(false),
- NO_PVM(false),
+ WORLD_IMMUTABLE(null, false),
+ NO_PVM(null, false),
NO_MVP(PlayerAbility.INVULNERABLE, false),
- SHOW_NAME_TAGS(false),
- AUTO_JUMP(true),
+ SHOW_NAME_TAGS(null, false),
+ AUTO_JUMP(null, true),
ALLOW_FLIGHT(PlayerAbility.MAY_FLY, false),
NO_CLIP(PlayerAbility.NO_CLIP, false),
WORLD_BUILDER(PlayerAbility.WORLD_BUILDER, false),
@@ -145,24 +172,34 @@ public enum Type {
private final PlayerAbility ability;
private final boolean defaultValue;
- Type(boolean defaultValue) {
- this.defaultValue = defaultValue;
- this.ability = null;
- }
-
Type(PlayerAbility ability, boolean defaultValue) {
this.ability = ability;
this.defaultValue = defaultValue;
}
+ /**
+ * Get default value
+ *
+ * @return default value
+ */
public boolean getDefaultValue() {
return this.defaultValue;
}
+ /**
+ * Get player ability type
+ *
+ * @return player ability type
+ */
public PlayerAbility getAbility() {
return this.ability;
}
+ /**
+ * Check whether adventure setting is a valid player ability
+ *
+ * @return is a valid player ability
+ */
public boolean isAbility() {
return this.ability != null;
}
diff --git a/src/main/java/cn/nukkit/IPlayer.java b/src/main/java/cn/nukkit/IPlayer.java
index d940e26ab74..caa23ba4126 100644
--- a/src/main/java/cn/nukkit/IPlayer.java
+++ b/src/main/java/cn/nukkit/IPlayer.java
@@ -6,77 +6,63 @@
import java.util.UUID;
/**
- * 用来描述一个玩家和获得这个玩家相应信息的接口。
* An interface to describe a player and get its information.
- *
- * 这个玩家可以在线,也可以是不在线。
- * This player can be online or offline.
+ *
+ * This player can be online or offline.
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
* @see cn.nukkit.Player
* @see cn.nukkit.OfflinePlayer
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public interface IPlayer extends ServerOperator, Metadatable {
/**
- * 返回这个玩家是否在线。
* Returns if this player is online.
*
* @return 这个玩家是否在线。
If this player is online.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
boolean isOnline();
/**
- * 返回这个玩家的名称。
* Returns the name of this player.
- *
- *
如果是在线的玩家,这个函数只会返回登录名字。如果要返回显示的名字,参见{@link cn.nukkit.Player#getDisplayName}
+ *
+ * 如果是在线的玩家,这个函数只会返回登录名字。如果要返回显示的名字,参见{@link cn.nukkit.Player#getDisplayName}
* Notice that this will only return its login name. If you need its display name, turn to
- * {@link cn.nukkit.Player#getDisplayName}
+ * {@link cn.nukkit.Player#getDisplayName}
*
* @return 这个玩家的名称。
The name of this player.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
String getName();
-
+
UUID getUniqueId();
/**
- * 返回这个玩家是否被封禁(ban)。
* Returns if this player is banned.
*
* @return 这个玩家的名称。
The name of this player.
* @see #setBanned
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
boolean isBanned();
/**
- * 设置这个玩家是否被封禁(ban)。
* Sets this player to be banned or to be pardoned.
*
* @param value 如果为{@code true},封禁这个玩家。如果为{@code false},解封这个玩家。
* {@code true} for ban and {@code false} for pardon.
* @see #isBanned
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
void setBanned(boolean value);
/**
- * 返回这个玩家是否已加入白名单。
* Returns if this player is pardoned by whitelist.
*
* @return 这个玩家是否已加入白名单。
If this player is pardoned by whitelist.
* @see cn.nukkit.Server#isWhitelisted
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
boolean isWhitelisted();
/**
- * 把这个玩家加入白名单,或者取消这个玩家的白名单。
* Adds this player to the white list, or removes it from the whitelist.
*
* @param value 如果为{@code true},把玩家加入白名单。如果为{@code false},取消这个玩家的白名单。
@@ -84,59 +70,45 @@ public interface IPlayer extends ServerOperator, Metadatable {
* @see #isWhitelisted
* @see cn.nukkit.Server#addWhitelist
* @see cn.nukkit.Server#removeWhitelist
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
void setWhitelisted(boolean value);
/**
- * 得到这个接口的{@code Player}对象。
* Returns a {@code Player} object for this interface.
*
* @return 这个接口的 {@code Player}对象。
a {@code Player} object for this interface.
* @see cn.nukkit.Server#getPlayerExact
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
Player getPlayer();
/**
- * 返回玩家所在的服务器。
* Returns the server carrying this player.
*
* @return 玩家所在的服务器。
the server carrying this player.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
Server getServer();
/**
- * 得到这个玩家第一次游戏的时间。
* Returns the time this player first played in this server.
*
* @return Unix时间(以秒为单位。
Unix time in seconds.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
Long getFirstPlayed();
/**
- * 得到这个玩家上次加入游戏的时间。
* Returns the time this player last joined in this server.
*
* @return Unix时间(以秒为单位。
Unix time in seconds.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
Long getLastPlayed();
/**
- * 返回这个玩家以前是否来过服务器。
* Returns if this player has played in this server before.
- *
- * 如果想得到这个玩家是不是第一次玩,可以使用:
+ *
* If you want to know if this player is the first time playing in this server, you can use:
- *
- * if(!player.hasPlayerBefore()) {...}
+ * if (!player.hasPlayerBefore()) {...}
*
* @return 这个玩家以前是不是玩过游戏。
If this player has played in this server before.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
boolean hasPlayedBefore();
-
}
diff --git a/src/main/java/cn/nukkit/InterruptibleThread.java b/src/main/java/cn/nukkit/InterruptibleThread.java
index c36cdf2f743..02fe8d9ca09 100644
--- a/src/main/java/cn/nukkit/InterruptibleThread.java
+++ b/src/main/java/cn/nukkit/InterruptibleThread.java
@@ -1,17 +1,14 @@
package cn.nukkit;
/**
- * 描述一个可以被中断的线程的接口。
* An interface to describe a thread that can be interrupted.
- *
- * 在Nukkit服务器停止时,Nukkit会找到所有实现了{@code InterruptibleThread}的线程,并逐一中断。
+ *
* When a Nukkit server is stopping, Nukkit finds all threads implements {@code InterruptibleThread},
- * and interrupt them one by one.
+ * and interrupt them one by one.
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
* @see cn.nukkit.scheduler.AsyncWorker
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public interface InterruptibleThread {
}
diff --git a/src/main/java/cn/nukkit/Nukkit.java b/src/main/java/cn/nukkit/Nukkit.java
index c90a0e9665d..14ccb42917d 100644
--- a/src/main/java/cn/nukkit/Nukkit.java
+++ b/src/main/java/cn/nukkit/Nukkit.java
@@ -1,6 +1,5 @@
package cn.nukkit;
-import cn.nukkit.network.protocol.ProtocolInfo;
import cn.nukkit.utils.ServerKiller;
import com.google.common.base.Preconditions;
import io.netty.util.ResourceLeakDetector;
@@ -13,7 +12,6 @@
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import java.io.IOException;
@@ -30,52 +28,47 @@
*/
/**
- * Nukkit启动类,包含{@code main}函数。
- * The launcher class of Nukkit, including the {@code main} function.
+ * The launcher class of Nukkit, including the {@code main} function
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
@Log4j2
public class Nukkit {
public final static Properties GIT_INFO = getGitInfo();
public final static String VERSION = getVersion();
- public final static String API_VERSION = "1.0.15";
- public final static String CODENAME = "";
- @Deprecated
- public final static String MINECRAFT_VERSION = ProtocolInfo.MINECRAFT_VERSION;
- @Deprecated
- public final static String MINECRAFT_VERSION_NETWORK = ProtocolInfo.MINECRAFT_VERSION_NETWORK;
-
- public final static String PATH = System.getProperty("user.dir") + "/";
- public final static String DATA_PATH = System.getProperty("user.dir") + "/";
+ public final static String API_VERSION = "1.1.0";
+ public final static String PATH = System.getProperty("user.dir") + '/';
+ public final static String DATA_PATH = System.getProperty("user.dir") + '/';
public final static String PLUGIN_PATH = DATA_PATH + "plugins";
- public static final long START_TIME = System.currentTimeMillis();
- public static boolean ANSI = true;
- public static boolean TITLE = false;
- public static boolean shortTitle = requiresShortTitle();
+ /**
+ * Server start time
+ */
+ public final static long START_TIME = System.currentTimeMillis();
+ /**
+ * Console title enabled
+ */
+ public static boolean TITLE = true;
+ /**
+ * Debug logging level
+ */
public static int DEBUG = 1;
public static void main(String[] args) {
- // Force IPv4 since Nukkit is not compatible with IPv6
System.setProperty("java.net.preferIPv4Stack" , "true");
System.setProperty("log4j.skipJansi", "false");
- System.getProperties().putIfAbsent("io.netty.allocator.type", "unpooled"); // Disable memory pooling unless specified
- // Force Mapped ByteBuffers for LevelDB till fixed.
- System.setProperty("leveldb.mmap", "true");
+ // Disable memory pooling unless specified
+ System.getProperties().putIfAbsent("io.netty.allocator.type", "unpooled");
- // Netty logger for debug info
- InternalLoggerFactory.setDefaultFactory(Log4J2LoggerFactory.INSTANCE);
- ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
+ // Force Mapped ByteBuffers for LevelDB till fixed
+ System.setProperty("leveldb.mmap", "true");
// Define args
OptionParser parser = new OptionParser();
parser.allowsUnrecognizedOptions();
OptionSpec helpSpec = parser.accepts("help", "Shows this page").forHelp();
- OptionSpec ansiSpec = parser.accepts("disable-ansi", "Disables console coloring");
OptionSpec titleSpec = parser.accepts("enable-title", "Enables title at the top of the window");
OptionSpec vSpec = parser.accepts("v", "Set verbosity of logging").withRequiredArg().ofType(String.class);
OptionSpec verbositySpec = parser.accepts("verbosity", "Set verbosity of logging").withRequiredArg().ofType(String.class);
@@ -88,13 +81,14 @@ public static void main(String[] args) {
try {
// Display help page
parser.printHelpOn(System.out);
- } catch (IOException e) {
- // ignore
+ } catch (IOException ignored) {
}
return;
}
- ANSI = !options.has(ansiSpec);
+ InternalLoggerFactory.setDefaultFactory(Log4J2LoggerFactory.INSTANCE);
+ ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
+
TITLE = options.has(titleSpec);
String verbosity = options.valueOf(vSpec);
@@ -106,8 +100,7 @@ public static void main(String[] args) {
try {
Level level = Level.valueOf(verbosity);
setLogLevel(level);
- } catch (Exception e) {
- // ignore
+ } catch (Exception ignored) {
}
}
@@ -115,7 +108,7 @@ public static void main(String[] args) {
try {
if (TITLE) {
- System.out.print((char) 0x1b + "]0;Nukkit is starting up..." + (char) 0x07);
+ System.out.print((char) 0x1b + "]0;Nukkit " + getVersion() + (char) 0x07);
}
new Server(PATH, DATA_PATH, PLUGIN_PATH, language);
} catch (Throwable t) {
@@ -125,7 +118,7 @@ public static void main(String[] args) {
if (TITLE) {
System.out.print((char) 0x1b + "]0;Stopping Server..." + (char) 0x07);
}
- log.info("Stopping other threads");
+ log.info("Stopping other threads...");
for (Thread thread : java.lang.Thread.getAllStackTraces().keySet()) {
if (!(thread instanceof InterruptibleThread)) {
@@ -137,7 +130,7 @@ public static void main(String[] args) {
}
}
- ServerKiller killer = new ServerKiller(8);
+ ServerKiller killer = new ServerKiller(10);
killer.start();
if (TITLE) {
@@ -146,21 +139,17 @@ public static void main(String[] args) {
System.exit(0);
}
- private static boolean requiresShortTitle() {
- //Shorter title for windows 8/2012
- String osName = System.getProperty("os.name").toLowerCase();
- return osName.contains("windows") &&(osName.contains("windows 8") || osName.contains("2012"));
- }
-
private static Properties getGitInfo() {
InputStream gitFileStream = Nukkit.class.getClassLoader().getResourceAsStream("git.properties");
if (gitFileStream == null) {
+ log.debug("Unable to find git.properties");
return null;
}
Properties properties = new Properties();
try {
properties.load(gitFileStream);
} catch (IOException e) {
+ log.debug("Unable to load git.properties", e);
return null;
}
return properties;
@@ -170,7 +159,7 @@ private static String getVersion() {
StringBuilder version = new StringBuilder();
version.append("git-");
String commitId;
- if (GIT_INFO == null || (commitId = GIT_INFO.getProperty("git.commit.id.abbrev")) == null) {
+ if (GIT_INFO == null || (commitId = GIT_INFO.getProperty("git.commit.id.abbrev")) == null || commitId.isEmpty()) {
return version.append("null").toString();
}
return version.append(commitId).toString();
@@ -179,16 +168,12 @@ private static String getVersion() {
public static void setLogLevel(Level level) {
Preconditions.checkNotNull(level, "level");
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration log4jConfig = ctx.getConfiguration();
- LoggerConfig loggerConfig = log4jConfig.getLoggerConfig(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+ LoggerConfig loggerConfig = ctx.getConfiguration().getLoggerConfig(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
loggerConfig.setLevel(level);
ctx.updateLoggers();
}
public static Level getLogLevel() {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration log4jConfig = ctx.getConfiguration();
- LoggerConfig loggerConfig = log4jConfig.getLoggerConfig(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
- return loggerConfig.getLevel();
+ return ((LoggerContext) LogManager.getContext(false)).getConfiguration().getLoggerConfig(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME).getLevel();
}
}
diff --git a/src/main/java/cn/nukkit/OfflinePlayer.java b/src/main/java/cn/nukkit/OfflinePlayer.java
index 66c8c9d39cb..18c45cf292c 100644
--- a/src/main/java/cn/nukkit/OfflinePlayer.java
+++ b/src/main/java/cn/nukkit/OfflinePlayer.java
@@ -8,27 +8,24 @@
import java.util.UUID;
/**
- * 描述一个不在线的玩家的类。
- * Describes an offline player.
+ * Describes an offline player
*
* @author MagicDroidX(code) @ Nukkit Project
* @author 粉鞋大妈(javadoc) @ Nukkit Project
* @see cn.nukkit.Player
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public class OfflinePlayer implements IPlayer {
+
private final Server server;
private final CompoundTag namedTag;
/**
- * 初始化这个{@code OfflinePlayer}对象。
* Initializes the object {@code OfflinePlayer}.
*
* @param server 这个玩家所在服务器的{@code Server}对象。
* The server this player is in, as a {@code Server} object.
* @param uuid 这个玩家的UUID。
* UUID of this player.
- * @since Nukkit 1.0 | Nukkit API 1.0.0
*/
public OfflinePlayer(Server server, UUID uuid) {
this(server, uuid, null);
@@ -49,6 +46,7 @@ public OfflinePlayer(Server server, UUID uuid, String name) {
} else {
throw new IllegalArgumentException("Name and UUID cannot both be null");
}
+
if (nbt == null) {
nbt = new CompoundTag();
}
@@ -57,7 +55,9 @@ public OfflinePlayer(Server server, UUID uuid, String name) {
if (uuid != null) {
this.namedTag.putLong("UUIDMost", uuid.getMostSignificantBits());
this.namedTag.putLong("UUIDLeast", uuid.getLeastSignificantBits());
- } else {
+ }
+
+ if (name != null && (uuid == null || !this.namedTag.contains("NameTag"))) {
this.namedTag.putString("NameTag", name);
}
}
@@ -173,5 +173,4 @@ public boolean hasMetadata(String metadataKey) {
public void removeMetadata(String metadataKey, Plugin owningPlugin) {
this.server.getPlayerMetadata().removeMetadata(this, metadataKey, owningPlugin);
}
-
}
diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java
index 51d94ed114f..a8024ccac01 100644
--- a/src/main/java/cn/nukkit/Player.java
+++ b/src/main/java/cn/nukkit/Player.java
@@ -4,15 +4,19 @@
import cn.nukkit.block.*;
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.blockentity.BlockEntityItemFrame;
+import cn.nukkit.blockentity.BlockEntityLectern;
import cn.nukkit.blockentity.BlockEntitySpawnable;
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
import cn.nukkit.command.data.CommandDataVersions;
import cn.nukkit.entity.*;
+import cn.nukkit.entity.custom.EntityManager;
import cn.nukkit.entity.data.*;
import cn.nukkit.entity.item.*;
import cn.nukkit.entity.projectile.EntityArrow;
+import cn.nukkit.entity.projectile.EntityProjectile;
import cn.nukkit.entity.projectile.EntityThrownTrident;
+import cn.nukkit.event.block.WaterFrostEvent;
import cn.nukkit.event.entity.*;
import cn.nukkit.event.entity.EntityDamageEvent.DamageCause;
import cn.nukkit.event.entity.EntityDamageEvent.DamageModifier;
@@ -30,27 +34,26 @@
import cn.nukkit.form.window.FormWindow;
import cn.nukkit.form.window.FormWindowCustom;
import cn.nukkit.inventory.*;
-import cn.nukkit.inventory.transaction.CraftingTransaction;
-import cn.nukkit.inventory.transaction.EnchantTransaction;
-import cn.nukkit.inventory.transaction.InventoryTransaction;
-import cn.nukkit.inventory.transaction.RepairItemTransaction;
+import cn.nukkit.inventory.transaction.*;
import cn.nukkit.inventory.transaction.action.InventoryAction;
import cn.nukkit.inventory.transaction.data.ReleaseItemData;
import cn.nukkit.inventory.transaction.data.UseItemData;
import cn.nukkit.inventory.transaction.data.UseItemOnEntityData;
import cn.nukkit.item.*;
+import cn.nukkit.item.custom.CustomItemManager;
import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.lang.TextContainer;
import cn.nukkit.lang.TranslationContainer;
import cn.nukkit.level.*;
import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.level.particle.ItemBreakParticle;
import cn.nukkit.level.particle.PunchBlockParticle;
import cn.nukkit.math.*;
import cn.nukkit.metadata.MetadataValue;
import cn.nukkit.nbt.NBTIO;
import cn.nukkit.nbt.tag.*;
import cn.nukkit.network.CompressionProvider;
-import cn.nukkit.network.Network;
import cn.nukkit.network.SourceInterface;
import cn.nukkit.network.encryption.PrepareEncryptionTask;
import cn.nukkit.network.protocol.*;
@@ -65,33 +68,40 @@
import cn.nukkit.resourcepacks.ResourcePack;
import cn.nukkit.scheduler.AsyncTask;
import cn.nukkit.utils.*;
-import co.aikar.timings.Timing;
-import co.aikar.timings.Timings;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Sets;
import io.netty.util.internal.PlatformDependent;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
-import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
-import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
-import it.unimi.dsi.fastutil.longs.LongIterator;
-import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
+import lombok.Getter;
+import lombok.Setter;
import lombok.extern.log4j.Log4j2;
+import javax.annotation.Nullable;
+import java.awt.*;
+import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.ByteOrder;
+import java.util.List;
import java.util.*;
+import java.util.Queue;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.stream.Stream;
/**
+ * The Player class
+ *
* @author MagicDroidX & Box
* Nukkit Project
*/
@@ -102,19 +112,17 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde
public static final int CREATIVE = 1;
public static final int ADVENTURE = 2;
public static final int SPECTATOR = 3;
- public static final int VIEW = SPECTATOR;
-
- public static final int SURVIVAL_SLOTS = 36;
- public static final int CREATIVE_SLOTS = 112;
public static final int CRAFTING_SMALL = 0;
public static final int CRAFTING_BIG = 1;
public static final int CRAFTING_ANVIL = 2;
public static final int CRAFTING_ENCHANT = 3;
public static final int CRAFTING_BEACON = 4;
+ public static final int CRAFTING_SMITHING = 1003;
+ public static final int CRAFTING_LOOM = 1004;
public static final float DEFAULT_SPEED = 0.1f;
- public static final float MAXIMUM_SPEED = 0.5f;
+ public static final float MAXIMUM_SPEED = 6f; // TODO: Decrease when block collisions are fixed
public static final float DEFAULT_FLY_SPEED = 0.05f;
public static final int PERMISSION_CUSTOM = 3;
@@ -125,41 +133,34 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde
public static final int ANVIL_WINDOW_ID = 2;
public static final int ENCHANT_WINDOW_ID = 3;
public static final int BEACON_WINDOW_ID = 4;
+ public static final int LOOM_WINDOW_ID = 2;
+ public static final int SMITHING_WINDOW_ID = 6;
- protected static final int RESOURCE_PACK_CHUNK_SIZE = 8 * 1024; // 8KB
+ protected static final int RESOURCE_PACK_CHUNK_SIZE = 8192; // 8KB
protected final SourceInterface interfaz;
protected final NetworkPlayerSession networkSession;
+ @Deprecated
+ public long creationTime;
public boolean playedBefore;
- public boolean spawned = false;
- public boolean loggedIn = false;
- public boolean locallyInitialized = false;
- private boolean loginVerified = false;
- private int unverifiedPackets;
+ public boolean spawned;
+ public boolean loggedIn;
+ private boolean loginVerified;
private boolean loginPacketReceived;
- private boolean awaitingEncryptionHandshake;
+ protected boolean networkSettingsRequested;
public int gamemode;
- public long lastBreak;
- private BlockVector3 lastBreakPosition = new BlockVector3();
-
- protected int windowCnt = 4;
+ protected long randomClientId;
+ private String unverifiedUsername = "";
protected final BiMap windows = HashBiMap.create();
-
protected final BiMap windowIndex = windows.inverse();
protected final Set permanentWindows = new IntOpenHashSet();
+ @Getter
private boolean inventoryOpen;
+ protected int windowCnt = 4;
protected int closingWindowId = Integer.MIN_VALUE;
- protected int messageCounter = 2;
-
- private String clientSecret;
-
- public Vector3 speed = null;
-
- private final Queue clientMovements = PlatformDependent.newMpscQueue(4);
-
public final HashSet achievements = new HashSet<>();
public int craftingType = CRAFTING_SMALL;
@@ -169,63 +170,58 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde
protected CraftingTransaction craftingTransaction;
protected EnchantTransaction enchantTransaction;
protected RepairItemTransaction repairItemTransaction;
-
- public long creationTime = 0;
-
- protected long randomClientId;
-
- protected Vector3 forceMovement = null;
-
- protected Vector3 teleportPosition = null;
+ protected LoomTransaction loomTransaction;
+ protected SmithingTransaction smithingTransaction;
+
+ public Vector3 speed;
+ protected Vector3 forceMovement;
+ protected Vector3 teleportPosition;
+ protected Vector3 newPosition;
+ protected Vector3 sleeping;
+ private Vector3 lastRightClickPos;
+ private final Queue clientMovements = PlatformDependent.newMpscQueue(4);
protected boolean connected = true;
protected final InetSocketAddress socketAddress;
protected boolean removeFormat = true;
protected String username;
- private String unverifiedUsername = "";
protected String iusername;
protected String displayName;
- protected int startAction = -1;
-
- protected Vector3 sleeping = null;
- protected Long clientID = null;
-
- private int loaderId;
-
+ private boolean hasSpawnChunks;
+ private final int loaderId;
+ private int chunksSent;
+ protected int nextChunkOrderRun = 1;
+ protected int chunkRadius;
+ protected int viewDistance;
public final Map usedChunks = new Long2ObjectOpenHashMap<>();
-
- protected int chunkLoadCount = 0;
protected final Long2ObjectLinkedOpenHashMap loadQueue = new Long2ObjectLinkedOpenHashMap<>();
- protected int nextChunkOrderRun = 1;
protected final Map hiddenPlayers = new HashMap<>();
- protected Vector3 newPosition = null;
-
- protected int chunkRadius;
- protected int viewDistance;
- protected final int chunksPerTick;
- protected final int spawnThreshold;
+ protected Position spawnPosition;
- protected Position spawnPosition = null;
-
- protected int inAirTicks = 0;
- protected int startAirTicks = 5;
+ protected int inAirTicks;
+ protected int startAirTicks = 10;
protected AdventureSettings adventureSettings;
- protected boolean checkMovement = true;
+ private PermissibleBase perm;
- private PermissibleBase perm = null;
+ /**
+ * Option not to update shield blocking status.
+ */
+ @Getter
+ @Setter
+ private boolean canTickShield = true;
- private int exp = 0;
- private int expLevel = 0;
+ private int exp;
+ private int expLevel;
- protected PlayerFood foodData = null;
+ protected PlayerFood foodData;
- private Entity killer = null;
+ private Entity killer;
private final AtomicReference locale = new AtomicReference<>(null);
@@ -235,68 +231,164 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde
protected boolean enableClientCommand = true;
- private BlockEnderChest viewingEnderChest = null;
-
- protected int lastEnderPearl = 20;
- protected int lastChorusFruitTeleport = 20;
+ private BlockEnderChest viewingEnderChest;
private LoginChainData loginChainData;
- public Block breakingBlock = null;
- private PlayerBlockActionData lastBlockAction;
-
- public int pickedXPOrb = 0;
+ public int pickedXPOrb;
+ private boolean canPickupXP = true;
- protected int formWindowCount = 0;
+ protected int formWindowCount;
protected Map formWindows = new Int2ObjectOpenHashMap<>();
protected Map serverSettings = new Int2ObjectOpenHashMap<>();
protected Map dummyBossBars = new Long2ObjectLinkedOpenHashMap<>();
- private AsyncTask preLoginEventTask = null;
- protected boolean shouldLogin = false;
-
- public EntityFishingHook fishing = null;
+ private AsyncTask preLoginEventTask;
+ protected boolean shouldLogin;
- public long lastSkinChange;
-
- protected double lastRightClickTime = 0.0;
- protected Vector3 lastRightClickPos = null;
+ private static Stream pkIDs;
+ protected int startAction = -1;
+ private int lastEmote;
+ protected int lastEnderPearl = 20;
+ protected int lastChorusFruitTeleport = 20;
+ protected int lastFireworkBoost = 20;
+ public long lastSkinChange = -1;
+ private double lastRightClickTime;
+ public long lastBreak = -1; // When last block break was started
+ private BlockVector3 lastBreakPosition = new BlockVector3();
+ public Block breakingBlock; // Block player is breaking currently
+ private BlockFace breakingBlockFace; // Block face player is breaking currently
+ private PlayerBlockActionData lastBlockAction;
+ public EntityFishingHook fishing;
+ @Getter
+ private boolean formOpen;
+ private boolean flySneaking;
+ public boolean locallyInitialized;
+ private boolean foodEnabled = true;
+ protected boolean checkMovement = true;
private int timeSinceRest;
+ private boolean inSoulSand;
+ private boolean dimensionChangeInProgress;
+ private boolean awaitingDimensionAck;
+ private boolean awaitingEncryptionHandshake;
+ private int riderJumpTick;
+ private int riptideTicks;
+ private int blockingDelay;
+ private int fireworkBoostTicks;
+ private int fireworkBoostLevel;
+
+ @Setter
+ private boolean needSendData;
+ private boolean needSendAdventureSettings;
+ private boolean needSendFoodLevel;
+ @Setter
+ private boolean needSendInventory;
+ private boolean needSendHeldItem;
+ private boolean needSendRotation;
+ private boolean dimensionFix560;
+
+ /**
+ * Save last crossbow load tick (used to prevent loading a crossbow launching it immediately afterward)
+ */
+ private int crossbowLoadTick;
+ /**
+ * Packets that can be received before the player has logged in
+ */
+ private static final Set PRE_LOGIN_PACKETS = Sets.newHashSet(ProtocolInfo.BATCH_PACKET, ProtocolInfo.LOGIN_PACKET, ProtocolInfo.REQUEST_NETWORK_SETTINGS_PACKET, ProtocolInfo.REQUEST_CHUNK_RADIUS_PACKET, ProtocolInfo.SET_LOCAL_PLAYER_AS_INITIALIZED_PACKET, ProtocolInfo.RESOURCE_PACK_CHUNK_REQUEST_PACKET, ProtocolInfo.RESOURCE_PACK_CLIENT_RESPONSE_PACKET, ProtocolInfo.CLIENT_CACHE_STATUS_PACKET, ProtocolInfo.PACKET_VIOLATION_WARNING_PACKET, ProtocolInfo.CLIENT_TO_SERVER_HANDSHAKE_PACKET);
+ /**
+ * Default kick message for flying
+ */
+ private static final String MSG_FLYING_NOT_ENABLED = "Flying is not enabled on this server";
+ /**
+ * Get action start tick
+ * @return action start tick, -1 = no action in progress
+ */
public int getStartActionTick() {
return startAction;
}
+ /**
+ * Set action start tick to current tick
+ */
public void startAction() {
this.startAction = this.server.getTick();
}
+ /**
+ * Reset action start tick
+ */
public void stopAction() {
this.startAction = -1;
}
+ /**
+ * Get last tick an ender pearl was used
+ * @return last ender pearl used tick
+ */
public int getLastEnderPearlThrowingTick() {
return lastEnderPearl;
}
+ /**
+ * Set last ender pearl throw tick to current tick
+ */
public void onThrowEnderPearl() {
this.lastEnderPearl = this.server.getTick();
}
+ /**
+ * Get last chorus fruit teleport tick
+ * @return last chorus fruit teleport tick
+ */
public int getLastChorusFruitTeleport() {
return lastChorusFruitTeleport;
}
+ /**
+ * Set last chorus fruit teleport tick to current tick
+ */
public void onChorusFruitTeleport() {
this.lastChorusFruitTeleport = this.server.getTick();
}
+ /**
+ * Get last tick firework boost for elytra was used
+ * @return last firework boost tick
+ */
+ public int getLastFireworkBoostTick() {
+ return lastFireworkBoost;
+ }
+
+ /**
+ * Set last firework boost tick to current tick
+ */
+ public void onFireworkBoost(int boostLevel) {
+ this.lastFireworkBoost = this.server.getTick();
+ this.fireworkBoostLevel = boostLevel;
+ this.fireworkBoostTicks = boostLevel == 3 ? 44 : boostLevel == 2 ? 29 : 23;
+ }
+
+ /**
+ * Set last spin attack tick to current tick
+ */
+ public void onSpinAttack(int riptideLevel) {
+ this.riptideTicks = 50 + (riptideLevel << 5);
+ }
+
+ /**
+ * Get ender chest the player is viewing
+ * @return the ender chest player is viewing or null if player is not viewing an ender chest
+ */
public BlockEnderChest getViewingEnderChest() {
return viewingEnderChest;
}
+ /**
+ * Add player to ender chest viewers
+ */
public void setViewingEnderChest(BlockEnderChest chest) {
if (chest == null && this.viewingEnderChest != null) {
this.viewingEnderChest.getViewers().remove(this);
@@ -306,12 +398,17 @@ public void setViewingEnderChest(BlockEnderChest chest) {
this.viewingEnderChest = chest;
}
+ /**
+ * Get player quit message
+ * @return quit message
+ */
public TranslationContainer getLeaveMessage() {
- return new TranslationContainer(TextFormat.YELLOW + "%multiplayer.player.left", this.getDisplayName());
+ return new TranslationContainer(TextFormat.YELLOW + "%multiplayer.player.left", this.displayName);
}
+ @Deprecated
public String getClientSecret() {
- return clientSecret;
+ return null;
}
/**
@@ -326,30 +423,30 @@ public Long getClientId() {
@Override
public boolean isBanned() {
- return this.server.getNameBans().isBanned(this.getName());
+ return this.server.getNameBans().isBanned(this.username);
}
@Override
public void setBanned(boolean value) {
if (value) {
- this.server.getNameBans().addBan(this.getName(), null, null, null);
- this.kick(PlayerKickEvent.Reason.NAME_BANNED, "Banned by admin");
+ this.server.getNameBans().addBan(this.username, null, null, null);
+ this.kick(PlayerKickEvent.Reason.NAME_BANNED, "You are banned!");
} else {
- this.server.getNameBans().remove(this.getName());
+ this.server.getNameBans().remove(this.username);
}
}
@Override
public boolean isWhitelisted() {
- return this.server.isWhitelisted(this.getName().toLowerCase());
+ return this.server.isWhitelisted(this.iusername);
}
@Override
public void setWhitelisted(boolean value) {
if (value) {
- this.server.addWhitelist(this.getName().toLowerCase());
+ this.server.addWhitelist(this.iusername);
} else {
- this.server.removeWhitelist(this.getName().toLowerCase());
+ this.server.removeWhitelist(this.iusername);
}
}
@@ -373,87 +470,145 @@ public boolean hasPlayedBefore() {
return this.playedBefore;
}
+ /**
+ * Get current adventure settings
+ * @return adventure settings
+ */
public AdventureSettings getAdventureSettings() {
return adventureSettings;
}
+ /**
+ * Set and send adventure settings
+ * @param adventureSettings new adventure settings
+ */
public void setAdventureSettings(AdventureSettings adventureSettings) {
this.adventureSettings = adventureSettings.clone(this);
this.adventureSettings.update();
}
+ /**
+ * Reset in air ticks
+ */
public void resetInAirTicks() {
+ if (this.inAirTicks != 0) {
+ this.startAirTicks = 10;
+ }
this.inAirTicks = 0;
}
+ /**
+ * Set allow flight adventure setting
+ * @param value allow flight enabled
+ */
@Deprecated
public void setAllowFlight(boolean value) {
- this.getAdventureSettings().set(Type.ALLOW_FLIGHT, value);
- this.getAdventureSettings().update();
+ this.adventureSettings.set(Type.ALLOW_FLIGHT, value);
+ this.adventureSettings.update();
}
+ /**
+ * Check wether allow flight adventure setting is enabled
+ * @return allow flight enabled
+ */
@Deprecated
public boolean getAllowFlight() {
- return this.getAdventureSettings().get(Type.ALLOW_FLIGHT);
+ return this.adventureSettings.get(Type.ALLOW_FLIGHT);
}
+ /**
+ * Set can modify world adventure setting(s)
+ * @param value can modify world
+ */
public void setAllowModifyWorld(boolean value) {
- this.getAdventureSettings().set(Type.WORLD_IMMUTABLE, !value);
- this.getAdventureSettings().set(Type.MINE, value);
- this.getAdventureSettings().set(Type.BUILD, value);
- this.getAdventureSettings().update();
+ this.adventureSettings.set(Type.WORLD_IMMUTABLE, !value);
+ this.adventureSettings.set(Type.MINE, value);
+ this.adventureSettings.set(Type.BUILD, value);
+ this.adventureSettings.update();
}
+ /**
+ * Set can interact adventure setting(s)
+ * @param value can interact
+ */
public void setAllowInteract(boolean value) {
- setAllowInteract(value, value);
+ this.setAllowInteract(value, value);
}
+ /**
+ * Set can interact adventure setting(s)
+ * @param value can interact
+ * @param containers can open containers
+ */
public void setAllowInteract(boolean value, boolean containers) {
- this.getAdventureSettings().set(Type.WORLD_IMMUTABLE, !value);
- this.getAdventureSettings().set(Type.DOORS_AND_SWITCHED, value);
- this.getAdventureSettings().set(Type.OPEN_CONTAINERS, containers);
- this.getAdventureSettings().update();
+ this.adventureSettings.set(Type.WORLD_IMMUTABLE, !value);
+ this.adventureSettings.set(Type.DOORS_AND_SWITCHED, value);
+ this.adventureSettings.set(Type.OPEN_CONTAINERS, containers);
+ this.adventureSettings.update();
}
+ /**
+ * Set auto jump adventure setting (adventureSettings.set(Type.AUTO_JUMP) + adventureSettings.update())
+ * @param value auto jump enabled
+ */
@Deprecated
public void setAutoJump(boolean value) {
- this.getAdventureSettings().set(Type.AUTO_JUMP, value);
- this.getAdventureSettings().update();
+ this.adventureSettings.set(Type.AUTO_JUMP, value);
+ this.adventureSettings.update();
}
+ /**
+ * Check whether auto jump adventure setting is enabled (adventureSettings.get(Type.AUTO_JUMP))
+ * @return auto jump enabled
+ */
@Deprecated
public boolean hasAutoJump() {
- return this.getAdventureSettings().get(Type.AUTO_JUMP);
+ return this.adventureSettings.get(Type.AUTO_JUMP);
}
@Override
public void spawnTo(Player player) {
- if (this.spawned && player.spawned && this.isAlive() && player.getLevel() == this.level && player.canSee(this) && !this.isSpectator()) {
+ if (this.spawned && player.spawned && this.isAlive() && player.isAlive() && player.getLevel() == this.level && player.canSee(this) && !this.isSpectator()) {
super.spawnTo(player);
}
}
- @Override
- public Server getServer() {
- return this.server;
- }
-
+ /**
+ * Check whether player can use text formatting
+ * @return player can use text formatting
+ */
public boolean getRemoveFormat() {
return removeFormat;
}
+ /**
+ * Make player unable to use text formatting (color codes etc.)
+ */
public void setRemoveFormat() {
this.setRemoveFormat(true);
}
+ /**
+ * Set whether player can use text formatting (color codes etc.)
+ * @param remove remove formatting from received texts
+ */
public void setRemoveFormat(boolean remove) {
this.removeFormat = remove;
}
+ /**
+ * Check whether player can see another player (not hidden)
+ * @param player target player
+ * @return player can see the target player
+ */
public boolean canSee(Player player) {
return !this.hiddenPlayers.containsKey(player.getUniqueId());
}
+ /**
+ * Hide this player from target player
+ * @param player target player
+ */
public void hidePlayer(Player player) {
if (this == player) {
return;
@@ -462,6 +617,10 @@ public void hidePlayer(Player player) {
player.despawnFrom(this);
}
+ /**
+ * Allow target player to see this player
+ * @param player target player
+ */
public void showPlayer(Player player) {
if (this == player) {
return;
@@ -477,14 +636,26 @@ public boolean canCollideWith(Entity entity) {
return false;
}
+ /**
+ * Check whether player can pick up xp orbs
+ * @return can pick up xp orbs
+ */
+ public boolean canPickupXP() {
+ return this.canPickupXP;
+ }
+
+ /**
+ * Set whether player can pick up xp orbs
+ * @param canPickupXP can pick up xp orbs
+ */
+ public void setCanPickupXP(boolean canPickupXP) {
+ this.canPickupXP = canPickupXP;
+ }
+
@Override
public void resetFallDistance() {
super.resetFallDistance();
- if (this.inAirTicks != 0) {
- this.startAirTicks = 5;
- }
- this.inAirTicks = 0;
- this.highestPosition = this.y;
+ this.resetInAirTicks();
}
@Override
@@ -494,7 +665,7 @@ public boolean isOnline() {
@Override
public boolean isOp() {
- return this.server.isOp(this.getName());
+ return this.server.isOp(this.username);
}
@Override
@@ -504,13 +675,13 @@ public void setOp(boolean value) {
}
if (value) {
- this.server.addOp(this.getName());
+ this.server.addOp(this.username);
} else {
- this.server.removeOp(this.getName());
+ this.server.removeOp(this.username);
}
this.recalculatePermissions();
- this.getAdventureSettings().update();
+ this.adventureSettings.update();
this.sendCommandData();
}
@@ -573,13 +744,21 @@ public void recalculatePermissions() {
this.server.getPluginManager().subscribeToPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE, this);
}
- if (this.isEnableClientCommand() && spawned) this.sendCommandData();
+ if (this.enableClientCommand && spawned) this.sendCommandData();
}
+ /**
+ * Are commands enabled for this player on the client side
+ * @return commands enabled
+ */
public boolean isEnableClientCommand() {
return this.enableClientCommand;
}
+ /**
+ * Set commands enabled client side. This does not necessarily prevent commands from being used.
+ * @param enable can use commands
+ */
public void setEnableClientCommand(boolean enable) {
this.enableClientCommand = enable;
SetCommandsEnabledPacket pk = new SetCommandsEnabledPacket();
@@ -588,26 +767,26 @@ public void setEnableClientCommand(boolean enable) {
if (enable) this.sendCommandData();
}
+ /**
+ * Send updated command data (AvailableCommandsPacket)
+ */
public void sendCommandData() {
- if (!spawned) {
- return;
- }
AvailableCommandsPacket pk = new AvailableCommandsPacket();
Map data = new HashMap<>();
- int count = 0;
+
for (Command command : this.server.getCommandMap().getCommands().values()) {
- if ("help".equals(command.getName())) {
- continue; // The client will add this
- }
- if (!command.testPermissionSilent(this) || !command.isRegistered()) {
- continue;
+ if (command.isRegistered()) {
+ if ("help".equals(command.getName())) {
+ continue; // The client will add this
+ }
+ CommandDataVersions commandData = command.generateCustomCommandData(this);
+ if (commandData != null) { // No permission
+ data.put(command.getName(), commandData);
+ }
}
- ++count;
- CommandDataVersions data0 = command.generateCustomCommandData(this);
- data.put(command.getName(), data0);
}
- if (count > 0) {
- //TODO: structure checking
+
+ if (!data.isEmpty()) {
pk.commands = data;
this.dataPacket(pk);
}
@@ -624,25 +803,13 @@ public Player(SourceInterface interfaz, Long clientID, InetSocketAddress socketA
this.networkSession = interfaz.getSession(socketAddress);
this.perm = new PermissibleBase(this);
this.server = Server.getInstance();
- this.lastBreak = -1;
this.socketAddress = socketAddress;
- this.clientID = clientID;
this.loaderId = Level.generateChunkLoaderId(this);
- this.chunksPerTick = this.server.getConfig("chunk-sending.per-tick", 4);
- this.spawnThreshold = this.server.getConfig("chunk-sending.spawn-threshold", 56);
- this.spawnPosition = null;
this.gamemode = this.server.getGamemode();
this.setLevel(this.server.getDefaultLevel());
this.viewDistance = this.server.getViewDistance();
- this.chunkRadius = viewDistance;
- //this.newPosition = new Vector3(0, 0, 0);
+ this.chunkRadius = this.viewDistance;
this.boundingBox = new SimpleAxisAlignedBB(0, 0, 0, 0, 0, 0);
- this.lastSkinChange = -1;
-
- this.uuid = null;
- this.rawUUID = null;
-
- this.creationTime = System.currentTimeMillis();
}
@Override
@@ -656,26 +823,51 @@ public boolean isPlayer() {
return true;
}
+ /**
+ * Remove achievement from player if the player has it
+ * @param achievementId achievement id
+ */
public void removeAchievement(String achievementId) {
achievements.remove(achievementId);
}
+ /**
+ * Check whether player has an achievement
+ * @param achievementId achievement id
+ * @return has achievement
+ */
public boolean hasAchievement(String achievementId) {
return achievements.contains(achievementId);
}
+ /**
+ * Check whether player is still connected
+ * @return connected
+ */
public boolean isConnected() {
return connected;
}
+ /**
+ * Get player's display name. Default value is player's username.
+ * @return display name
+ */
public String getDisplayName() {
return this.displayName;
}
+ /**
+ * Set player's display name
+ * @param displayName display name
+ */
public void setDisplayName(String displayName) {
+ if (displayName == null) {
+ displayName = "";
+ server.getLogger().debug("Warning: setDisplayName: argument is null", new Throwable(""));
+ }
this.displayName = displayName;
if (this.spawned) {
- this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getDisplayName(), this.getSkin(), this.getLoginChainData().getXUID());
+ this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.displayName, this.getSkin(), this.loginChainData.getXUID());
}
}
@@ -683,30 +875,62 @@ public void setDisplayName(String displayName) {
public void setSkin(Skin skin) {
super.setSkin(skin);
if (this.spawned) {
- this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.getDisplayName(), skin, this.getLoginChainData().getXUID());
+ this.server.updatePlayerListData(this.getUniqueId(), this.getId(), this.displayName, skin, this.loginChainData.getXUID());
}
}
+ /**
+ * Get player's host address
+ * @return host address
+ */
public String getAddress() {
return this.socketAddress.getAddress().getHostAddress();
}
+ /**
+ * Get the port of player's connection
+ * @return port
+ */
public int getPort() {
return this.socketAddress.getPort();
}
+ /**
+ * Get player's socket address
+ * @return socket address
+ */
public InetSocketAddress getSocketAddress() {
return this.socketAddress;
}
+ /**
+ * Get most recent position of received movements
+ * @return next position or current position if no next position has been received
+ */
public Position getNextPosition() {
return this.newPosition != null ? new Position(this.newPosition.x, this.newPosition.y, this.newPosition.z, this.level) : this.getPosition();
}
+ public AxisAlignedBB getNextPositionBB() {
+ if (this.newPosition == null) {
+ return this.boundingBox;
+ }
+ Vector3 diff = this.newPosition.subtract(this);
+ return this.boundingBox.getOffsetBoundingBox(diff.x, diff.y, diff.z);
+ }
+
+ /**
+ * Check whether player is sleeping
+ * @return is sleeping
+ */
public boolean isSleeping() {
return this.sleeping != null;
}
+ /**
+ * Get in air ticks
+ * @return in air ticks
+ */
public int getInAirTicks() {
return this.inAirTicks;
}
@@ -714,33 +938,58 @@ public int getInAirTicks() {
/**
* Returns whether the player is currently using an item (right-click and hold).
*
- * @return bool
+ * @return whether the player is currently using an item
*/
public boolean isUsingItem() {
- return this.getDataFlag(DATA_FLAGS, DATA_FLAG_ACTION) && this.startAction > -1;
+ return this.startAction > -1 && this.getDataFlag(DATA_FLAGS, DATA_FLAG_ACTION);
}
+ /**
+ * Set using item flag
+ * @param value is using item
+ */
public void setUsingItem(boolean value) {
this.startAction = value ? this.server.getTick() : -1;
this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, value);
}
+ /**
+ * Get interaction button text
+ * @return button text
+ */
public String getButtonText() {
return this.buttonText;
}
+ /**
+ * Set interaction button text
+ * @param text button text
+ */
public void setButtonText(String text) {
- if (this.buttonText.equals(text)) {
- return;
+ if (text == null) {
+ text = "";
+ server.getLogger().debug("Warning: setButtonText: argument is null", new Throwable(""));
+ }
+ if (!text.equals(buttonText)) {
+ this.buttonText = text;
+ this.setDataPropertyAndSendOnlyToSelf(new StringEntityData(Entity.DATA_INTERACTIVE_TAG, this.buttonText));
}
- this.buttonText = text;
- this.setDataProperty(new StringEntityData(Entity.DATA_INTERACTIVE_TAG, this.buttonText));
}
+ /**
+ * Unload a chunk on current level
+ * @param x chunk x
+ * @param z chunk z
+ */
public void unloadChunk(int x, int z) {
this.unloadChunk(x, z, null);
}
+ /**
+ * Unload a chunk on given level
+ * @param x chunk x
+ * @param z chunk z
+ */
public void unloadChunk(int x, int z, Level level) {
level = level == null ? this.level : level;
long index = Level.chunkHash(x, z);
@@ -757,24 +1006,69 @@ public void unloadChunk(int x, int z, Level level) {
this.loadQueue.remove(index);
}
+ /**
+ * Unload all loaded chunks
+ * @param online player is online; send entity despawn packets
+ */
+ private void unloadChunks(boolean online) {
+ for (long index : this.usedChunks.keySet()) {
+ int chunkX = Level.getHashX(index);
+ int chunkZ = Level.getHashZ(index);
+ this.level.unregisterChunkLoader(this, chunkX, chunkZ);
+
+ for (Entity entity : level.getChunkEntities(chunkX, chunkZ).values()) {
+ if (entity != this) {
+ if (online) {
+ entity.despawnFrom(this);
+ } else {
+ entity.hasSpawned.remove(loaderId);
+ }
+ }
+ }
+ }
+
+ this.usedChunks.clear();
+ this.loadQueue.clear();
+ }
+
+ /**
+ * Get player's spawn position
+ * @return player's spawn position or server's default (safe) spawn position if not set
+ */
public Position getSpawn() {
- if (this.spawnPosition != null && this.spawnPosition.getLevel() != null) {
- return this.spawnPosition;
+ if (this.spawnPosition != null && this.spawnPosition.getLevel() != null && this.spawnPosition.getLevel().getProvider() != null) {
+ return this.spawnPosition.add(0.5, 0, 0.5);
} else {
return this.server.getDefaultLevel().getSafeSpawn();
}
}
+ /**
+ * Get player's spawn position
+ * @return player's spawn position or null if not set
+ */
+ @Nullable
+ public Position getSpawnPosition() {
+ return this.spawnPosition;
+ }
+
+ /**
+ * Send a chunk packet
+ * @param x chunk x
+ * @param z xhunk z
+ * @param packet chunk packet
+ */
public void sendChunk(int x, int z, DataPacket packet) {
if (!this.connected) {
return;
}
- this.usedChunks.put(Level.chunkHash(x, z), Boolean.TRUE);
- this.chunkLoadCount++;
+ this.usedChunks.put(Level.chunkHash(x, z), true);
this.dataPacket(packet);
+ this.chunksSent++;
+
if (this.spawned) {
for (Entity entity : this.level.getChunkEntities(x, z).values()) {
if (this != entity && !entity.closed && entity.isAlive()) {
@@ -782,30 +1076,33 @@ public void sendChunk(int x, int z, DataPacket packet) {
}
}
}
- }
- public void sendChunk(int x, int z, int subChunkCount, byte[] payload) {
- if (!this.connected) {
- return;
+ // Hack: Fix dimension screen issues
+ if (this.dimensionFix560) {
+ this.dimensionFix560 = false;
+ PlayerActionPacket pap = new PlayerActionPacket();
+ pap.action = PlayerActionPacket.ACTION_DIMENSION_CHANGE_ACK;
+ pap.entityId = this.getId();
+ this.dataPacket(pap);
}
+ }
- this.usedChunks.put(Level.chunkHash(x, z), true);
- this.chunkLoadCount++;
-
- LevelChunkPacket pk = new LevelChunkPacket();
- pk.chunkX = x;
- pk.chunkZ = z;
- pk.subChunkCount = subChunkCount;
- pk.data = payload;
-
- this.dataPacket(pk);
-
- if (this.spawned) {
- for (Entity entity : this.level.getChunkEntities(x, z).values()) {
- if (this != entity && !entity.closed && entity.isAlive()) {
- entity.spawnTo(this);
- }
- }
+ /**
+ * Send a chunk packet
+ * @param x chunk x
+ * @param z xhunk z
+ * @param subChunkCount sub chunk count
+ * @param payload packet payload
+ */
+ public void sendChunk(int x, int z, int subChunkCount, byte[] payload, int dimension) {
+ if (this.connected) {
+ LevelChunkPacket pk = new LevelChunkPacket();
+ pk.chunkX = x;
+ pk.chunkZ = z;
+ pk.dimension = dimension;
+ pk.subChunkCount = subChunkCount;
+ pk.data = payload;
+ this.sendChunk(x, z, pk);
}
}
@@ -814,35 +1111,38 @@ protected void sendNextChunk() {
return;
}
- Timings.playerChunkSendTimer.startTiming();
-
if (!loadQueue.isEmpty()) {
int count = 0;
ObjectIterator> iter = loadQueue.long2ObjectEntrySet().fastIterator();
while (iter.hasNext()) {
- Long2ObjectMap.Entry entry = iter.next();
- long index = entry.getLongKey();
-
- if (count >= this.chunksPerTick) {
+ if (count >= server.chunksPerTick) {
break;
}
+
+ Long2ObjectMap.Entry entry = iter.next();
+ long index = entry.getLongKey();
int chunkX = Level.getHashX(index);
int chunkZ = Level.getHashZ(index);
++count;
- this.usedChunks.put(index, false);
- this.level.registerChunkLoader(this, chunkX, chunkZ, false);
+ try {
+ this.usedChunks.put(index, false);
+ this.level.registerChunkLoader(this, chunkX, chunkZ, false);
- if (!this.level.populateChunk(chunkX, chunkZ)) {
- if (this.spawned && this.teleportPosition == null) {
- continue;
- } else {
- break;
- }
- }
+ if (!this.level.populateChunk(chunkX, chunkZ)) {
+ if (this.spawned && this.teleportPosition == null) {
+ continue;
+ } else {
+ break;
+ }
+ }
- iter.remove();
+ iter.remove();
+ } catch (Exception ex) {
+ server.getLogger().logException(ex);
+ return;
+ }
PlayerChunkRequestEvent ev = new PlayerChunkRequestEvent(this, chunkX, chunkZ);
this.server.getPluginManager().callEvent(ev);
@@ -851,51 +1151,68 @@ protected void sendNextChunk() {
}
}
}
- if (this.chunkLoadCount >= this.spawnThreshold && !this.spawned && this.teleportPosition == null) {
- this.doFirstSpawn();
+
+ if (!this.hasSpawnChunks && this.chunksSent >= server.spawnThreshold) {
+ this.hasSpawnChunks = true;
+
+ this.sendPlayStatus(PlayStatusPacket.PLAYER_SPAWN);
}
- Timings.playerChunkSendTimer.stopTiming();
}
protected void doFirstSpawn() {
- this.spawned = true;
-
- this.inventory.sendContents(this);
- this.inventory.sendArmorContents(this);
- this.offhandInventory.sendContents(this);
- this.setEnableClientCommand(true);
+ if (this.spawned) {
+ return;
+ }
- SetTimePacket setTimePacket = new SetTimePacket();
- setTimePacket.time = this.level.getTime();
- this.dataPacket(setTimePacket);
+ this.noDamageTicks = 60;
+ this.setAirTicks(400);
- Position pos = this.level.getSafeSpawn(this);
+ if (this.hasPermission(Server.BROADCAST_CHANNEL_USERS)) {
+ this.server.getPluginManager().subscribeToPermission(Server.BROADCAST_CHANNEL_USERS, this);
+ }
- PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(this, pos, true);
+ if (this.hasPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE)) {
+ this.server.getPluginManager().subscribeToPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE, this);
+ }
+ boolean dead = this.getHealth() < 1;
+ Position spawn = dead ? this.getSpawn() : this;
+ PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(this, spawn.level.getSafeSpawn(spawn), true);
this.server.getPluginManager().callEvent(respawnEvent);
- pos = respawnEvent.getRespawnPosition();
+ this.spawned = true;
+
+ if (dead) {
+ if (this.server.isHardcore()) {
+ this.setBanned(true);
+ return;
+ }
+
+ this.teleport(respawnEvent.getRespawnPosition(), null);
+
+ // TODO: should probably respawn() here
+ this.setHealth(this.getMaxHealth());
+ this.foodData.setLevel(20, 20);
+ this.sendData(this);
+ } else {
+ this.setPosition(respawnEvent.getRespawnPosition());
+ this.sendPosition(respawnEvent.getRespawnPosition(), yaw, pitch, MovePlayerPacket.MODE_TELEPORT);
+ this.forceMovement = this.teleportPosition = respawnEvent.getRespawnPosition();
- this.sendPlayStatus(PlayStatusPacket.PLAYER_SPAWN);
+ this.getLevel().sendTime(this);
+ this.getLevel().sendWeather(this);
+ }
PlayerJoinEvent playerJoinEvent = new PlayerJoinEvent(this,
- new TranslationContainer(TextFormat.YELLOW + "%multiplayer.player.joined", new String[]{
- this.getDisplayName()
- })
+ new TranslationContainer(TextFormat.YELLOW + "%multiplayer.player.joined", new String[]{this.displayName})
);
this.server.getPluginManager().callEvent(playerJoinEvent);
- if (playerJoinEvent.getJoinMessage().toString().trim().length() > 0) {
+ if (!playerJoinEvent.getJoinMessage().toString().trim().isEmpty()) {
this.server.broadcastMessage(playerJoinEvent.getJoinMessage());
}
- this.noDamageTicks = 60;
-
- this.getServer().sendRecipeList(this);
-
-
for (long index : this.usedChunks.keySet()) {
int chunkX = Level.getHashX(index);
int chunkZ = Level.getHashZ(index);
@@ -906,37 +1223,18 @@ protected void doFirstSpawn() {
}
}
- int experience = this.getExperience();
- if (experience != 0) {
- this.sendExperience(experience);
- }
-
- int level = this.getExperienceLevel();
- if (level != 0) {
- this.sendExperienceLevel(this.getExperienceLevel());
- }
-
- this.teleport(pos, null); // Prevent PlayerTeleportEvent during player spawn
+ // Prevent PlayerTeleportEvent during player spawn
+ //this.teleport(pos, null);
if (!this.isSpectator()) {
this.spawnToAll();
}
- //todo Updater
-
- //Weather
- this.getLevel().sendWeather(this);
-
- //FoodLevel
- PlayerFood food = this.getFoodData();
- if (food.getLevel() != food.getMaxLevel()) {
- food.sendFoodLevel();
- }
-
- if (this.getHealth() < 1) {
- this.respawn();
+ if (!this.locallyInitialized) {
+ // Not really needed anymore but it's here for plugin compatibility
+ this.server.getPluginManager().callEvent(new PlayerLocallyInitializedEvent(this));
+ this.locallyInitialized = true;
}
-
}
protected boolean orderChunks() {
@@ -944,21 +1242,17 @@ protected boolean orderChunks() {
return false;
}
- Timings.playerChunkOrderTimer.startTiming();
-
- this.nextChunkOrderRun = 200;
+ this.nextChunkOrderRun = 20;
loadQueue.clear();
Long2ObjectOpenHashMap 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.spawnThresholdRadius;
int radiusSqr = radius * radius;
-
-
long index;
for (int x = 0; x <= radius; x++) {
int xx = x * x;
@@ -967,43 +1261,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);
@@ -1020,59 +1314,59 @@ 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;
}
+ /**
+ * This method no longer has special function. Calls dataPacket().
+ * @param packet data packet
+ * @return return value of dataPacket()
+ */
@Deprecated
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);
@@ -1080,23 +1374,43 @@ public boolean directDataPacket(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;
@@ -1116,15 +1430,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)) {
@@ -1132,15 +1452,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)));
@@ -1149,7 +1480,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();
@@ -1159,8 +1489,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;
}
@@ -1187,6 +1530,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;
}
@@ -1194,8 +1546,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;
@@ -1205,32 +1555,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;
@@ -1263,18 +1627,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();
@@ -1288,23 +1645,42 @@ public boolean setGamemode(int gamemode, boolean clientSide, AdventureSettings n
return true;
}
+ /**
+ * Send adventure settings (adventureSettings.update())
+ */
@Deprecated
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;
}
@@ -1312,45 +1688,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
@@ -1376,7 +1723,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;
@@ -1395,189 +1742,383 @@ 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.getLevel().getProvider() == null) {
+ break;
+ }
+
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 (inEndPortalTicks == 1 && EnumLevel.THE_END.getLevel() != null) {
+ 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;
+
+ this.lastYaw = to.yaw;
+ this.lastPitch = to.pitch;
+ }
+
+ this.firstMove = false;
- PlayerMoveEvent event = new PlayerMoveEvent(this, source, target);
- this.blocksAround = null;
- this.collisionBlocks = null;
- this.server.getPluginManager().callEvent(event);
+ 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 (!(invalidMotion = event.isCancelled())) {
- if (!target.equals(event.getTo())) {
- this.teleport(event.getTo(), null);
+ if (this.riding == null && this.inventory != null) {
+ 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.addMovement(this.x, this.y, this.z, this.yaw, this.pitch, this.yaw);
+ if (this.inAirTicks == 3 && swimming == 0) {
+ jump = 0.05;
+ }
+ this.foodData.updateFoodExpLevel(jump + swimming);
}
- } else {
- this.blocksAround = blocksAround;
- this.collisionBlocks = collidingBlocks;
}
}
- 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);
+ 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));
+ }
+ }
+ }
+ }
}
- } 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 && this.riding == null) {
- 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;
- }
- 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;
}
@@ -1588,11 +2129,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 {
@@ -1600,31 +2140,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);
}
@@ -1634,15 +2168,41 @@ public boolean setMotion(Vector3 motion) {
return false;
}
+ /**
+ * 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(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()))
+ 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);
}
@@ -1659,21 +2219,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;
}
@@ -1682,96 +2244,162 @@ 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) {
+
+ // 1.20.10 doesn't stop it automatically
if (this.isGliding()) {
this.setGliding(false);
}
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 && !(this.speed.x == 0 && this.speed.y == 0 && this.speed.z == 0)) {
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.fireworkBoostTicks > 0) {
+ this.fireworkBoostTicks -= tickDiff;
+
+ double multiplier = 1 + 0.25 * (this.fireworkBoostLevel < 1 ? 0.25 : this.fireworkBoostLevel);
+ this.setMotion(new Vector3(
+ -Math.sin(Math.toRadians(this.yaw)) * Math.cos(Math.toRadians(this.pitch)) * multiplier,
+ -Math.sin(Math.toRadians(this.pitch)) * multiplier,
+ Math.cos(Math.toRadians(this.yaw)) * Math.cos(Math.toRadians(this.pitch)) * multiplier));
+ }
+
+ 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 = "";
}
@@ -1791,8 +2419,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
@@ -1812,17 +2438,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
@@ -1833,6 +2455,9 @@ private EntityInteractable getEntityAtPosition(Entity[] nearbyEntities, int x, i
return null;
}
+ /**
+ * Internal: Process chunk sending
+ */
public void checkNetwork() {
if (!this.isOnline()) {
return;
@@ -1847,68 +2472,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(null, 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) {
@@ -1916,47 +2595,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());
+ Vector3 sp = this.level.getProvider().getSpawn();
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;
@@ -1978,21 +2670,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;
@@ -2001,11 +2692,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();
@@ -2013,6 +2699,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()) {
@@ -2020,15 +2711,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;
@@ -2038,174 +2724,232 @@ 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);
+ if (this.getLevel().isRaining()) {
+ startGamePacket.rainLevel = this.getLevel().getRainTime();
+ if (this.getLevel().isThundering()) {
+ startGamePacket.lightningLevel = this.getLevel().getThunderTime();
+ }
+ }
- this.dataPacket(new BiomeDefinitionListPacket());
- this.dataPacket(new AvailableEntityIdentifiersPacket());
- this.inventory.sendCreativeContents();
- this.getAdventureSettings().update();
+ this.forceDataPacket(startGamePacket, null);
- this.sendAttributes();
+ this.loggedIn = true;
- this.sendPotionEffects(this);
+ this.server.getLogger().info(this.getServer().getLanguage().translateString("nukkit.player.logIn",
+ TextFormat.AQUA + this.username + TextFormat.WHITE,
+ this.getAddress(),
+ String.valueOf(this.getPort()),
+ String.valueOf(this.id),
+ this.level.getName(),
+ 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);
- this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, false);
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_SILENT, true, false);
+ this.setDataFlag(DATA_FLAGS, DATA_FLAG_HAS_COLLISION, false, false);
}
- this.sendData(this);
- this.loggedIn = true;
+ if (CustomItemManager.get().hasCustomItems()) {
+ this.dataPacket(CustomItemManager.get().getCachedPacket());
+ }
+ this.dataPacket(BiomeDefinitionListPacket.getCachedPacket());
+ this.dataPacket(EntityManager.get().getCachedPacket());
+
+ 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.level.sendTime(this);
+ this.adventureSettings.update();
+
+ GameRulesChangedPacket gameRulesPK = new GameRulesChangedPacket();
+ gameRulesPK.gameRulesMap = level.getGameRules().getGameRules();
+ this.dataPacket(gameRulesPK);
+ this.server.sendFullPlayerListData(this);
this.sendAttributes();
- this.setNameTagVisible(true);
- this.setNameTagAlwaysVisible(true);
- this.setCanClimb(true);
- this.server.getLogger().info(this.getServer().getLanguage().translateString("nukkit.player.logIn",
- TextFormat.AQUA + this.username + TextFormat.WHITE,
- this.getAddress(),
- 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))));
+ 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:
+ this.networkSettingsRequested = true;
- int protocolVersion = ((RequestNetworkSettingsPacket) packet).protocolVersion;
+ if (this.getNetworkSession().getCompression() != CompressionProvider.NONE) {
+ this.getServer().getLogger().debug((this.username == null ? this.unverifiedUsername : this.username) +
+ ": got a RequestNetworkSettingsPacket but compression is already set");
+ return;
+ }
- 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;
- }
+ RequestNetworkSettingsPacket networkSettingsRequest = (RequestNetworkSettingsPacket) packet;
- 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;
+ 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;
+ }
- this.loginPacketReceived = true;
+ 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;
+ }
- LoginPacket loginPacket = (LoginPacket) packet;
+ this.loginPacketReceived = true;
- this.unverifiedUsername = TextFormat.clean(loginPacket.username);
+ LoginPacket loginPacket = (LoginPacket) packet;
+ this.unverifiedUsername = TextFormat.clean(loginPacket.username);
- if (loginPacket.skin == null) {
- this.close("", "disconnectionScreen.invalidSkin");
- return;
- }
+ if (!this.networkSettingsRequested) {
+ this.close("", "Invalid login sequence: login packet before network settings");
+ return;
+ }
- if (this.server.getOnlinePlayers().size() >= this.server.getMaxPlayers() && this.kick(PlayerKickEvent.Reason.SERVER_FULL, "disconnectionScreen.serverFull", false)) {
- return;
+ 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;
+ }
- // 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);
+ 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;
}
@@ -2213,923 +2957,1127 @@ public void handleDataPacket(DataPacket packet) {
valid = false;
break;
}
+ }
- if (!valid || Objects.equals(this.iusername, "rcon") || Objects.equals(this.iusername, "console")) {
- this.close("", "disconnectionScreen.invalidName");
-
- break;
- }
-
- 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());
+ 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 (server.encryptionEnabled) {
- this.getServer().getScheduler().scheduleAsyncTask(new PrepareEncryptionTask(this) {
+ PlayerPreLoginEvent playerPreLoginEvent;
+ this.server.getPluginManager().callEvent(playerPreLoginEvent = new PlayerPreLoginEvent(this, "Plugin reason"));
+ if (playerPreLoginEvent.isCancelled()) {
+ this.close("", playerPreLoginEvent.getKickMessage());
+ return;
+ }
- @Override
- public void onCompletion(Server server) {
- if (!Player.this.connected) {
- return;
- }
+ if (server.encryptionEnabled) {
+ this.getServer().getScheduler().scheduleAsyncTask(null, new PrepareEncryptionTask(this) {
- if (this.getHandshakeJwt() == null || this.getEncryptionKey() == null || this.getEncryptionCipher() == null || this.getDecryptionCipher() == null) {
- Player.this.close("Failed to enable encryption");
- return;
- }
+ @Override
+ public void onCompletion(Server server) {
+ if (!Player.this.connected) {
+ 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();
+ 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 (dis > 100) {
- this.sendPosition(this, movePlayerPacket.yaw, movePlayerPacket.pitch, MovePlayerPacket.MODE_RESET);
- 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;
+ }
- boolean revert = false;
- if (!this.isAlive() || !this.spawned) {
- revert = true;
- this.forceMovement = new Vector3(this.x, this.y, this.z);
- }
+ 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;
- if (this.forceMovement != null && (newPos.distanceSquared(this.forceMovement) > 0.1 || revert)) {
- this.sendPosition(this.forceMovement, movePlayerPacket.yaw, movePlayerPacket.pitch, MovePlayerPacket.MODE_RESET);
- } else {
+ if (!skin.isValid()) {
+ this.close("", "disconnectionScreen.invalidSkin");
+ return;
+ }
- movePlayerPacket.yaw %= 360;
- movePlayerPacket.pitch %= 360;
+ 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;
+ }
- if (movePlayerPacket.yaw < 0) {
- movePlayerPacket.yaw += 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);
}
- this.setRotation(movePlayerPacket.yaw, movePlayerPacket.pitch);
- this.newPosition = newPos;
- this.forceMovement = null;
- }
- 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);
- }
+ 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);
+ }
- 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;
+ 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;
}
+ }
- if (this.teleportPosition != null) {
- return;
- }
+ if (this.teleportPosition != null) {
+ return;
+ }
- 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());
- }
- }
+ 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.isSpectator() && authPacket.getInputData().contains(AuthInputAction.MISSED_SWING)) {
- level.addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_ATTACK_NODAMAGE, -1, "minecraft:player", false, false);
+ 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 (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);
+ } 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) < 16) {
+ ((EntityBoat) this.riding).onInput(authPacket.getPosition().getX(), authPacket.getPosition().getY(), authPacket.getPosition().getZ(), authPacket.getHeadYaw());
}
}
+ }
- 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);
- }
- }
+ 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_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.START_SPRINTING)) {
+ PlayerToggleSprintEvent playerToggleSprintEvent = new PlayerToggleSprintEvent(this, true);
+ if ((this.foodData.getLevel() <= 6 && !this.getAdventureSettings().get(Type.FLYING)) ||
+ this.riding != null || this.sleeping != null || this.hasEffect(Effect.BLINDNESS) ||
+ (this.isSneaking() && !authPacket.getInputData().contains(AuthInputAction.STOP_SNEAKING))) {
+ playerToggleSprintEvent.setCancelled(true);
}
-
- 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);
- }
+ 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_JUMPING)) {
- PlayerJumpEvent playerJumpEvent = new PlayerJumpEvent(this);
- this.server.getPluginManager().callEvent(playerJumpEvent);
+ 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.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_SNEAKING)) {
+ PlayerToggleSneakEvent playerToggleSneakEvent = new PlayerToggleSneakEvent(this, true);
+ if (this.riding != null || this.sleeping != null) {
+ playerToggleSneakEvent.setCancelled(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);
- }
+ this.server.getPluginManager().callEvent(playerToggleSneakEvent);
+ if (playerToggleSneakEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSneaking(true);
}
+ }
- 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.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.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_CRAWLING)) {
+ PlayerToggleCrawlEvent playerToggleCrawlEvent = new PlayerToggleCrawlEvent(this, true);
+ if (this.riding != null || this.sleeping != null) {
+ playerToggleCrawlEvent.setCancelled(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(false);
- } else {
- this.getAdventureSettings().set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
- }
+ this.server.getPluginManager().callEvent(playerToggleCrawlEvent);
+ if (playerToggleCrawlEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setCrawling(true);
}
+ }
- 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(false);
- } else {
- this.getAdventureSettings().set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
- }
+ 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);
}
+ }
- Vector3 clientPosition = authPacket.getPosition().subtract(0, this.riding == null ? this.getBaseOffset() : this.riding.getMountedOffset(this).getY(), 0).asVector3();
+ if (authPacket.getInputData().contains(AuthInputAction.START_JUMPING)) {
+ this.server.getPluginManager().callEvent(new PlayerJumpEvent(this));
+ }
- 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.START_GLIDING)) {
+ boolean withoutElytra = false;
+ Item chestplate = this.getInventory().getChestplateFast();
+ if (chestplate == null || chestplate.getId() != ItemID.ELYTRA || chestplate.getDamage() >= chestplate.getMaxDurability()) {
+ withoutElytra = true;
+ }
+ if (withoutElytra && !server.getAllowFlight()) {
+ this.kick(PlayerKickEvent.Reason.FLYING_DISABLED, MSG_FLYING_NOT_ENABLED, true);
+ return;
+ }
+ PlayerToggleGlideEvent playerToggleGlideEvent = new PlayerToggleGlideEvent(this, true);
+ if (this.riding != null || this.sleeping != null || withoutElytra) {
+ playerToggleGlideEvent.setCancelled(true);
}
+ this.server.getPluginManager().callEvent(playerToggleGlideEvent);
+ if (playerToggleGlideEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setGliding(true);
+ }
+ }
- if (distSqrt > 100) {
- this.sendPosition(this, authPacket.getYaw(), authPacket.getPitch(), MovePlayerPacket.MODE_RESET);
- 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);
}
+ }
- 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.START_SWIMMING)) {
+ PlayerToggleSwimEvent ptse = new PlayerToggleSwimEvent(this, true);
+ if (this.riding != null || this.sleeping != null || !this.isInsideOfWater()) {
+ ptse.setCancelled(true);
+ }
+ this.server.getPluginManager().callEvent(ptse);
+ if (ptse.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSwimming(true);
}
+ this.setUsingItem(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.STOP_SWIMMING)) {
+ PlayerToggleSwimEvent ptse = new PlayerToggleSwimEvent(this, false);
+ this.server.getPluginManager().callEvent(ptse);
+ if (ptse.isCancelled()) {
+ this.needSendData = true;
} else {
- float yaw = authPacket.getYaw() % 360;
- float pitch = authPacket.getPitch() % 360;
- if (yaw < 0) {
- yaw += 360;
- }
+ this.setSwimming(false);
+ }
+ }
- this.setRotation(yaw, pitch);
- this.newPosition = clientPosition;
- this.clientMovements.offer(clientPosition);
- this.forceMovement = null;
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_SPIN_ATTACK)) {
+ PlayerToggleSpinAttackEvent playerToggleSpinAttackEvent = new PlayerToggleSpinAttackEvent(this, false);
+ this.server.getPluginManager().callEvent(playerToggleSpinAttackEvent);
+ if (playerToggleSpinAttackEvent.isCancelled()) {
+ this.needSendData = true;
+ } else {
+ this.setSpinAttack(false);
}
- break;
- case ProtocolInfo.MOB_EQUIPMENT_PACKET:
- if (!this.spawned || !this.isAlive()) {
+ }
+
+ 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 {
+ this.adventureSettings.set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
+ }
+ }
- MobEquipmentPacket mobEquipmentPacket = (MobEquipmentPacket) packet;
+ if (authPacket.getInputData().contains(AuthInputAction.STOP_FLYING)) {
+ PlayerToggleFlightEvent playerToggleFlightEvent = new PlayerToggleFlightEvent(this, false);
+ if (this.isSpectator()) {
+ playerToggleFlightEvent.setCancelled(true);
+ }
+ server.getPluginManager().callEvent(playerToggleFlightEvent);
+ if (playerToggleFlightEvent.isCancelled()) {
+ this.needSendAdventureSettings = true;
+ } else {
+ this.adventureSettings.set(AdventureSettings.Type.FLYING, playerToggleFlightEvent.isFlying());
+ }
+ }
- Inventory inv = this.getWindowById(mobEquipmentPacket.windowId);
+ if (this.adventureSettings.get(AdventureSettings.Type.FLYING)) {
+ this.flySneaking = authPacket.getInputData().contains(AuthInputAction.SNEAKING);
+ }
- if (inv == null) {
- this.server.getLogger().debug(this.getName() + " has no open container with window ID " + mobEquipmentPacket.windowId);
- return;
- }
+ Vector3 clientPosition = authPacket.getPosition().subtract(0, this.riding == null ? this.getBaseOffset() : this.riding.getMountedOffset(this).getY(), 0).asVector3();
- Item item = inv.getItem(mobEquipmentPacket.hotbarSlot);
+ 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 (!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;
- }
+ boolean revertMotion = false;
+ if (!this.isAlive() || !this.spawned) {
+ revertMotion = true;
+ this.forceMovement = this;
+ }
- if (inv instanceof PlayerInventory) {
- ((PlayerInventory) inv).equipItem(mobEquipmentPacket.hotbarSlot);
+ 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;
}
- this.setDataFlag(Player.DATA_FLAGS, Player.DATA_FLAG_ACTION, false);
+ 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;
+ }
- break;
- case ProtocolInfo.PLAYER_ACTION_PACKET:
- PlayerActionPacket playerActionPacket = (PlayerActionPacket) packet;
- if (!this.spawned || !this.isAlive() && playerActionPacket.action != PlayerActionPacket.ACTION_RESPAWN) {
- break;
- }
+ MobEquipmentPacket mobEquipmentPacket = (MobEquipmentPacket) packet;
- 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 (mobEquipmentPacket.windowId != ContainerIds.INVENTORY) {
+ 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 (this.inventory == null) {
+ return;
+ }
- ModalFormResponsePacket modalFormPacket = (ModalFormResponsePacket) packet;
+ this.inventory.equipItem(mobEquipmentPacket.hotbarSlot);
+ 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;
+ }
- 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.setUsingItem(false);
+ return;
+ case ProtocolInfo.MODAL_FORM_RESPONSE_PACKET:
+ this.formOpen = false;
- for (FormResponseHandler handler : window.getHandlers()) {
- handler.handle(this, modalFormPacket.formId);
- }
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
+
+ ModalFormResponsePacket modalFormPacket = (ModalFormResponsePacket) packet;
- PlayerSettingsRespondedEvent event = new PlayerSettingsRespondedEvent(this, modalFormPacket.formId, window);
- getServer().getPluginManager().callEvent(event);
+ if (formWindows.containsKey(modalFormPacket.formId)) {
+ FormWindow window = formWindows.remove(modalFormPacket.formId);
+ window.setResponse(modalFormPacket.data.trim());
- //Set back new settings if not been cancelled
- if (!event.isCancelled() && window instanceof FormWindowCustom)
- ((FormWindowCustom) window).setElementsFromResponse();
+ for (FormResponseHandler handler : window.getHandlers()) {
+ handler.handle(this, modalFormPacket.formId);
}
- break;
+ 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());
- case ProtocolInfo.INTERACT_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
+ for (FormResponseHandler handler : window.getHandlers()) {
+ handler.handle(this, modalFormPacket.formId);
}
- InteractPacket interactPacket = (InteractPacket) packet;
+ PlayerSettingsRespondedEvent event = new PlayerSettingsRespondedEvent(this, modalFormPacket.formId, window);
+ getServer().getPluginManager().callEvent(event);
- if (interactPacket.target == 0 && interactPacket.action == InteractPacket.ACTION_MOUSEOVER) {
- this.setButtonText("");
- break;
- }
+ if (!event.isCancelled() && window instanceof FormWindowCustom)
+ ((FormWindowCustom) window).setElementsFromResponse();
+ }
- Entity targetEntity = interactPacket.target == this.getId() ? this : this.level.getEntity(interactPacket.target);
+ return;
+ case ProtocolInfo.INTERACT_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- if (targetEntity == null || !this.isAlive() || !targetEntity.isAlive()) {
- break;
- }
+ InteractPacket interactPacket = (InteractPacket) packet;
- 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;
- }
+ if (interactPacket.target == 0 && interactPacket.action == InteractPacket.ACTION_MOUSEOVER) {
+ this.setButtonText("");
+ return;
+ }
- 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);
+ Entity targetEntity = interactPacket.target == this.getId() ? this : this.level.getEntity(interactPacket.target);
- this.getServer().getPluginManager().callEvent(new PlayerMouseOverEntityEvent(this, targetEntity));
- break;
- case InteractPacket.ACTION_VEHICLE_EXIT:
- if (!(targetEntity instanceof EntityRideable) || this.riding != targetEntity) {
- break;
- }
+ if (targetEntity == null || !this.isAlive() || !targetEntity.isAlive()) {
+ if (targetEntity != null || interactPacket.action != InteractPacket.ACTION_OPEN_INVENTORY) {
+ return;
+ }
+ }
- ((EntityRideable) riding).dismountEntity(this);
- break;
- case InteractPacket.ACTION_OPEN_INVENTORY:
- if (targetEntity != this) {
- break;
- }
- if (!this.inventoryOpen && this.inventory.open(this)) {
+ 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() || this.inventory == null || this.inventoryOpen) {
+ 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 && this.isCreative()) {
+ 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);
-
- 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;
+ PlayerBlockPickEvent pickEvent = new PlayerBlockPickEvent(this, block, item);
+ if (this.isSpectator()) {
+ pickEvent.setCancelled();
+ }
+
+ 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;
- }
+ if (!itemExists && !this.isCreative()) {
+ return;
+ }
+
+ 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 != AnimatePacket.Action.SWING_ARM &&
+ !(this.riding != null && (animatePacket.action == AnimatePacket.Action.ROW_LEFT || animatePacket.action == AnimatePacket.Action.ROW_RIGHT))) {
+ 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();
- TextPacket textPacket = (TextPacket) packet;
+ CommandRequestPacket commandRequestPacket = (CommandRequestPacket) packet;
+ PlayerCommandPreprocessEvent playerCommandPreprocessEvent = new PlayerCommandPreprocessEvent(this, commandRequestPacket.command + ' ');
+ this.server.getPluginManager().callEvent(playerCommandPreprocessEvent);
+ if (playerCommandPreprocessEvent.isCancelled()) {
+ 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;
- }
+ this.server.dispatchCommand(playerCommandPreprocessEvent.getPlayer(), playerCommandPreprocessEvent.getMessage().substring(1));
+ return;
+ case ProtocolInfo.TEXT_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
+
+ 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 (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);
}
- 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);
- } else { // Close bugged inventory
- ContainerClosePacket pk = new ContainerClosePacket();
- pk.windowId = containerClosePacket.windowId;
- pk.wasServerInitiated = false;
- this.dataPacket(pk);
+ this.chat(chatMessage);
+ }
+ return;
+ case ProtocolInfo.CONTAINER_CLOSE_PACKET:
+ ContainerClosePacket containerClosePacket = (ContainerClosePacket) packet;
+ if (!this.spawned || (containerClosePacket.windowId == ContainerIds.INVENTORY && !inventoryOpen)) {
+ return;
+ }
- for (Inventory open : new ArrayList<>(this.windows.keySet())) {
- if (open instanceof ContainerInventory) {
- this.removeWindow(open);
- }
+ 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);
+ } else { // TODO: check this
+ ContainerClosePacket pk = new ContainerClosePacket();
+ pk.windowId = containerClosePacket.windowId;
+ pk.wasServerInitiated = false;
+ this.dataPacket(pk);
+
+ for (Inventory open : new ArrayList<>(this.windows.keySet())) {
+ if (open instanceof ContainerInventory) {
+ this.removeWindow(open);
}
}
- break;
- case ProtocolInfo.CRAFTING_EVENT_PACKET:
- break;
- case ProtocolInfo.BLOCK_ENTITY_DATA_PACKET:
- if (!this.spawned || !this.isAlive()) {
- break;
- }
+ }
+ return;
+ case ProtocolInfo.BLOCK_ENTITY_DATA_PACKET:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- BlockEntityDataPacket blockEntityDataPacket = (BlockEntityDataPacket) packet;
- this.craftingType = CRAFTING_SMALL;
- this.resetCraftingGridType();
+ BlockEntityDataPacket blockEntityDataPacket = (BlockEntityDataPacket) packet;
+ this.resetCraftingGridType();
- Vector3 pos = new Vector3(blockEntityDataPacket.x, blockEntityDataPacket.y, blockEntityDataPacket.z);
- if (pos.distanceSquared(this) > 10000) {
- break;
+ Vector3 pos = this.temporalVector.setComponents(blockEntityDataPacket.x, blockEntityDataPacket.y, blockEntityDataPacket.z);
+ if (pos.distanceSquared(this) > 2500) {
+ 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(false);
- 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.MAP_INFO_REQUEST_PACKET:
- MapInfoRequestPacket pk = (MapInfoRequestPacket) packet;
- Item mapItem = null;
+ this.setGamemode(setPlayerGameTypePacket.gamemode, true);
+ Command.broadcastCommandMessage(this, new TranslationContainer("commands.gamemode.success.self", Server.getGamemodeString(this.gamemode)));
+ }
+ return;
+ case ProtocolInfo.MAP_INFO_REQUEST_PACKET:
+ if (this.inventory == null) {
+ return;
+ }
- for (Item item1 : this.offhandInventory.getContents().values()) {
+ 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;
+ }
+ }
+
+ 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 (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 (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 (mapItem == null) {
- for (BlockEntity be : this.level.getBlockEntities().values()) {
- if (be instanceof BlockEntityItemFrame) {
- BlockEntityItemFrame itemFrame1 = (BlockEntityItemFrame) be;
+ 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 (itemFrame1.getItem() instanceof ItemMap && ((ItemMap) itemFrame1.getItem()).getMapId() == pk.mapId) {
- ((ItemMap) itemFrame1.getItem()).sendImage(this);
- break;
+ 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));
+ }
+
+ 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;
+
+ Inventory inv;
+ 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) {
+
+ 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;
+
+ 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);
+
+ for (Item drop : drops) {
+ this.level.dropItem(this, drop);
+ }
- if (!event.isCancelled()) {
- ((ItemMap) mapItem).sendImage(this);
+ 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;
+ }
}
- }
- 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;
+ 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;
}
+ }
- InventoryTransactionPacket transactionPacket = (InventoryTransactionPacket) packet;
-
- 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.getName() + ": " + networkInventoryAction);
- this.sendAllInventories();
- break packetswitch;
- }
+ List actions = new ArrayList<>();
+ for (NetworkInventoryAction networkInventoryAction : transactionPacket.actions) {
+ InventoryAction a = networkInventoryAction.createInventoryAction(this);
- actions.add(a);
+ 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 {
@@ -3141,493 +4089,729 @@ 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() ? 14 : 8)) {
- 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());
- }
- break packetswitch;
- }
- }
- }
+ lastRightClickPos = blockVector.asVector3();
+ lastRightClickTime = System.currentTimeMillis();
- inventory.sendHeldItem(this);
+ this.breakingBlock = null;
- if (blockVector.distanceSquared(this) > 10000) {
- break packetswitch;
- }
+ this.setUsingItem(false);
- Block target = this.level.getBlock(blockVector.asVector3());
- block = target.getSide(face);
+ // 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() ? 14 : 8)) {
+ 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
- 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;
- }
-
- System.out.println("USE_ITEM_ACTION_BREAK_BLOCK");
-
- this.resetCraftingGridType();
-
- Item i = this.getInventory().getItemInHand();
-
- Item oldItem = i.clone();
-
- if (this.canInteract(blockVector.add(0.5, 0.5, 0.5), this.isCreative() ? 14 : 8) && (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 ((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);
- } else {
+
+ 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");
}
- inventory.sendHeldItem(this.getViewers().values());
}
+
+ if (!itemSent && !oldItem.equals(useItemData.itemInHand)) {
+ this.needSendHeldItem = true;
+ }
+ return;
}
- break packetswitch;
}
+ }
+
+ this.needSendHeldItem = true;
+
+ if (blockVector.distanceSquared(this) > 10000) {
+ return;
+ }
+
+ Block target = this.level.getBlock(blockVector.asVector3());
+ block = target.getSide(face);
+
+ this.level.sendBlocks(this, new Block[]{target, block}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
+
+ if (target instanceof BlockDoor) {
+ BlockDoor door = (BlockDoor) target;
- inventory.sendContents(this);
- inventory.sendHeldItem(this);
+ Block part;
- 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 ((door.getDamage() & 0x08) > 0) {
+ part = target.down();
- BlockEntity blockEntity = this.level.getBlockEntity(blockVector.asVector3());
- if (blockEntity instanceof BlockEntitySpawnable) {
- ((BlockEntitySpawnable) blockEntity).spawnTo(this);
+ if (part.getId() == target.getId()) {
+ target = part;
+
+ this.level.sendBlocks(this, new Block[]{target}, UpdateBlockPacket.FLAG_ALL_PRIORITY);
}
}
+ }
+ return;
+ case InventoryTransactionPacket.USE_ITEM_ACTION_BREAK_BLOCK:
+ return;
+ case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_AIR:
+ if (!this.spawned || !this.isAlive()) {
+ return;
+ }
- break packetswitch;
- case InventoryTransactionPacket.USE_ITEM_ACTION_CLICK_AIR:
- Vector3 directionVector = this.getDirectionVector();
+ if (inventory.getHeldItemIndex() != useItemData.hotbarSlot) {
+ this.inventory.equipItem(useItemData.hotbarSlot);
- 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.crossbowLoadTick = 0;
+ }
+
+ 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);
+
+ if (this.sleeping != null) {
+ this.getServer().getLogger().debug(username + ": USE_ITEM_ON_ENTITY_ACTION_ATTACK while sleeping");
+ return;
+ }
+
+ if (this.inventoryOpen) {
+ this.getServer().getLogger().debug(username + ": USE_ITEM_ON_ENTITY_ACTION_ATTACK while viewing inventory");
+ return;
+ }
- 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();
+ this.setShieldBlockingDelay(5);
+
+ if (server.attackStopSprint) {
+ this.setSprinting(false);
+ }
+
+ Enchantment[] enchantments = item.getEnchantments();
+
+ 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;
+ }
+
+ for (Enchantment enchantment : item.getEnchantments()) {
+ enchantment.doPostAttack(this, target);
+ }
- if (!target.attack(entityDamageByEntityEvent)) {
- if (item.isTool() && this.isSurvival()) {
- this.inventory.sendContents(this);
+ 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.spawned || !this.isAlive()) {
+ 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);
- }
+ if (this.inventory == null) {
+ 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;
+ 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;
+ }
- if (hotbarPacket.windowId != ContainerIds.INVENTORY) {
- return; //In PE this should never happen
- }
+ 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;
+ }
- 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()) {
+ if (this.inventory == null) {
+ return;
+ }
+
+ 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());
}
- 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.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;
+ }
+ EmotePacket cleanEmotePacket = new EmotePacket();
+ cleanEmotePacket.runtimeId = emotePacket.runtimeId;
+ cleanEmotePacket.emoteID = emotePacket.emoteID;
+ Server.broadcastPacket(this.getViewers().values(), cleanEmotePacket);
+ 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) {
+ 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);
+ }
}
- 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())));
- if (bookEditPacket.text != null && bookEditPacket.text.length() > 256) {
- this.getServer().getLogger().debug(username + ": BookEditPacket with too long text");
- return;
+ 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 (!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;
+ }
+ }
+
+ 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 (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;
- }
+ 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);
+
+ Item handItem = this.inventory.getItemInHand();
+ Block target = this.level.getBlock(chunk, blockPos.x, blockPos.y, blockPos.z, false);
+ PlayerInteractEvent playerInteractEvent = new PlayerInteractEvent(this, handItem, 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();
@@ -3635,90 +4819,74 @@ private void onBlockBreakStart(Vector3 pos, BlockFace face) {
}
break;
case Block.ITEM_FRAME_BLOCK:
- BlockEntity itemFrame = this.level.getBlockEntity(pos);
+ case Block.GLOW_FRAME:
+ 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:
+ BlockEntity lectern = this.level.getBlockEntityIfLoaded(this.chunk, this.temporalVector.setComponents(blockPos.x, blockPos.y, blockPos.z));
+ if (lectern instanceof BlockEntityLectern && ((BlockEntityLectern) lectern).dropBook(this)) {
+ return;
+ }
+ break;
}
- Block block = target.getSide(face);
- if (block.getId() == Block.FIRE) {
+ int bid = this.level.getBlockIdAt(this.chunk, 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(handItem, 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() ? 14 : 8);
- 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() ? 14 : 8) && (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);
}
@@ -3726,29 +4894,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());
}
}
}
@@ -3780,6 +4970,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()));
@@ -3787,7 +4984,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;
}
@@ -3807,7 +5004,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();
@@ -3816,15 +5018,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);
}
@@ -3834,7 +5058,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) {
@@ -3843,17 +5067,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);
}
@@ -3871,16 +5094,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;
@@ -3888,6 +5111,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;
@@ -3919,7 +5145,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;
@@ -3940,7 +5165,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);
}
@@ -3958,6 +5183,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;
@@ -3990,27 +5220,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 : this.server.getOnlinePlayers().values()) {
if (!player.canSee(this)) {
player.showPlayer(this);
}
@@ -4018,31 +5260,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());
}
@@ -4050,15 +5279,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.unverifiedUsername : 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);
@@ -4072,33 +5299,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();
@@ -4113,11 +5357,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);
@@ -4127,6 +5371,10 @@ public void save(boolean async) {
}
}
+ /**
+ * Get player's username
+ * @return username
+ */
public String getName() {
return this.username;
}
@@ -4143,24 +5391,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:
@@ -4169,14 +5420,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:
@@ -4195,14 +5448,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;
@@ -4222,7 +5474,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;
@@ -4233,11 +5491,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";
}
@@ -4260,10 +5521,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()) {
@@ -4272,9 +5534,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)) {
@@ -4285,9 +5548,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)) {
@@ -4299,28 +5562,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);
}
}
@@ -4330,7 +5594,6 @@ protected void respawn() {
return;
}
- this.craftingType = CRAFTING_SMALL;
this.resetCraftingGridType();
PlayerRespawnEvent playerRespawnEvent = new PlayerRespawnEvent(this, this.getSpawn());
@@ -4338,34 +5601,59 @@ 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.setSwimming(false);
this.setGliding(false);
+ this.setCrawling(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(EntityPotionEffectEvent.Cause.DEATH);
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
@@ -4375,11 +5663,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);
}
@@ -4389,53 +5678,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);
@@ -4447,32 +5759,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};
@@ -4485,50 +5826,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();
@@ -4554,15 +5904,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;
}
@@ -4578,15 +5930,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) {
@@ -4605,49 +5960,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.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;
}
- if (targets != null) {
- Server.broadcastPacket(targets, pk);
- } else {
- this.clientMovements.clear();
+ this.ySize = 0;
- this.dataPacket(pk);
- }
+ 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);
}
}
@@ -4665,9 +6061,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) {
@@ -4679,7 +6079,9 @@ protected boolean checkTeleportPosition() {
}
this.spawnToAll();
- this.forceMovement = this.teleportPosition;
+ if (!enderPearl) {
+ this.forceMovement = this.teleportPosition;
+ }
this.teleportPosition = null;
return true;
}
@@ -4688,13 +6090,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);
}
@@ -4704,90 +6101,59 @@ 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);
- }
- }
- }
-
+ /**
+ * deprecated: use teleport() with null cause instead
+ */
+ @Deprecated
public void teleportImmediate(Location location) {
this.teleportImmediate(location, TeleportCause.PLUGIN);
}
+ /**
+ * deprecated: use teleport() with null cause instead
+ */
+ @Deprecated
public void teleportImmediate(Location location, TeleportCause cause) {
- Location from = this.getLocation();
- if (super.teleport(location, cause)) {
- this.removeAllWindows();
-
- 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.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.teleport(location, null);
}
/**
@@ -4810,12 +6176,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;
}
@@ -4842,8 +6209,7 @@ public int addServerSettings(FormWindow window) {
*/
@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());
}
/**
@@ -4909,6 +6275,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);
@@ -4917,6 +6288,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);
}
@@ -4977,13 +6353,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);
@@ -5003,72 +6380,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 && !(inventory instanceof ContainerInventory)) {
+ 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;
}
@@ -5098,24 +6523,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
@@ -5125,131 +6548,205 @@ 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;
- @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();
+ 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);
- SetTimePacket setTime = new SetTimePacket();
- setTime.time = level.getTime();
- this.dataPacket(setTime);
+ NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket();
+ chunkPublisherUpdatePacket.position = this.asBlockVector3();
+ chunkPublisherUpdatePacket.radius = this.chunkRadius << 4;
+ this.dataPacket(chunkPublisherUpdatePacket);
- GameRulesChangedPacket gameRulesChanged = new GameRulesChangedPacket();
- gameRulesChanged.gameRules = level.getGameRules();
- this.dataPacket(gameRulesChanged);
- return true;
- }
+ this.dimensionFix560 = true;
+ }
- return false;
+ @Override
+ protected void preSwitchLevel() {
+ this.unloadChunks(true);
+ }
+
+ @Override
+ protected void afterSwitchLevel() {
+ // Send spawn to update compass position
+ SetSpawnPositionPacket spawnPosition = new SetSpawnPositionPacket();
+ spawnPosition.spawnType = SetSpawnPositionPacket.TYPE_WORLD_SPAWN;
+ Vector3 spawn = level.getProvider().getSpawn();
+ 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);
}
+ /**
+ * 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;
@@ -5257,19 +6754,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;
}
@@ -5281,12 +6784,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 (((EntityProjectile) entity).shootingEntity != null && item.hasEnchantment(Enchantment.ID_TRIDENT_LOYALTY) && !this.equals(((EntityProjectile) entity).shootingEntity)) {
+ return false;
+ }
+
if (!this.isCreative() && !this.inventory.canAddItem(item)) {
return false;
}
@@ -5294,11 +6816,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;
}
@@ -5310,13 +6837,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) {
+ this.inventory.setItem(favSlot, item.clone());
+ } else {
+ this.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)) {
@@ -5324,7 +6858,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;
}
@@ -5335,7 +6869,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;
}
@@ -5345,7 +6888,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;
}
@@ -5353,37 +6896,49 @@ public boolean pickupEntity(Entity entity, boolean near) {
}
}
- int tick = this.getServer().getTick();
- if (pickedXPOrb < tick && entity instanceof EntityXPOrb && this.boundingBox.isVectorInside(entity)) {
+ if (this.pickedXPOrb < this.server.getTick() && entity instanceof EntityXPOrb) {
EntityXPOrb xpOrb = (EntityXPOrb) entity;
- if (xpOrb.getPickupDelay() <= 0) {
+ if (xpOrb.getPickupDelay() <= 0 && this.boundingBox.isVectorInside(entity)) {
int exp = xpOrb.getExp();
- entity.kill();
+ entity.close();
this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_EXPERIENCE_ORB);
- pickedXPOrb = tick;
+ this.pickedXPOrb = this.server.getTick();
- //Mending
- ArrayList itemsWithMending = new ArrayList<>();
+ IntArrayList itemsWithMending = new IntArrayList();
for (int i = 0; i < 4; i++) {
- if (inventory.getArmorItem(i).getEnchantment((short)Enchantment.ID_MENDING) != null) {
- itemsWithMending.add(inventory.getSize() + i);
+ if (this.inventory.getArmorItem(i).hasEnchantment(Enchantment.ID_MENDING)) {
+ itemsWithMending.add(this.inventory.getSize() + i);
}
}
- if (inventory.getItemInHand().getEnchantment((short)Enchantment.ID_MENDING) != null) {
- itemsWithMending.add(inventory.getHeldItemIndex());
+
+ if (this.inventory.getItemInHandFast().hasEnchantment(Enchantment.ID_MENDING)) {
+ itemsWithMending.add(this.inventory.getHeldItemIndex());
+ }
+
+ Item offhand = this.getOffhandInventory().getItem(0);
+ if (offhand.getId() == Item.SHIELD && offhand.hasEnchantment(Enchantment.ID_MENDING)) {
+ itemsWithMending.add(-1);
}
- 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;
+
+ if (!itemsWithMending.isEmpty()) {
+ int itemToRepair = itemsWithMending.getInt(Utils.random.nextInt(itemsWithMending.size()));
+ boolean isOffhand = itemToRepair == -1;
+
+ Item repaired = isOffhand ? offhand : this.inventory.getItem(itemToRepair);
+ if (repaired instanceof ItemDurable) {
+ 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 {
+ this.inventory.setItem(itemToRepair, repaired);
+ }
return true;
}
}
@@ -5415,6 +6970,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;
}
@@ -5447,7 +7007,7 @@ public void startFishing(Item fishingRod) {
.add(new FloatTag("", (float) yaw))
.add(new FloatTag("", (float) pitch)));
double f = 1.1;
- EntityFishingHook fishingHook = new EntityFishingHook(chunk, nbt, this);
+ EntityFishingHook fishingHook = (EntityFishingHook) Entity.createEntity(EntityFishingHook.NETWORK_ID, chunk, nbt, this);
fishingHook.setMotion(new Vector3(-Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * f * f, -Math.sin(Math.toRadians(pitch)) * f * f,
Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * f * f));
ProjectileLaunchEvent ev = new ProjectileLaunchEvent(fishingHook);
@@ -5482,58 +7042,112 @@ 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;
+ }
+
+ @Override
+ public final boolean canSaveToStorage() {
+ return false;
+ }
- this.server.getScheduler().scheduleAsyncTask(this.preLoginEventTask);
- this.processLogin();
+ /**
+ * Whether interactions should be handled as if player is sneaking
+ */
+ public boolean sneakToBlockInteract() {
+ return this.isSneaking() || this.flySneaking;
}
/**
@@ -5548,6 +7162,13 @@ public void setHudElementVisibility(boolean visible, HudElement... elements) {
this.dataPacket(pk);
}
+ @Override
+ public void setGliding(boolean value) {
+ this.fireworkBoostTicks = 0;
+
+ super.setGliding(value);
+ }
+
/**
* Close form windows sent with showFormWindow
*/
diff --git a/src/main/java/cn/nukkit/PlayerFood.java b/src/main/java/cn/nukkit/PlayerFood.java
index 90ff6a9a9ca..ee91b07608d 100644
--- a/src/main/java/cn/nukkit/PlayerFood.java
+++ b/src/main/java/cn/nukkit/PlayerFood.java
@@ -9,22 +9,22 @@
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 double foodExpLevel = 0;
+ private int foodLevel;
+ private float foodSaturationLevel;
+ private int foodTickTimer;
+ private double foodExpLevel;
private final Player player;
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 e871f05dc07..ce46f7893bd 100644
--- a/src/main/java/cn/nukkit/Server.java
+++ b/src/main/java/cn/nukkit/Server.java
@@ -4,9 +4,11 @@
import cn.nukkit.blockentity.*;
import cn.nukkit.command.*;
import cn.nukkit.console.NukkitConsole;
+import cn.nukkit.dispenser.DispenseBehaviorRegister;
import cn.nukkit.entity.Attribute;
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.EntityHuman;
+import cn.nukkit.entity.custom.EntityManager;
import cn.nukkit.entity.data.Skin;
import cn.nukkit.entity.item.*;
import cn.nukkit.entity.mob.*;
@@ -16,7 +18,6 @@
import cn.nukkit.event.HandlerList;
import cn.nukkit.event.level.LevelInitEvent;
import cn.nukkit.event.level.LevelLoadEvent;
-import cn.nukkit.event.server.BatchPacketsEvent;
import cn.nukkit.event.server.PlayerDataSerializeEvent;
import cn.nukkit.event.server.QueryRegenerateEvent;
import cn.nukkit.event.server.ServerStopEvent;
@@ -24,6 +25,7 @@
import cn.nukkit.inventory.Recipe;
import cn.nukkit.item.Item;
import cn.nukkit.item.RuntimeItems;
+import cn.nukkit.item.custom.CustomItemManager;
import cn.nukkit.item.enchantment.Enchantment;
import cn.nukkit.lang.BaseLang;
import cn.nukkit.lang.TextContainer;
@@ -31,18 +33,14 @@
import cn.nukkit.level.EnumLevel;
import cn.nukkit.level.GlobalBlockPalette;
import cn.nukkit.level.Level;
-import cn.nukkit.level.Position;
import cn.nukkit.level.biome.EnumBiome;
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.math.Vector3;
import cn.nukkit.metadata.EntityMetadataStore;
import cn.nukkit.metadata.LevelMetadataStore;
import cn.nukkit.metadata.PlayerMetadataStore;
@@ -52,11 +50,11 @@
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.BiomeDefinitionListPacket;
import cn.nukkit.network.protocol.DataPacket;
import cn.nukkit.network.protocol.PlayerListPacket;
import cn.nukkit.network.protocol.ProtocolInfo;
@@ -81,7 +79,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;
@@ -91,13 +88,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;
@@ -106,107 +98,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 float maxUse;
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 spawnThresholdRadius;
+ private String ip;
+ private int port;
+ private final UUID serverID = UUID.randomUUID();
+ 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 UUID_PATTERN = 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) {
@@ -214,13 +174,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]);
@@ -229,23 +187,140 @@ 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.
+ */
+ private String motd;
+ /**
+ * Default player data saving enabled.
+ */
+ boolean shouldSavePlayerData;
+ /**
+ * Anti fly checks enabled.
+ */
+ private boolean allowFlight;
+ /**
+ * Hardcore mode enabled.
+ */
+ private boolean isHardcore;
+ /**
+ * Force resource packs.
+ */
+ private boolean forceResources;
+ /**
+ * Force player gamemode to default on every join.
+ */
+ private boolean forceGamemode;
+ /**
+ * Whitelist enabled.
+ */
+ public boolean whitelistEnabled;
+ /**
+ * Xbox authentication enabled.
+ */
+ public boolean xboxAuth;
+ /**
+ * Server side achievements enabled.
+ */
+ boolean achievementsEnabled;
+ /**
+ * Pvp enabled. Can be changed per world using game rules.
+ */
+ boolean pvpEnabled;
+ /**
+ * Announce server side announcements to all players.
+ */
+ 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.
+ */
+ int spawnThreshold;
+ /**
+ * Zlib compression level for sent packets.
+ */
+ public int networkCompressionLevel;
+ /**
+ * Maximum view distance in chunks.
+ */
+ private int viewDistance;
+ /**
+ * Server's default gamemode.
+ */
+ public int gamemode;
+ /**
+ * Minimum amount of time between player skin changes.
+ */
+ private int skinChangeCooldown;
+ /**
+ * Spawn protection radius.
+ */
+ private int spawnRadius;
+ /**
+ * How often auto save should happen.
+ */
+ private int autoSaveTicks;
+ /**
+ * Limit automatic tick rate.
+ */
+ private int autoTickRateLimit;
+ /**
+ * Showing plugins in query enabled.
+ */
+ public boolean queryPlugins;
+ /**
+ * Chunk caching enabled.
+ */
+ public boolean cacheChunks;
+ /**
+ * Whether attacking an entity should stop player from sprinting.
+ */
+ boolean attackStopSprint;
+ /**
+ * Enable automatic tick rate adjustments.
+ */
+ private boolean autoTickRate;
+ /**
+ * Force server side translations.
+ */
+ private boolean forceLanguage;
+ /**
+ * Always tick players.
+ */
+ private boolean alwaysTickPlayers;
+ /**
+ * Don't disable client's own packs when force-resources is enabled.
+ */
+ boolean forceResourcesAllowOwnPacks;
+ /**
+ * Enable encryption.
+ */
+ 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!");
currentThread = Thread.currentThread(); // Saves the current thread instance as a reference, used in Server#isPrimaryThread()
@@ -253,27 +328,28 @@ public Level remove(Object key) {
this.filePath = filePath;
if (!new File(dataPath + "worlds/").exists()) {
+ //noinspection ResultOfMethodCallIgnored
new File(dataPath + "worlds/").mkdirs();
}
if (!new File(dataPath + "players/").exists()) {
+ //noinspection ResultOfMethodCallIgnored
new File(dataPath + "players/").mkdirs();
}
if (!new File(pluginPath).exists()) {
+ //noinspection ResultOfMethodCallIgnored
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!");
@@ -320,14 +396,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;
@@ -339,53 +413,21 @@ 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);
- }
- });
+ this.ignoredPackets.addAll(getConfig().getStringList("debug.ignored-packets"));
- // 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);
@@ -396,30 +438,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()));
}
}
@@ -434,58 +463,33 @@ 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
+ //noinspection ResultOfMethodCallIgnored
+ EnumBiome.values();
Effect.init();
Potion.init();
Attribute.init();
- GlobalBlockPalette.getOrCreateRuntimeId(0, 0); //Force it to load
+ DispenseBehaviorRegister.init();
+ //noinspection ResultOfMethodCallIgnored
+ EntityManager.get();
+ //noinspection ResultOfMethodCallIgnored
+ BiomeDefinitionListPacket.getCachedPacket();
- // 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);
}
@@ -500,34 +504,66 @@ public Level remove(Object key) {
this.pluginManager = new PluginManager(this, this.commandMap);
this.pluginManager.subscribeToPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE, this.consoleSender);
-
this.pluginManager.registerInterface(JavaPluginLoader.class);
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);
+ boolean regenerateItemPalette = false;
+
+ if (CustomItemManager.get().closeRegistry()) {
+ regenerateItemPalette = true;
+ }
+
+ if (regenerateItemPalette) {
+ RuntimeItems.getMapping().generatePalette();
+ }
+
+ EntityManager.get().closeRegistry();
+
+ Item.initCreativeItems();
+
+ 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(TheEnd.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)) {
long seed;
+ String seedString = String.valueOf(this.getConfig("worlds." + name + ".seed", System.currentTimeMillis()));
try {
- seed = ((Integer) this.getConfig("worlds." + name + ".seed")).longValue();
- } catch (Exception e) {
- seed = System.currentTimeMillis();
+ seed = Long.parseLong(seedString);
+ } catch (NumberFormatException e) {
+ seed = seedString.hashCode();
}
Map options = new HashMap<>();
@@ -569,20 +605,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());
+ Vector3 spawn = level.getProvider().getSpawn();
+ level.populateChunk(spawn.getChunkX(), spawn.getChunkZ(), true);
+ //}
+
+ EnumLevel.initLevels();
this.enablePlugins(PluginLoadOrder.POSTWORLD);
@@ -591,13 +629,18 @@ public Level remove(Object key) {
this.watchdog.start();
}
+ // Initialize metrics
+ new NukkitMetrics(this);
+
this.start();
}
+ @SuppressWarnings("UnusedReturnValue")
public int broadcastMessage(String message) {
return this.broadcast(message, BROADCAST_CHANNEL_USERS);
}
+ @SuppressWarnings("UnusedReturnValue")
public int broadcastMessage(TextContainer message) {
return this.broadcast(message, BROADCAST_CHANNEL_USERS);
}
@@ -610,6 +653,7 @@ public int broadcastMessage(String message, CommandSender[] recipients) {
return recipients.length;
}
+ @SuppressWarnings("UnusedReturnValue")
public int broadcastMessage(String message, Collection extends CommandSender> recipients) {
for (CommandSender recipient : recipients) {
recipient.sendMessage(message);
@@ -678,66 +722,14 @@ public static void broadcastPacket(Player[] players, 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);
- 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(this, 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()) {
@@ -751,21 +743,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");
}
@@ -779,57 +783,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;
}
@@ -840,103 +855,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();
if (this.watchdog != null) {
+ this.getLogger().debug("Stopping Watchdog...");
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[] QUERY_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, QUERY_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 {
@@ -950,22 +979,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) {
@@ -979,11 +1011,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) {
@@ -999,9 +1028,7 @@ public void removeOnlinePlayer(Player player) {
if (player.getUniqueId() == null) {
return;
}
- 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())};
@@ -1026,7 +1053,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) {
@@ -1060,13 +1087,12 @@ public void sendFullPlayerListData(Player player) {
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);
}
@@ -1074,23 +1100,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;
}
@@ -1111,74 +1134,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();
@@ -1186,13 +1207,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();
}
@@ -1201,25 +1223,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);
@@ -1243,36 +1263,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() {
@@ -1292,7 +1302,7 @@ public String getNukkitVersion() {
}
public String getCodename() {
- return Nukkit.CODENAME;
+ return "";
}
public String getVersion() {
@@ -1324,15 +1334,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() {
@@ -1351,23 +1361,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) {
@@ -1394,23 +1396,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;
}
@@ -1421,17 +1422,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":
@@ -1441,9 +1439,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;
}
@@ -1456,45 +1451,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() {
@@ -1529,14 +1524,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;
@@ -1546,36 +1556,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);
@@ -1584,7 +1639,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, removing: " + name);
nameLookup.delete(nameBytes);
return Optional.empty();
}
@@ -1605,12 +1660,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));
}
@@ -1658,8 +1713,7 @@ private CompoundTag getOfflinePlayerDataInternal(String name, boolean runEvent,
return NBTIO.readCompressed(dataStream.get());
}
} catch (IOException e) {
- log.warn(this.getLanguage().translateString("nukkit.data.playerCorrupted", name));
- log.throwing(e);
+ log.warn(this.getLanguage().translateString("nukkit.data.playerCorrupted", name), e);
} finally {
if (dataStream.isPresent()) {
try {
@@ -1674,10 +1728,11 @@ private CompoundTag getOfflinePlayerDataInternal(String name, boolean runEvent,
if (this.shouldSavePlayerData()) {
log.info(this.getLanguage().translateString("nukkit.data.playerNotFound", name));
}
- Position spawn = this.getDefaultLevel().getSafeSpawn();
+ Vector3 spawn = this.getDefaultLevel().getProvider().getSpawn();
+ 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))
@@ -1695,7 +1750,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);
@@ -1722,48 +1777,62 @@ 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(null, 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);
} catch (Exception e) {
- log.error(this.getLanguage().translateString("nukkit.data.saveError", name, e));
+ log.error(this.getLanguage().translateString("nukkit.data.saveError", name, e), e);
}
}
+ /**
+ * 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();
- return !uuidPattern.matcher(name).matches() && name.endsWith(".dat");
+ return !UUID_PATTERN.matcher(name).matches() && name.endsWith(".dat");
});
if (files == null) {
@@ -1806,6 +1875,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();
@@ -1826,10 +1901,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;
}
}
@@ -1837,6 +1917,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<>();
@@ -1851,46 +1937,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)) {
@@ -1901,28 +2020,54 @@ 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
+ */
+ @SuppressWarnings("UnusedReturnValue")
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;
}
@@ -1931,14 +2076,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;
}
@@ -1946,37 +2090,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;
@@ -1991,7 +2174,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;
@@ -1999,7 +2182,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;
@@ -2007,51 +2190,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;
@@ -2063,7 +2223,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;
@@ -2072,19 +2232,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;
}
@@ -2099,50 +2278,125 @@ 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;
+ Object value = this.properties.get(variable);
+ return value == null ? defaultValue : value;
}
+ /**
+ * 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;
+ Object value = this.properties.get(key);
+ return value == null ? defaultValue : value.toString();
}
+ /**
+ * 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;
+ Object value = this.properties.get(variable);
+ return value == null || (value instanceof String && ((String) value).isEmpty()) ? defaultValue : Integer.parseInt(String.valueOf(value));
}
+ /**
+ * 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;
+ Object value = this.properties.get(variable);
+ if (value == null) {
+ value = defaultValue;
+ }
if (value instanceof Boolean) {
return (Boolean) value;
}
@@ -2156,11 +2410,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) {
@@ -2170,14 +2436,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);
@@ -2187,6 +2468,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);
@@ -2196,43 +2482,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);
}
+ /**
+ * 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<>();
@@ -2249,51 +2570,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.
+ * 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.
+ * 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);
+ 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);
@@ -2301,92 +2655,94 @@ 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);
+ Entity.registerEntity("Breeze", EntityBreeze.class);
+ Entity.registerEntity("Bogged", EntityBogged.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);
+ Entity.registerEntity("Sniffer", EntitySniffer.class);
+ Entity.registerEntity("Armadillo", EntityArmadillo.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);
@@ -2395,6 +2751,7 @@ private void registerBlockEntities() {
BlockEntity.registerBlockEntity(BlockEntity.FLOWER_POT, BlockEntityFlowerPot.class);
BlockEntity.registerBlockEntity(BlockEntity.BREWING_STAND, BlockEntityBrewingStand.class);
BlockEntity.registerBlockEntity(BlockEntity.ITEM_FRAME, BlockEntityItemFrame.class);
+ BlockEntity.registerBlockEntity(BlockEntity.GLOW_ITEM_FRAME, BlockEntityItemFrameGlow.class);
BlockEntity.registerBlockEntity(BlockEntity.CAULDRON, BlockEntityCauldron.class);
BlockEntity.registerBlockEntity(BlockEntity.ENDER_CHEST, BlockEntityEnderChest.class);
BlockEntity.registerBlockEntity(BlockEntity.BEACON, BlockEntityBeacon.class);
@@ -2405,29 +2762,160 @@ 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 EnumLevel.NETHER.getLevel() != null;
}
+ /**
+ * 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) {
+ 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", true);
+
+ this.networkCompressionThreshold = this.getConfig("network.batch-threshold", 256);
+ this.networkCompressionLevel = Math.max(Math.min(this.getConfig("network.compression-level", 5), 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.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.spawnThresholdRadius = (int) Math.ceil(Math.sqrt(this.spawnThreshold));
+
+ /* server.properties */
+
+ this.maxPlayers = this.getPropertyInt("max-players", 20);
+ 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.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", "0")) & 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..e405c0759ec 100644
--- a/src/main/java/cn/nukkit/block/Block.java
+++ b/src/main/java/cn/nukkit/block/Block.java
@@ -10,6 +10,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 +19,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 +28,89 @@
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;
+
+ @SuppressWarnings("UnnecessaryBoxing")
+ public static final int MAX_BLOCK_ID = Integer.valueOf("2048");
+ 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() {
- 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 {
+ @SuppressWarnings("rawtypes")
Constructor constructor = c.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
- for (int data = 0; data < 16; ++data) {
- fullList[(id << 4) | data] = (Block) constructor.newInstance(data);
+ 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 < 16; ++data) {
- fullList[(id << 4) | data] = block;
+ 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 +125,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 +137,147 @@ 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;
+ }
+
+ 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);
+
+ 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 (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;
+
+ return fullList[fullId].clone();
}
public static Block get(int fullId, Level level, int x, int y, int z) {
+ 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 = fullList[fullId].clone();
+
block.x = x;
block.y = y;
block.z = z;
block.level = level;
+ block.layer = layer;
return block;
}
+ public static int getBlockLight(int blockId) {
+ return light[blockId];
+ }
+
+ public static int getBlockLightFilter(int blockId) {
+ return lightFilter[blockId];
+ }
+
+ public static boolean isBlockSolidById(int blockId) {
+ return solid[blockId];
+ }
+
+ public static boolean isBlockTransparentById(int blockId) {
+ 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 +289,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 +329,10 @@ public int getToolType() {
return ItemTool.TYPE_NONE;
}
+ public int getToolTier() {
+ return 0;
+ }
+
public double getFrictionFactor() {
return 0.6;
}
@@ -487,6 +377,10 @@ public boolean canBePushed() {
return true;
}
+ public boolean breakWhenPushed() {
+ return false;
+ }
+
public boolean hasComparatorInputOverride() {
return false;
}
@@ -507,14 +401,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 +434,6 @@ public int getDamage() {
}
public void setDamage(int meta) {
- // Do nothing
}
public final void setDamage(Integer meta) {
@@ -539,13 +448,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 +506,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 +553,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);
@@ -648,9 +571,9 @@ public double getBreakTime(Item item, Player player) {
}
/**
- * @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: This function lacks the Player and is not accurate enough, use getBreakTime(Item, Player) instead
+ * @param item item used to break the block
+ * @return break time in seconds
*/
@Deprecated
public double getBreakTime(Item item) {
@@ -663,9 +586,8 @@ public double getBreakTime(Item item) {
(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) {
+ ) {
+ switch (item.getTier()) {
case ItemTool.TIER_WOODEN:
base /= 2;
break;
@@ -702,24 +624,23 @@ public boolean canBeBrokenWith(Item item) {
}
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 +652,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 +664,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 +676,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 +688,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 +700,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 +712,13 @@ public Block west(int step) {
return getSide(BlockFace.WEST, step);
}
+ public Block west(int step, BlockLayer layer) {
+ return this.getSide(layer, BlockFace.WEST, step);
+ }
+
@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 +731,6 @@ public boolean collidesWithBB(AxisAlignedBB bb, boolean collisionBB) {
}
public void onEntityCollide(Entity entity) {
-
}
public AxisAlignedBB getBoundingBox() {
@@ -801,6 +745,10 @@ protected AxisAlignedBB recalculateBoundingBox() {
return this;
}
+ protected AxisAlignedBB recalculateCollisionBoundingBox() {
+ return this.getBoundingBox();
+ }
+
@Override
public double getMinX() {
return this.x;
@@ -831,10 +779,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 +911,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 +937,93 @@ 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 canConnectToFullSolid(Block down) {
+ if (down.isTransparent()) {
+ switch (down.getId()) {
+ case BEACON:
+ case ICE:
+ case GLASS:
+ case STAINED_GLASS:
+ case BARRIER:
+ case GLOWSTONE:
+ case SEA_LANTERN:
+ case MANGROVE_ROOTS:
+ case MUDDY_MANGROVE_ROOTS:
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ protected static boolean canStayOnFullSolid(Block down) {
+ if (canConnectToFullSolid(down)) {
+ return true;
+ }
+ switch (down.getId()) {
+ case SCAFFOLDING:
+ case HOPPER_BLOCK:
+ return true;
+ }
+ if (down instanceof BlockSlab) {
+ return ((BlockSlab) down).hasTopBit();
+ }
+ return down instanceof BlockTrapdoor && ((BlockTrapdoor) down).isTop() && !((BlockTrapdoor) down).isOpen();
+ }
+
+ 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;
+ }
+
+ protected boolean isNarrowSurface() {
+ return this instanceof BlockGlassPane || this instanceof BlockFence || this instanceof BlockWall || this instanceof BlockChain || this instanceof BlockIronBars;
+ }
+
+ public boolean alwaysDropsOnExplosion() {
+ return false;
+ }
+
+ /**
+ * Returns true for WATER and STILL_WATER, false for others
+ */
+ public static boolean isWater(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..3bac780bdb4
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockAmethystBud.java
@@ -0,0 +1,113 @@
+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) {
+ if (Block.canConnectToFullSolid(this.getSide(face.getOpposite()))) {
+ this.setDamage(face.getIndex());
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+ return false;
+ }
+
+ @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..466a86065bd 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..0f8769d9d0b
--- /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(Block.get(this.getId(), 0), 0);
+ }
+
+ @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..92de7f62e45
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBamboo.java
@@ -0,0 +1,320 @@
+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;
+ int time = level.getTime() % Level.TIME_FULL;
+ boolean canGrow = time < 13184 || time > 22800;
+ if (this.getAge() == 0 && (up = this.up()).getId() == AIR && canGrow/*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..65925ee3c21
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBambooSapling.java
@@ -0,0 +1,174 @@
+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;
+ int time = level.getTime() % Level.TIME_FULL;
+ boolean canGrow = time < 13184 || time > 22800;
+ if (getAge() == 0 && (up = this.up()).getId() == AIR && canGrow/*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 49f7a9f5429..213641230e5 100644
--- a/src/main/java/cn/nukkit/block/BlockBed.java
+++ b/src/main/java/cn/nukkit/block/BlockBed.java
@@ -3,19 +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 {
@@ -38,11 +46,6 @@ public boolean canBeActivated() {
return true;
}
- @Override
- public double getResistance() {
- return 1;
- }
-
@Override
public double getHardness() {
return 0.2;
@@ -58,29 +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_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;
- }
-
- if (player == null) {
- return false;
- }
-
- int time = this.getLevel().getTime() % Level.TIME_FULL;
-
- boolean isNight = (time >= Level.TIME_NIGHT && time < Level.TIME_SUNRISE);
-
- if (!isNight && !this.getLevel().isThundering()) {
- player.sendMessage(new TranslationContainer("tile.bed.noSleep"));
+ 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));
+ }
+
+ Explosion explosion = new Explosion(this.add(0.5, 0, 0.5), 5, this);
+ explosion.explodeA();
+ explosion.explodeB();
+ }
return true;
}
@@ -93,23 +90,56 @@ 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 {
- player.sendMessage(new TranslationContainer("tile.bed.notValid"));
+ if (player != null) {
+ player.sendMessage("§7%tile.bed.notValid", true);
+ }
return true;
}
}
- if (!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;
@@ -117,16 +147,15 @@ 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) {
- 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());
@@ -137,36 +166,69 @@ 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 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);
+ } 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;
}
}
- 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
+ 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, 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;
}
@@ -175,12 +237,13 @@ private void createBlockEntity(Block pos, int color) {
CompoundTag nbt = BlockEntity.getDefaultCompound(pos, BlockEntity.BED);
nbt.putByte("color", color);
- BlockEntity.createBlockEntity(BlockEntity.BED, pos.getChunk(), 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
@@ -204,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..9864544eb51
--- /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..9376f1d9783
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBell.java
@@ -0,0 +1,405 @@
+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;
+ }
+
+ if (attachmentFace == BlockFace.UP) {
+ return Block.canStayOnFullSolid(support);
+ }
+ 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..56d7373ead6
--- /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..01df5f71dcb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockBlueIce.java
@@ -0,0 +1,29 @@
+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;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockBone.java b/src/main/java/cn/nukkit/block/BlockBone.java
index ae747a445b9..7945abb9fbe 100644
--- a/src/main/java/cn/nukkit/block/BlockBone.java
+++ b/src/main/java/cn/nukkit/block/BlockBone.java
@@ -11,7 +11,7 @@
/**
* @author CreeperFace
*/
-public class BlockBone extends BlockSolidMeta implements Faceable {
+public class BlockBone extends BlockSolid implements Faceable {
private static final int[] FACES = {
0,
@@ -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)};
}
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..1ad784a35eb 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);
}
@@ -36,21 +37,21 @@ public double getResistance() {
return 30;
}
+ private static final String[] NAMES = {
+ "Stone Bricks",
+ "Mossy Stone Bricks",
+ "Cracked Stone Bricks",
+ "Chiseled Stone Bricks"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Stone Bricks",
- "Mossy Stone Bricks",
- "Cracked Stone Bricks",
- "Chiseled Stone Bricks"
- };
-
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@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..c5c22d59127
--- /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.level.getBlockIdAt((int) this.x, (int) this.y + 1, (int) this.z) == AIR) {
+ 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..a17b8d7d3d0
--- /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.isWater(block.getId()) && 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..fa08018df5b 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;
@@ -35,12 +34,12 @@ public double getHardness() {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (target.isTransparent()) {
+ this.setDamage(face.getIndex());
+ if (!isSupportValid(this.getSide(this.getFacing().getOpposite()))) {
return false;
}
- this.setDamage(face.getIndex());
- this.level.setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
@@ -58,19 +57,18 @@ 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;
}
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
- if (this.getSide(getFacing().getOpposite()).isTransparent()) {
+ if (!isSupportValid(this.getSide(this.getFacing().getOpposite()))) {
this.level.useBreakOn(this, Item.get(Item.WOODEN_PICKAXE));
return Level.BLOCK_UPDATE_NORMAL;
}
@@ -80,11 +78,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;
@@ -93,6 +90,16 @@ public int onUpdate(int type) {
return 0;
}
+ private boolean isSupportValid(Block block) {
+ if (!block.isTransparent()) {
+ return true;
+ }
+ if (this.getFacing() == BlockFace.UP) {
+ return Block.canStayOnFullSolid(block);
+ }
+ return Block.canConnectToFullSolid(block);
+ }
+
public boolean isActivated() {
return ((this.getDamage() & 0x08) == 0x08);
}
@@ -126,11 +133,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/BlockButtonWooden.java b/src/main/java/cn/nukkit/block/BlockButtonWooden.java
index b5d8fb2944a..490fa749034 100644
--- a/src/main/java/cn/nukkit/block/BlockButtonWooden.java
+++ b/src/main/java/cn/nukkit/block/BlockButtonWooden.java
@@ -22,7 +22,7 @@ public int getId() {
@Override
public String getName() {
- return "Wooden Button";
+ return "Oak Button";
}
@Override
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..2e5b9ce443b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCampfire.java
@@ -0,0 +1,261 @@
+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.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.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_FIZZ);
+ }
+ }
+ 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..87cf0709a33 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;
@@ -47,7 +42,7 @@ public boolean isSolid() {
@Override
public String getName() {
- return DyeColor.getByWoolData(getDamage()) + " Carpet";
+ return DyeColor.getByWoolData(getDamage()).getName() + " Carpet";
}
@Override
@@ -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..9c8130b2453 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,142 @@ 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.count--;
+ }
+
+ 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.count--;
+ }
- Item potion = new ItemPotion();
if (player.getInventory().canAddItem(potion)) {
player.getInventory().addItem(potion);
} else {
@@ -179,7 +272,48 @@ 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.count--;
+ }
+
+ 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 +322,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 +343,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 +361,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 +401,54 @@ 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.count--;
+ 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..054e55a851a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCaveVines.java
@@ -0,0 +1,243 @@
+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;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+}
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..f4b3daec93e 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 {
@@ -61,42 +61,10 @@ public int getToolType() {
return ItemTool.TYPE_AXE;
}
- @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;
- }
-
- @Override
- public double getMaxX() {
- return this.x + 0.9375;
- }
-
- @Override
- public double getMaxY() {
- return this.y + 0.9475;
- }
-
- @Override
- 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 +82,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 +102,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 +126,19 @@ public boolean onBreak(Item item) {
@Override
public boolean onActivate(Item item, Player player) {
if (player != null) {
- Block top = up();
- if (!top.isTransparent()) {
- return true;
+ Block top = this.up();
+ if (!(top instanceof BlockStairs)) { // Stairs don't block chest on vanilla
+ if (!top.isTransparent()) {
+ 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 +172,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..19d098d522e 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[] FACES_2 = {
+ 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,23 +86,14 @@ 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.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(FACES_2[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..36e1febeee1
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockComposter.java
@@ -0,0 +1,210 @@
+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, MUSHROOM_STEW);
+ registerBlocks(85, HAY_BALE, BROWN_MUSHROOM_BLOCK, RED_MUSHROOM_BLOCK, 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()) {
+ empty(item, player);
+ 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) {
+ if (isEmpty()) {
+ return null;
+ }
+ 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/BlockConcrete.java b/src/main/java/cn/nukkit/block/BlockConcrete.java
index 115f543014e..3597f2981c9 100644
--- a/src/main/java/cn/nukkit/block/BlockConcrete.java
+++ b/src/main/java/cn/nukkit/block/BlockConcrete.java
@@ -35,7 +35,7 @@ public double getHardness() {
@Override
public String getName() {
- return "Concrete";
+ return getDyeColor().getName() + " Concrete";
}
@Override
diff --git a/src/main/java/cn/nukkit/block/BlockConcretePowder.java b/src/main/java/cn/nukkit/block/BlockConcretePowder.java
index 20811ae3608..6e43d09f0b2 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 (CONCRETE_POWDER << Block.DATA_BITS) + getDamage();
}
@Override
@@ -44,7 +33,7 @@ public int getId() {
@Override
public String getName() {
- return "Concrete Powder";
+ return getDyeColor().getName() + " Concrete Powder";
}
@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);
}
}
@@ -104,4 +93,8 @@ public boolean place(Item item, Block b, Block target, BlockFace face, double fx
public BlockColor getColor() {
return DyeColor.getByWoolData(getDamage()).getColor();
}
+
+ public DyeColor getDyeColor() {
+ return DyeColor.getByWoolData(getDamage());
+ }
}
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..73139c9c87a
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoral.java
@@ -0,0 +1,114 @@
+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 = {
+ "Tube Coral",
+ "Brain Coral",
+ "Bubble Coral",
+ "Fire Coral",
+ "Horn Coral",
+ "",
+ "",
+ "",
+ "Dead Tube Coral",
+ "Dead Brain Coral",
+ "Dead Bubble Coral",
+ "Dead Fire Coral",
+ "Dead Horn Coral"
+ };
+
+ public BlockCoral() {
+ this(0);
+ }
+
+ public BlockCoral(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ int variant = this.getDamage();
+ 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.getDamage() < 8 && !(block instanceof BlockWater || block.level.isBlockWaterloggedAt(block.getChunk(), (int) block.x, (int) block.y, (int) block.z))) {
+ this.setDamage(8 + this.getDamage()); // Dead
+ }
+ 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];
+ }
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+}
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..71fdbc8ea82
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralBlock.java
@@ -0,0 +1,124 @@
+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 = {
+ "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));
+ level.getServer().getPluginManager().callEvent(event);
+ 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..2ba5330c0a2
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockCoralFan.java
@@ -0,0 +1,195 @@
+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 = {
+ "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()));
+ level.getServer().getPluginManager().callEvent(event);
+ 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.NORMAL, 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.NORMAL, 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..2080606f77b
--- /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 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..a327f55ec0f 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,36 @@ 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) {
+ int time = level.getTime() % Level.TIME_FULL;
+ return time < 13184 || time > 22800 ? 15 : 0;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
}
@Override
@@ -48,11 +75,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..3169f1d88cf 100644
--- a/src/main/java/cn/nukkit/block/BlockDaylightDetectorInverted.java
+++ b/src/main/java/cn/nukkit/block/BlockDaylightDetectorInverted.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;
/**
* Created on 2015/11/22 by CreeperFace.
@@ -9,9 +12,6 @@
*/
public class BlockDaylightDetectorInverted extends BlockDaylightDetector {
- public BlockDaylightDetectorInverted() {
- }
-
@Override
public int getId() {
return DAYLIGHT_DETECTOR_INVERTED;
@@ -22,13 +22,25 @@ 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) {
+ int time = level.getTime() % Level.TIME_FULL;
+ return time < 13184 || time > 22800 ? 0 : 15;
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockDeadBush.java b/src/main/java/cn/nukkit/block/BlockDeadBush.java
index 23b4c9e2296..66d06ac0108 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);
}
@@ -42,7 +40,7 @@ public boolean canBeReplaced() {
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
Block down = this.down();
int id = down.getId();
- if (id == SAND || id == TERRACOTTA || id == STAINED_TERRACOTTA || id == DIRT || id == PODZOL || id == MYCELIUM || id == GRASS) {
+ if (id == SAND || id == TERRACOTTA || id == STAINED_TERRACOTTA || id == DIRT || id == PODZOL || id == MYCELIUM || id == GRASS || id == MOSS_BLOCK) {
this.getLevel().setBlock(block, this, true, true);
return true;
}
@@ -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..54dba736218 100644
--- a/src/main/java/cn/nukkit/block/BlockDirt.java
+++ b/src/main/java/cn/nukkit/block/BlockDirt.java
@@ -1,13 +1,14 @@
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.item.*;
+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 +18,7 @@ public BlockDirt() {
this(0);
}
- public BlockDirt(int meta){
+ public BlockDirt(int meta) {
super(meta);
}
@@ -54,17 +55,47 @@ 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;
+ }
+ } else if (player != null && item.getId() == Item.POTION && item.getDamage() == ItemPotion.NO_EFFECTS) {
+ item.count--;
+ Item emptyBottle = Item.get(Item.GLASS_BOTTLE);
+ if (player.getInventory().canAddItem(emptyBottle)) {
+ player.getInventory().addItem(emptyBottle);
+ } else {
+ player.getLevel().dropItem(player.add(0, 1.3, 0), emptyBottle, player.getDirectionVector().multiply(0.4));
+ }
+ this.getLevel().setBlock(this, get(MUD), true);
+ return true;
}
return false;
@@ -72,7 +103,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..b55eab93cbd 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,15 +231,14 @@ 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];
+
+ int direction = FACES[player != null ? player.getDirection().getHorizontalIndex() : 0];
Block left = this.getSide(player.getDirection().rotateYCCW());
Block right = this.getSide(player.getDirection().rotateY());
@@ -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..9d0ce3ace01 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
@@ -82,9 +84,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
Block up = up();
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);
+ if (up.getId() == AIR && (id == GRASS || id == DIRT || id == PODZOL || id == FARMLAND || id == MYCELIUM || id == MOSS_BLOCK)) {
+ // 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..efe46e3484e 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 = {
+ "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..e56c023ee02 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 = {
+ "Stone",
+ "Sandstone",
+ "Oak",
+ "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..b6e21925724
--- /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 = {
+ "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..c85a71c5015
--- /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 = {
+ "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..7bf20a7a6a3 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 = {
+ "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..b92d735f3a7
--- /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.block.custom.properties.BlockProperties;
+import cn.nukkit.block.custom.properties.BlockProperty;
+import cn.nukkit.block.custom.properties.BooleanBlockProperty;
+import cn.nukkit.block.custom.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..e80932bdfef
--- /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.block.custom.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..f6070d46b0d 100644
--- a/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java
+++ b/src/main/java/cn/nukkit/block/BlockEndPortalFrame.java
@@ -4,14 +4,10 @@
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.
*/
@@ -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.sneakToBlockInteract()) {
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.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..801388d6eb6 100644
--- a/src/main/java/cn/nukkit/block/BlockEndRod.java
+++ b/src/main/java/cn/nukkit/block/BlockEndRod.java
@@ -8,12 +8,14 @@
import cn.nukkit.utils.Faceable;
/**
- * http://minecraft.gamepedia.com/End_Rod
+ * ...
*
* @author PikyCZ
*/
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,8 +76,7 @@ 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.setDamage(FACES[player != null ? face.getIndex() : 0]);
this.getLevel().setBlock(block, this, true, true);
return 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..01b74651572 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);
@@ -64,37 +65,12 @@ public int getToolType() {
return ItemTool.TYPE_PICKAXE;
}
- @Override
- public double getMinX() {
- return this.x + 0.0625;
- }
-
- @Override
- public double getMinZ() {
- return this.z + 0.0625;
- }
-
- @Override
- public double getMaxX() {
- return this.x + 0.9375;
- }
-
- @Override
- public double getMaxY() {
- return this.y + 0.9475;
- }
-
- @Override
- 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) {
- 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,8 +88,8 @@ 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
@@ -125,21 +101,11 @@ public boolean onActivate(Item item, Player player) {
}
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 +121,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 +159,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..9cb3ce6a0ff 100644
--- a/src/main/java/cn/nukkit/block/BlockFallable.java
+++ b/src/main/java/cn/nukkit/block/BlockFallable.java
@@ -9,9 +9,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 +27,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 +44,7 @@ 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);
-
- if (fall != null) {
- fall.spawnToAll();
- }
+ Entity.createEntity(EntityFallingBlock.NETWORK_ID, this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt).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..ff2c538eda9
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFallableMeta.java
@@ -0,0 +1,53 @@
+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;
+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());
+
+ Entity.createEntity(EntityFallingBlock.NETWORK_ID, this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt).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..282365f18a9 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,25 +48,21 @@ 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) {
- 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;
}
boolean found = false;
+ boolean unloadedChunk = false;
if (this.level.isRaining()) {
found = true;
@@ -78,10 +74,14 @@ 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.getChunkIfLoaded(x >> 4, z >> 4);
+ if (chunk == null) {
+ unloadedChunk = true;
+ continue;
+ }
- if (block == WATER || block == STILL_WATER) {
+ int block = this.level.getBlockIdAt(chunk, x, y, z);
+ if (Block.isWater(block) || block == FROSTED_ICE || this.level.isBlockWaterloggedAt(chunk, x, y, z)) {
found = true;
break;
}
@@ -90,23 +90,32 @@ public int onUpdate(int type) {
}
}
- Block block = this.level.getBlock(v.setComponents(x, y - 1, z));
- if (found || block instanceof BlockWater) {
+ if (!found && unloadedChunk) {
+ return 0;
+ }
+
+ 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);
- } else {
+ this.level.setBlock(this, this, false, true);
+ } else if (!(up instanceof BlockCrops)) {
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..98f6d05c201 100644
--- a/src/main/java/cn/nukkit/block/BlockFence.java
+++ b/src/main/java/cn/nukkit/block/BlockFence.java
@@ -48,22 +48,22 @@ public int getToolType() {
return ItemTool.TYPE_AXE;
}
+ private static final String[] NAMES = {
+ "Oak Fence",
+ "Spruce Fence",
+ "Birch Fence",
+ "Jungle Fence",
+ "Acacia Fence",
+ "Dark Oak Fence",
+ "",
+ ""
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Oak Fence",
- "Spruce Fence",
- "Birch Fence",
- "Jungle Fence",
- "Acacia Fence",
- "Dark Oak Fence",
- "",
- ""
- };
- return names[this.getDamage() & 0x07];
+ return NAMES[this.getDamage() & 0x07];
}
- @Override
protected AxisAlignedBB recalculateBoundingBox() {
boolean north = this.canConnect(this.north());
boolean south = this.canConnect(this.south());
@@ -99,19 +99,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 +120,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..96ff0b6861f 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,34 +62,15 @@ 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];
+ return NAMES[this.getDamage() & 0x0f];
}
@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 id = down.getId();
- if (id == Block.GRASS || id == Block.DIRT || id == Block.FARMLAND || id == Block.PODZOL || id == MYCELIUM) {
+ if (id == Block.GRASS || id == Block.DIRT || id == Block.FARMLAND || id == Block.PODZOL || id == MYCELIUM || id == MOSS_BLOCK) {
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..efafd50c07d 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,35 @@ 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:
+ case WITHER_ROSE:
+ case MANGROVE_PROPAGULE:
+ return true;
default:
return false;
}
@@ -48,19 +78,24 @@ public int getId() {
return FLOWER_POT_BLOCK;
}
- @Override
- public double getHardness() {
- return 0;
+ private static boolean isSupportValid(Block block) {
+ return block.isSolid() || block.isNarrowSurface() || Block.canStayOnFullSolid(block);
}
@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 +108,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 +119,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,30 +147,26 @@ 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()) {
- item.setCount(item.getCount() - 1);
- player.getInventory().setItemInHand(item.getCount() > 0 ? item : Item.get(Item.AIR));
+ if (!player.isCreative()) {
+ item.count--;
}
return true;
}
@@ -153,13 +184,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 +239,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/BlockFroglight.java b/src/main/java/cn/nukkit/block/BlockFroglight.java
new file mode 100644
index 00000000000..86a103bb176
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFroglight.java
@@ -0,0 +1,37 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.utils.BlockColor;
+
+public abstract class BlockFroglight extends BlockSolidMeta {
+
+ protected BlockFroglight(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.3;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.3;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 15;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WHITE_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFroglightOchre.java b/src/main/java/cn/nukkit/block/BlockFroglightOchre.java
new file mode 100644
index 00000000000..11e80055764
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFroglightOchre.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockFroglightOchre extends BlockFroglight {
+
+ public BlockFroglightOchre() {
+ this(0);
+ }
+
+ public BlockFroglightOchre(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Ochre Froglight";
+ }
+
+ @Override
+ public int getId() {
+ return OCHRE_FROGLIGHT;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFroglightPearlescent.java b/src/main/java/cn/nukkit/block/BlockFroglightPearlescent.java
new file mode 100644
index 00000000000..b1163833fbd
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFroglightPearlescent.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockFroglightPearlescent extends BlockFroglight {
+
+ public BlockFroglightPearlescent() {
+ this(0);
+ }
+
+ public BlockFroglightPearlescent(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Pearlescent Froglight";
+ }
+
+ @Override
+ public int getId() {
+ return PEARLESCENT_FROGLIGHT;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFroglightVerdant.java b/src/main/java/cn/nukkit/block/BlockFroglightVerdant.java
new file mode 100644
index 00000000000..59992bf73c6
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFroglightVerdant.java
@@ -0,0 +1,22 @@
+package cn.nukkit.block;
+
+public class BlockFroglightVerdant extends BlockFroglight {
+
+ public BlockFroglightVerdant() {
+ this(0);
+ }
+
+ public BlockFroglightVerdant(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public String getName() {
+ return "Verdant Froglight";
+ }
+
+ @Override
+ public int getId() {
+ return VERDANT_FROGLIGHT;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockFrogspawn.java b/src/main/java/cn/nukkit/block/BlockFrogspawn.java
new file mode 100644
index 00000000000..4f012abbec1
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockFrogspawn.java
@@ -0,0 +1,45 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.item.Item;
+import cn.nukkit.math.BlockFace;
+
+public class BlockFrogspawn extends BlockFlowable {
+
+ public BlockFrogspawn() {
+ this(0);
+ }
+
+ public BlockFrogspawn(int meta) {
+ super(0);
+ }
+
+ @Override
+ public int getId() {
+ return FROG_SPAWN;
+ }
+
+ @Override
+ public String getName() {
+ return "Frogspawn";
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public int getDropExp() {
+ return 1;
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ Block up;
+ if (!(block instanceof BlockWater) || !((up = block.up()) instanceof BlockAir)) {
+ return false;
+ }
+ return this.getLevel().setBlock(up, this, true, true);
+ }
+}
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..0cecc9f2ce8 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..495bec2ab82
--- /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.block.custom.properties.BlockProperties;
+import cn.nukkit.block.custom.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..65c0e26ebb7 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;
}
}
@@ -79,6 +88,10 @@ public boolean onActivate(Item item, Player player) {
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_RANDOM) {
Block up = this.up();
+ if (up instanceof BlockUnknown) {
+ return 0;
+ }
+
if ((up.isSolid() && !up.isTransparent()) || up instanceof BlockLiquid) {
BlockSpreadEvent ev = new BlockSpreadEvent(this, this, Block.get(BlockID.DIRT));
Server.getInstance().getPluginManager().callEvent(ev);
@@ -88,10 +101,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 +131,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..726e4a067c1 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,17 +60,14 @@ 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.setDamage((this.getDamage() & 0x03) | FACES[face.getIndex()]);
this.getLevel().setBlock(block, this, true, true);
return 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..e868251bc46 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
@@ -80,7 +79,8 @@ public boolean onActivate(Item item, Player player) {
BlockEntity blockEntity = this.level.getBlockEntity(this);
if (blockEntity instanceof BlockEntityHopper) {
- return player.addWindow(((BlockEntityHopper) blockEntity).getInventory()) != -1;
+ player.addWindow(((BlockEntityHopper) blockEntity).getInventory());
+ return true;
}
return false;
@@ -123,11 +123,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 +152,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- return new ItemHopper();
+ return Item.get(Item.HOPPER);
}
@Override
@@ -162,6 +162,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..8d0473eb9be 100644
--- a/src/main/java/cn/nukkit/block/BlockHugeMushroomBrown.java
+++ b/src/main/java/cn/nukkit/block/BlockHugeMushroomBrown.java
@@ -3,8 +3,10 @@
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.level.generator.object.mushroom.BigMushroom;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 28.01.2016.
@@ -39,20 +41,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 +55,13 @@ public boolean canSilkTouch() {
}
@Override
- public BlockColor getColor() { return BlockColor.DIRT_BLOCK_COLOR; }
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item toItem() {
+ int type = this.getDamage() == BigMushroom.STEM ? BigMushroom.ALL_STEM : BigMushroom.ALL_OUTSIDE;
+ return new ItemBlock(Block.get(this.getId(), type), type);
+ }
}
diff --git a/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java b/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java
index 4b24523a5ad..5fc79d09b4f 100644
--- a/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java
+++ b/src/main/java/cn/nukkit/block/BlockHugeMushroomRed.java
@@ -3,8 +3,10 @@
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.level.generator.object.mushroom.BigMushroom;
import cn.nukkit.utils.BlockColor;
+import cn.nukkit.utils.Utils;
/**
* Created by Pub4Game on 28.01.2016.
@@ -39,20 +41,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
@@ -64,4 +58,10 @@ public boolean canSilkTouch() {
public BlockColor getColor() {
return BlockColor.RED_BLOCK_COLOR;
}
+
+ @Override
+ public Item toItem() {
+ int type = this.getDamage() == BigMushroom.STEM ? BigMushroom.ALL_STEM : BigMushroom.ALL_OUTSIDE;
+ return new ItemBlock(Block.get(this.getId(), type), type);
+ }
}
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..4c09f98a76c 100644
--- a/src/main/java/cn/nukkit/block/BlockID.java
+++ b/src/main/java/cn/nukkit/block/BlockID.java
@@ -1,10 +1,15 @@
package cn.nukkit.block;
+/**
+ * List of block IDs
+ */
public interface BlockID {
+
int AIR = 0;
int STONE = 1;
int GRASS = 2;
- int GRASS_BLOCK = 2;
+ @SuppressWarnings("unused")
+ int GRASS_BLOCK = GRASS;
int DIRT = 3;
int COBBLESTONE = 4;
int COBBLE = 4;
@@ -49,6 +54,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 +108,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 +176,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;
@@ -232,7 +236,7 @@ public interface BlockID {
int DARK_OAK_WOODEN_STAIRS = 164;
int SLIME_BLOCK = 165;
int SLIME = 165;
- int GLOW_STICK = 166;
+ //int GLOW_STICK = 166;
int IRON_TRAPDOOR = 167;
int PRISMARINE = 168;
int SEA_LANTERN = 169;
@@ -258,6 +262,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 +274,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 +290,7 @@ public interface BlockID {
int BLOCK_NETHER_WART_BLOCK = 214;
int RED_NETHER_BRICK = 215;
int BONE_BLOCK = 216;
-
+ //
int SHULKER_BOX = 218;
int PURPLE_GLAZED_TERRACOTTA = 219;
int WHITE_GLAZED_TERRACOTTA = 220;
@@ -296,6 +303,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;
@@ -303,20 +311,511 @@ public interface BlockID {
int BLACK_GLAZED_TERRACOTTA = 235;
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;
+ //
+ 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;
+ //
+ 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;
+ //
+ int COPPER_ORE = 566;
+ int LIGHTNING_ROD = 567;
+ int CRAFTER = 568;
+ int VAULT = 569;
+ int TRIAL_SPAWNER = 570;
+ int HEAVY_CORE = 571;
+ 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 CANDLE_CAKE = 684;
+ int WHITE_CANDLE_CAKE = 685;
+ int ORANGE_CANDLE_CAKE = 686;
+ int MAGENTA_CANDLE_CAKE = 687;
+ int LIGHT_BLUE_CANDLE_CAKE = 688;
+ int YELLOW_CANDLE_CAKE = 689;
+ int LIME_CANDLE_CAKE = 690;
+ int PINK_CANDLE_CAKE = 691;
+ int GRAY_CANDLE_CAKE = 692;
+ int LIGHT_GRAY_CANDLE_CAKE = 693;
+ int CYAN_CANDLE_CAKE = 694;
+ int PURPLE_CANDLE_CAKE = 695;
+ int BLUE_CANDLE_CAKE = 696;
+ int BROWN_CANDLE_CAKE = 697;
+ int GREEN_CANDLE_CAKE = 698;
+ int RED_CANDLE_CAKE = 699;
+ int BLACK_CANDLE_CAKE = 700;
+ 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 SCULK = 713;
+ int SCULK_VEIN = 714;
+ int SCULK_CATALYST = 715;
+ int SCULK_SHRIEKER = 716;
+ //
+ int REINFORCED_DEEPSLATE = 721;
+ //
+ int FROG_SPAWN = 723;
+ int PEARLESCENT_FROGLIGHT = 724;
+ int VERDANT_FROGLIGHT = 725;
+ int OCHRE_FROGLIGHT = 726;
+ int MANGROVE_LEAVES = 727;
+ int MUD = 728;
+ int MANGROVE_PROPAGULE = 729;
+ 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;
+ int MANGROVE_ROOTS = 737;
+ int MUDDY_MANGROVE_ROOTS = 738;
+ int MANGROVE_LOG = 739;
+ int STRIPPED_MANGROVE_LOG = 740;
+ int MANGROVE_PLANKS = 741;
+ int MANGROVE_BUTTON = 742;
+ int MANGROVE_STAIRS = 743;
+ int MANGROVE_SLAB = 744;
+ int MANGROVE_PRESSURE_PLATE = 745;
+ int MANGROVE_FENCE = 746;
+ int MANGROVE_FENCE_GATE = 747;
+ int MANGROVE_DOOR = 748;
+ int MANGROVE_STANDING_SIGN = 749;
+ int MANGROVE_WALL_SIGN = 750;
+ int MANGROVE_TRAPDOOR = 751;
+ int MANGROVE_WOOD = 752;
+ int STRIPPED_MANGROVE_WOOD = 753;
+ int MANGROVE_DOUBLE_SLAB = 754;
+ int OAK_HANGING_SIGN = 755;
+ int SPRUCE_HANGING_SIGN = 756;
+ int BIRCH_HANGING_SIGN = 757;
+ int JUNGLE_HANGING_SIGN = 758;
+ int ACACIA_HANGING_SIGN = 759;
+ int DARK_OAK_HANGING_SIGN = 760;
+ int CRIMSON_HANGING_SIGN = 761;
+ int WARPED_HANGING_SIGN = 762;
+ int MANGROVE_HANGING_SIGN = 763;
+ int BAMBOO_MOSAIC = 764;
+ int BAMBOO_PLANKS = 765;
+ int BAMBOO_BUTTON = 766;
+ int BAMBOO_STAIRS = 767;
+ int BAMBOO_SLAB = 768;
+ int BAMBOO_PRESSURE_PLATE = 769;
+ int BAMBOO_FENCE = 770;
+ int BAMBOO_FENCE_GATE = 771;
+ int BAMBOO_DOOR = 772;
+ int BAMBOO_STANDING_SIGN = 773;
+ int BAMBOO_WALL_SIGN = 774;
+ int BAMBOO_TRAPDOOR = 775;
+ int BAMBOO_DOUBLE_SLAB = 776;
+ int BAMBOO_HANGING_SIGN = 777;
+ int BAMBOO_MOSAIC_STAIRS = 778;
+ int BAMBOO_MOSAIC_SLAB = 779;
+ int BAMBOO_MOSAIC_DOUBLE_SLAB = 780;
+ int CHISELED_BOOKSHELF = 781;
+ int BAMBOO_BLOCK = 782;
+ int STRIPPED_BAMBOO_BLOCK = 783;
+ int SUSPICIOUS_SAND = 784;
+ int CHERRY_BUTTON = 785;
+ int CHERRY_DOOR = 786;
+ int CHERRY_FENCE = 787;
+ int CHERRY_FENCE_GATE = 788;
+ int CHERRY_HANGING_SIGN = 789;
+ int STRIPPED_CHERRY_LOG = 790;
+ int CHERRY_LOG = 791;
+ int CHERRY_PLANKS = 792;
+ int CHERRY_PRESSURE_PLATE = 793;
+ int CHERRY_SLAB = 794;
+ int CHERRY_DOUBLE_SLAB = 795;
+ int CHERRY_STAIRS = 796;
+ int CHERRY_STANDING_SIGN = 797;
+ int CHERRY_TRAPDOOR = 798;
+ int CHERRY_WALL_SIGN = 799;
+ int STRIPPED_CHERRY_WOOD = 800;
+ int CHERRY_WOOD = 801;
+ int CHERRY_SAPLING = 802;
+ int CHERRY_LEAVES = 803;
+ int PINK_PETALS = 804;
+ int DECORATED_POT = 806;
+ int TORCHFLOWER_CROP = 822;
+ int TORCHFLOWER = 823;
+ int SUSPICIOUS_GRAVEL = 828;
+ int PITCHER_CROP = 829;
+ int CALIBRATED_SCULK_SENSOR = 835;
+ int SNIFFER_EGG = 851;
+ int PITCHER_PLANT = 867;
+ int TUFF_SLAB = 999;
+ int TUFF_DOUBLE_SLAB = 1000;
+ int TUFF_STAIRS = 1001;
+ int TUFF_WALL = 1002;
+ int POLISHED_TUFF = 1003;
+ int POLISHED_TUFF_SLAB = 1004;
+ int POLISHED_TUFF_DOUBLE_SLAB = 1005;
+ int POLISHED_TUFF_STAIRS = 1006;
+ int POLISHED_TUFF_WALL = 1007;
+ int CHISELED_TUFF = 1008;
+ int TUFF_BRICKS = 1009;
+ int TUFF_BRICK_SLAB = 1010;
+ int TUFF_BRICK_DOUBLE_SLAB = 1011;
+ int TUFF_BRICK_STAIRS = 1012;
+ int TUFF_BRICK_WALL = 1013;
+ int CHISELED_TUFF_BRICKS = 1014;
+ int CHISELED_COPPER = 1015;
+ int EXPOSED_CHISELED_COPPER = 1016;
+ int WEATHERED_CHISELED_COPPER = 1017;
+ int OXIDIZED_CHISELED_COPPER = 1018;
+ int WAXED_CHISELED_COPPER = 1019;
+ int WAXED_EXPOSED_CHISELED_COPPER = 1020;
+ int WAXED_OXIDIZED_CHISELED_COPPER = 1021;
+ int WAXED_WEATHERED_CHISELED_COPPER = 1022;
+ int COPPER_GRATE = 1023;
+ int EXPOSED_COPPER_GRATE = 1024;
+ int WEATHERED_COPPER_GRATE = 1025;
+ int OXIDIZED_COPPER_GRATE = 1026;
+ int WAXED_COPPER_GRATE = 1027;
+ int WAXED_EXPOSED_COPPER_GRATE = 1028;
+ int WAXED_WEATHERED_COPPER_GRATE = 1029;
+ int WAXED_OXIDIZED_COPPER_GRATE = 1030;
+ int COPPER_BULB = 1031;
+ int EXPOSED_COPPER_BULB = 1032;
+ int WEATHERED_COPPER_BULB = 1033;
+ int OXIDIZED_COPPER_BULB = 1034;
+ int WAXED_COPPER_BULB = 1035;
+ int WAXED_EXPOSED_COPPER_BULB = 1036;
+ int WAXED_WEATHERED_COPPER_BULB = 1037;
+ int WAXED_OXIDIZED_COPPER_BULB = 1038;
+ int COPPER_DOOR = 1039;
+ int EXPOSED_COPPER_DOOR = 1040;
+ int WEATHERED_COPPER_DOOR = 1041;
+ int OXIDIZED_COPPER_DOOR = 1042;
+ int WAXED_COPPER_DOOR = 1043;
+ int WAXED_EXPOSED_COPPER_DOOR = 1044;
+ int WAXED_WEATHERED_COPPER_DOOR = 1045;
+ int WAXED_OXIDIZED_COPPER_DOOR = 1046;
+ int COPPER_TRAPDOOR = 1047;
+ int EXPOSED_COPPER_TRAPDOOR = 1048;
+ int WEATHERED_COPPER_TRAPDOOR = 1049;
+ int OXIDIZED_COPPER_TRAPDOOR = 1050;
+ int WAXED_COPPER_TRAPDOOR = 1051;
+ int WAXED_EXPOSED_COPPER_TRAPDOOR = 1052;
+ int WAXED_WEATHERED_COPPER_TRAPDOOR = 1053;
+ int WAXED_OXIDIZED_COPPER_TRAPDOOR = 1054;
}
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..785c4c98481 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;
@@ -12,16 +11,12 @@
import cn.nukkit.network.protocol.LevelEventPacket;
import cn.nukkit.utils.Faceable;
-import java.util.concurrent.ThreadLocalRandom;
-
/**
* 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_BITMASK = 0b0111;
- private final static int MAP_BIT = 0b1000;
+ protected final static int[] FACING = {8, 9, 3, 2, 1, 0};
public BlockItemFrame() {
this(0);
@@ -61,15 +56,21 @@ 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.count--;
}
- 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 +81,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 +98,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;
@@ -111,24 +112,9 @@ public boolean onBreak(Item item) {
return true;
}
- @Override
- public Item[] getDrops(Item item) {
- BlockEntity blockEntity = this.getLevel().getBlockEntity(this);
- BlockEntityItemFrame itemFrame = (BlockEntityItemFrame) blockEntity;
- if (itemFrame != null && ThreadLocalRandom.current().nextFloat() <= itemFrame.getItemDropChance()) {
- return new Item[]{
- toItem(), itemFrame.getItem().clone()
- };
- } else {
- return new Item[]{
- toItem()
- };
- }
- }
-
@Override
public Item toItem() {
- return new ItemItemFrame();
+ return Item.get(Item.ITEM_FRAME);
}
@Override
@@ -153,18 +139,26 @@ public int getComparatorInputOverride() {
}
public BlockFace getFacing() {
- switch (this.getDamage() & FACING_BITMASK) {
+ switch (this.getDamage()) {
case 0:
+ case 4:
return BlockFace.WEST;
case 1:
+ case 5:
return BlockFace.EAST;
case 2:
+ case 6:
return BlockFace.NORTH;
case 3:
+ case 7:
return BlockFace.SOUTH;
+ case 8:
+ return BlockFace.UP;
+ case 9:
+ return BlockFace.DOWN;
}
- return null;
+ return BlockFace.UP;
}
@Override
@@ -172,13 +166,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..f3d290ffc19
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockItemFrameGlow.java
@@ -0,0 +1,62 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.blockentity.BlockEntity;
+import cn.nukkit.item.Item;
+import cn.nukkit.math.BlockFace;
+import cn.nukkit.nbt.tag.CompoundTag;
+import cn.nukkit.nbt.tag.Tag;
+import cn.nukkit.network.protocol.LevelEventPacket;
+
+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);
+ }
+
+ @Override
+ public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (target.isSolid() && (!block.isSolid() || block.canBeReplaced())) {
+ this.setDamage(FACING[face.getIndex()]);
+
+ this.getLevel().setBlock(this, this, true, true);
+
+ CompoundTag nbt = new CompoundTag()
+ .putString("id", BlockEntity.GLOW_ITEM_FRAME)
+ .putInt("x", (int) block.x)
+ .putInt("y", (int) block.y)
+ .putInt("z", (int) block.z)
+ .putByte("ItemRotation", 0)
+ .putFloat("ItemDropChance", 1.0f);
+ if (item.hasCustomBlockData()) {
+ for (Tag aTag : item.getCustomBlockData().getAllTags()) {
+ nbt.put(aTag.getName(), aTag);
+ }
+ }
+ BlockEntity.createBlockEntity(BlockEntity.GLOW_ITEM_FRAME, this.getChunk(), nbt);
+ this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_ITEM_FRAME_PLACED);
+ return true;
+ }
+ return false;
+ }
+}
+
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..9fadda9c14c 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,18 @@ 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) {
+ 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);
+
+ item.count--;
+ return true;
+ }
}
return false;
@@ -61,26 +86,27 @@ 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 (super.place(item, block, target, face, fx, fy, fz, player)) {
- createBlockEntity();
+ CompoundTag nbt = new CompoundTag()
+ .putList(new ListTag<>("Items"))
+ .putString("id", BlockEntity.JUKEBOX)
+ .putInt("x", getFloorX())
+ .putInt("y", getFloorY())
+ .putInt("z", getFloorZ());
+
+ BlockEntity.createBlockEntity(BlockEntity.JUKEBOX, this.getChunk(), nbt);
return true;
}
return false;
}
- private BlockEntity createBlockEntity() {
- CompoundTag nbt = new CompoundTag()
- .putList(new ListTag<>("Items"))
- .putString("id", BlockEntity.JUKEBOX)
- .putInt("x", getFloorX())
- .putInt("y", getFloorY())
- .putInt("z", getFloorZ());
-
- return BlockEntity.createBlockEntity(BlockEntity.JUKEBOX, this.level.getChunk(getFloorX() >> 4, getFloorZ() >> 4), nbt);
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.WOOD_BLOCK_COLOR;
}
@Override
- public BlockColor getColor() {
- return BlockColor.DIRT_BLOCK_COLOR;
+ 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..13411b8a321
--- /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 (!Block.isWater(blockIdAbove)) {
+ 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..0862d813a48 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,15 +146,7 @@ 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()) {
+ 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..6f7658f9757
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLantern.java
@@ -0,0 +1,163 @@
+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 || support instanceof BlockGlass || support instanceof BlockGlassPane) {
+ return true;
+ }
+ if (support instanceof BlockTrapdoor) {
+ return !((BlockTrapdoor) support).isTop() && !((BlockTrapdoor) support).isOpen();
+ }
+ 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();
+ return !down.isTransparent() || down.isNarrowSurface() || Block.canStayOnFullSolid(down);
+ }
+
+ @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..69b962b1cf3 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;
@@ -48,15 +49,16 @@ public int getToolType() {
return ItemTool.TYPE_HOE;
}
+ private static final String[] NAMES = {
+ "Oak Leaves",
+ "Spruce Leaves",
+ "Birch Leaves",
+ "Jungle Leaves"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Oak Leaves",
- "Spruce Leaves",
- "Birch Leaves",
- "Jungle Leaves"
- };
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@Override
@@ -71,8 +73,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 +90,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 +117,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())) {
+ 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,16 +134,20 @@ public int onUpdate(int type) {
return 0;
}
- private Boolean findLog(Block pos, LongSet visited, Integer distance, Integer check) {
- return findLog(pos, visited, distance, check, null);
+ protected void setOnDecayDamage() {
+ setDamage(getDamage() & 0x03);
}
- private Boolean findLog(Block pos, LongSet visited, Integer distance, Integer check, BlockFace fromSide) {
+ private boolean findLog(Block pos, LongSet visited) {
+ return findLog(pos, visited, 0, 0, null);
+ }
+
+ private boolean findLog(Block pos, LongSet visited, Integer distance, Integer check, BlockFace fromSide) {
++check;
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 +206,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 +218,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 +239,14 @@ 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..543773a855d 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,11 +25,7 @@ public BlockLeaves2(int meta) {
}
public String getName() {
- String[] names = new String[]{
- "Acacia Leaves",
- "Dark Oak Leaves"
- };
- return names[this.getDamage() & 0x01];
+ return NAMES[this.getDamage() & 0x01];
}
@Override
@@ -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/BlockLeavesMangrove.java b/src/main/java/cn/nukkit/block/BlockLeavesMangrove.java
new file mode 100644
index 00000000000..c053a9b1672
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLeavesMangrove.java
@@ -0,0 +1,69 @@
+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 BlockLeavesMangrove extends BlockLeaves {
+
+ public BlockLeavesMangrove() {
+ this(0);
+ }
+
+ public BlockLeavesMangrove(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return MANGROVE_LEAVES;
+ }
+
+ @Override
+ public String getName() {
+ return "Mangrove 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(Block.get(this.getId(), 0), 0);
+ }
+
+ @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/BlockLectern.java b/src/main/java/cn/nukkit/block/BlockLectern.java
new file mode 100644
index 00000000000..a5c6e1f8246
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockLectern.java
@@ -0,0 +1,170 @@
+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 BlockTransparentMeta 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.isCreative()) {
+ item.count--;
+ }
+ 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..b0b66f9efb8 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;
}
@@ -82,7 +86,7 @@ public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
int face = this.isPowerOn() ? this.getDamage() ^ 0x08 : this.getDamage();
BlockFace faces = LeverOrientation.byMetadata(face).getFacing().getOpposite();
- if (!this.getSide(faces).isSolid()) {
+ if (!isSupportValid(this.getSide(faces))) {
this.level.useBreakOn(this);
}
}
@@ -91,12 +95,12 @@ public int onUpdate(int type) {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (target.isNormalBlock()) {
- this.setDamage(LeverOrientation.forFacings(face, player.getHorizontalFacing()).getMetadata());
- this.getLevel().setBlock(block, this, true, true);
- return true;
+ LeverOrientation faces = LeverOrientation.forFacings(face, player.getHorizontalFacing());
+ this.setDamage(faces.getMetadata());
+ if (!isSupportValid(this.getSide(faces.facing.getOpposite()))) {
+ return false;
}
- return false;
+ return this.getLevel().setBlock(block, this, true, true);
}
@Override
@@ -104,19 +108,33 @@ 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;
}
+ private boolean isSupportValid(Block block) {
+ if (!block.isTransparent()) {
+ return true;
+ }
+ if (BlockFace.fromIndex(isPowerOn() ? getDamage() ^ 0x08 : getDamage()) == BlockFace.DOWN) {
+ return Block.canStayOnFullSolid(block);
+ }
+ return Block.canConnectToFullSolid(block);
+ }
+
@Override
public int getWeakPower(BlockFace side) {
return isPowerOn() ? 15 : 0;
}
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 +232,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..c3d885efe68
--- /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..da9dfa3306d 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,15 +175,17 @@ 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))) {
- vector = vector.normalize().add(0, -6, 0);
+ 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();
+ vector.y -= 6;
}
}
return this.flowVector = vector.normalize();
@@ -195,32 +209,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 +264,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 +272,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 +284,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 +304,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 +345,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 +373,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 +391,8 @@ public double getResistance() {
return 500;
}
- private boolean[] getOptimalFlowDirections() {
- int[] flowCost = new int[]{
+ protected boolean[] getOptimalFlowDirections() {
+ int[] flowCost = {
1000,
1000,
1000,
@@ -365,14 +412,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 +457,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 +476,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 +495,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..f01e16f3d30
--- /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/BlockMangrovePropagule.java b/src/main/java/cn/nukkit/block/BlockMangrovePropagule.java
new file mode 100644
index 00000000000..444ff0b5b5b
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMangrovePropagule.java
@@ -0,0 +1,45 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+
+public class BlockMangrovePropagule extends BlockSapling {
+
+ public BlockMangrovePropagule() {
+ this(0);
+ }
+
+ public BlockMangrovePropagule(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return MANGROVE_PROPAGULE;
+ }
+
+ @Override
+ public String getName() {
+ return "Mangrove Propagule";
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @Override
+ public boolean canBeFlowedInto() {
+ return false;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.FLOW_INTO_BLOCK;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMangroveRoots.java b/src/main/java/cn/nukkit/block/BlockMangroveRoots.java
new file mode 100644
index 00000000000..dab9e321ab8
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMangroveRoots.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.ItemTool;
+
+public class BlockMangroveRoots extends BlockTransparent {
+
+ @Override
+ public String getName() {
+ return "Mangrove Roots";
+ }
+
+ @Override
+ public int getId() {
+ return MANGROVE_ROOTS;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.7;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.7;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_AXE;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public int getBurnChance() {
+ return 30;
+ }
+
+ @Override
+ public int getBurnAbility() {
+ return 60;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockMangroveRootsMuddy.java b/src/main/java/cn/nukkit/block/BlockMangroveRootsMuddy.java
new file mode 100644
index 00000000000..1e36ffbdf1e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMangroveRootsMuddy.java
@@ -0,0 +1,38 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+
+public class BlockMangroveRootsMuddy extends BlockMangroveRoots {
+
+ @Override
+ public String getName() {
+ return "Muddy Mangrove Roots";
+ }
+
+ @Override
+ public int getId() {
+ return MUDDY_MANGROVE_ROOTS;
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_SHOVEL;
+ }
+
+ @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/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..adf172b52e4 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",
@@ -34,12 +35,12 @@ public int getId() {
@Override
public double getHardness() {
- return 0.75;
+ return 1;
}
@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..82593686f9f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockMoss.java
@@ -0,0 +1,118 @@
+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.item.ItemTool;
+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];
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+}
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..5ff2af6a8e0 100644
--- a/src/main/java/cn/nukkit/block/BlockMushroom.java
+++ b/src/main/java/cn/nukkit/block/BlockMushroom.java
@@ -1,10 +1,9 @@
package cn.nukkit.block;
import cn.nukkit.Player;
-import cn.nukkit.event.level.StructureGrowEvent;
import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
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 +37,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 || block.level.isBlockWaterloggedAt(block.getChunk(), (int) block.x, (int) block.y, (int) block.z)) {
+ return false;
+ }
+
if (canStay()) {
getLevel().setBlock(block, this, true, true);
return true;
@@ -53,7 +56,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 +75,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 +85,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 +99,14 @@ public boolean canSilkTouch() {
}
protected abstract int getType();
+
+ @Override
+ public boolean breakWhenPushed() {
+ return true;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
}
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..502cd7baad2 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,9 +40,9 @@ 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 block = this.clone();
block.setDamage(block.getDamage() + 1);
BlockGrowEvent ev = new BlockGrowEvent(this, block);
Server.getInstance().getPluginManager().callEvent(ev);
@@ -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..848a42d920f 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,9 @@ public Instrument getInstrument() {
}
public void emitSound() {
- if (this.up().getId() != AIR) return;
+ if (this.level.getBlockIdAt((int) this.x, (int) this.y + 1, (int) this.z) != AIR) {
+ return;
+ }
Instrument instrument = this.getInstrument();
@@ -222,11 +222,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.sneakToBlockInteract()) return false;
this.increaseStrength();
this.emitSound();
return true;
@@ -258,11 +259,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 +292,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..66ea91af066 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,54 @@ 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 (event.isCancelled()) {
+ return false;
+ }
- if (!calculator.canMove()) {
- return false;
- } else {
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);
+ Block pistonHead = null;
+ if (extending) {
+ pistonHead = this.getSide(direction);
+ if (!pistonHead.canBePushed()) {
+ return false; // TODO: figure out why this happens
+ }
+ }
+ List newBlocks = new ArrayList<>(blocks);
List destroyBlocks = calculator.getBlocksToDestroy();
BlockFace side = extending ? direction : direction.getOpposite();
@@ -201,43 +242,35 @@ 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()));
+ if (pistonHead != null) {
+ 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 +278,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 +293,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 +319,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 +348,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)) {
@@ -312,12 +360,16 @@ private boolean addBlockLine(Block origin) {
if (count + this.toMove.size() > 12) {
return false;
}
- }
+ }*/
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,9 +385,9 @@ 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;
- }
+ }*/
}
return true;
@@ -349,8 +401,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;
}
@@ -376,7 +437,7 @@ private void reorderListAtCollision(int count, int index) {
this.toMove.addAll(list2);
}
- private boolean addBranchingBlocks(Block block) {
+ /*private boolean addBranchingBlocks(Block block) {
for (BlockFace face : BlockFace.values()) {
if (face.getAxis() != this.moveDirection.getAxis() && !this.addBlockLine(block.getSide(face))) {
return false;
@@ -384,7 +445,7 @@ private boolean addBranchingBlocks(Block block) {
}
return true;
- }
+ }*/
public List getBlocksToMove() {
return this.toMove;
@@ -397,7 +458,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..71984c11667 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);
}
@@ -49,18 +49,18 @@ public int getBurnAbility() {
return 20;
}
+ private static final String[] NAMES = {
+ "Oak Planks",
+ "Spruce Planks",
+ "Birch Planks",
+ "Jungle Planks",
+ "Acacia Planks",
+ "Dark Oak Planks",
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Oak Wood Planks",
- "Spruce Wood Planks",
- "Birch Wood Planks",
- "Jungle Wood Planks",
- "Acacia Wood Planks",
- "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..1dba4d7c260
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockPointedDripstone.java
@@ -0,0 +1,287 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.block.properties.BlockPropertiesHelper;
+import cn.nukkit.block.properties.DripstoneThickness;
+import cn.nukkit.block.custom.properties.BlockProperties;
+import cn.nukkit.block.custom.properties.BooleanBlockProperty;
+import cn.nukkit.block.custom.properties.EnumBlockProperty;
+import cn.nukkit.entity.Entity;
+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;
+ }
+
+
+ Block tip = null;
+ if (up instanceof BlockPointedDripstone && hanging) {
+ tip = up;
+ } else if (down instanceof BlockPointedDripstone) {
+ tip = 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() {
+ // TODO: grow from ground too
+ return this.down().getId() == AIR && this.up().getId() == DRIPSTONE_BLOCK && Block.isWater(this.up(2).getId());
+ }
+
+ 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());
+
+ Entity.createEntity(EntityFallingBlock.NETWORK_ID, this.getLevel().getChunk((int) this.x >> 4, (int) this.z >> 4), nbt).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..182dc990659 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
@@ -85,8 +74,7 @@ public boolean isActivated() {
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
- Block down = this.down();
- if (down.isTransparent() && !(down instanceof BlockFence)) {
+ if (!isSupportValid(this.down())) {
this.level.useBreakOn(this, Item.get(Item.WOODEN_PICKAXE));
}
} else if (type == Level.BLOCK_UPDATE_SCHEDULED) {
@@ -102,15 +90,18 @@ 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) {
- Block down = block.down();
- if (down.isTransparent() && !(down instanceof BlockFence)) {
+ if (!isSupportValid(this.down())) {
return false;
}
- this.level.setBlock(block, this, true, true);
+ this.getLevel().setBlock(this, this, true, true);
return true;
}
+ private static boolean isSupportValid(Block block) {
+ return !block.isTransparent() || block.isNarrowSurface() || Block.canStayOnFullSolid(block);
+ }
+
@Override
protected AxisAlignedBB recalculateCollisionBoundingBox() {
return new SimpleAxisAlignedBB(this.x + 0.125, this.y, this.z + 0.125, this.x + 0.875, this.y + 0.25, this.z + 0.875D);
@@ -147,7 +138,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 +160,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,17 +185,22 @@ 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();
@Override
public Item toItem() {
- return new ItemBlock(this, 0, 1);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @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/BlockPressurePlateWood.java b/src/main/java/cn/nukkit/block/BlockPressurePlateWood.java
index 7588c91f9a9..a792424dcb5 100644
--- a/src/main/java/cn/nukkit/block/BlockPressurePlateWood.java
+++ b/src/main/java/cn/nukkit/block/BlockPressurePlateWood.java
@@ -23,7 +23,7 @@ public BlockPressurePlateWood() {
@Override
public String getName() {
- return "Wooden Pressure Plate";
+ return "Oak Pressure Plate";
}
@Override
@@ -38,12 +38,12 @@ public int getToolType() {
@Override
public double getHardness() {
- return 0.5D;
+ return 0.1D;
}
@Override
public double getResistance() {
- return 2.5D;
+ return 0.5D;
}
@Override
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..782b5b24a93 100644
--- a/src/main/java/cn/nukkit/block/BlockPurpur.java
+++ b/src/main/java/cn/nukkit/block/BlockPurpur.java
@@ -12,24 +12,33 @@ 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) {
super(meta);
}
+ private static final String[] NAMES = {
+ "Purpur Block",
+ "",
+ "Purpur Pillar",
+ ""
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Purpur Block",
- "",
- "Purpur Pillar",
- ""
- };
-
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@Override
@@ -55,16 +64,7 @@ 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.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..37d0eb608fe 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 {
@@ -16,8 +16,16 @@ public class BlockQuartz extends BlockSolidMeta {
public static final int QUARTZ_NORMAL = 0;
public static final int QUARTZ_CHISELED = 1;
public static final int QUARTZ_PILLAR = 2;
- public static final int QUARTZ_PILLAR2 = 3;
+ public static final int QUARTZ_SMOOTH = 3;
+ private static final short[] FACES = {
+ 0,
+ 0,
+ 0b1000,
+ 0b1000,
+ 0b0100,
+ 0b0100
+ };
public BlockQuartz() {
this(0);
@@ -42,31 +50,22 @@ public double getResistance() {
return 4;
}
+ private static final String[] NAMES = {
+ "Block of Quartz",
+ "Chiseled Quartz Block",
+ "Quartz Pillar Block",
+ "Smooth Quartz Block"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Quartz Block",
- "Chiseled Quartz Block",
- "Quartz Pillar",
- "Quartz Pillar"
- };
-
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@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.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..8cc7a77c437 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 (!canStayOnFullSolid(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 (!canStayOnFullSolid(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..fb96d19a355 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;
@@ -145,12 +141,12 @@ protected boolean checkSurrounding(Vector3 pos, boolean relative, int power) {
return false;
}
- return canPowered(new Vector3(dx, dy, dz), base, power, relative)
- || onStraight && canPowered(new Vector3(dx, dy - 1, dz), base, power, relative);
+ return canPowered(dx, dy, dz, base, power, relative)
+ || onStraight && canPowered(dx, dy - 1, dz, base, power, relative);
}
- protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boolean relative) {
- Block block = level.getBlock(pos);
+ protected boolean canPowered(int x, int y, int z, Rail.Orientation state, int power, boolean relative) {
+ Block block = level.getBlock(x, y, z);
if (!(block instanceof BlockRailActivator)) {
return false;
@@ -166,18 +162,23 @@ protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boo
|| base != Rail.Orientation.STRAIGHT_EAST_WEST
&& base != Rail.Orientation.ASCENDING_EAST
&& base != Rail.Orientation.ASCENDING_WEST)
- && (level.isBlockPowered(pos) || checkSurrounding(pos, relative, power + 1));
+ && (level.isBlockPowered(block) || checkSurrounding(block, 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..f28c6cd6555 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;
@@ -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..4533505baaa 100644
--- a/src/main/java/cn/nukkit/block/BlockRailPowered.java
+++ b/src/main/java/cn/nukkit/block/BlockRailPowered.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,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)) {
@@ -155,12 +152,12 @@ protected boolean checkSurrounding(Vector3 pos, boolean relative, int power) {
return false;
}
// Next check the if rail is on power state
- return canPowered(new Vector3(dx, dy, dz), base, power, relative)
- || onStraight && canPowered(new Vector3(dx, dy - 1, dz), base, power, relative);
+ return canPowered(dx, dy, dz, base, power, relative)
+ || onStraight && canPowered(dx, dy - 1, dz, base, power, relative);
}
- protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boolean relative) {
- Block block = level.getBlock(pos);
+ protected boolean canPowered(int x, int y, int z, Rail.Orientation state, int power, boolean relative) {
+ Block block = level.getBlock(x, y, z);
// What! My block is air??!! Impossible! XD
if (!(block instanceof BlockRailPowered)) {
return false;
@@ -179,13 +176,18 @@ protected boolean canPowered(Vector3 pos, Rail.Orientation state, int power, boo
|| base != Rail.Orientation.STRAIGHT_EAST_WEST
&& base != Rail.Orientation.ASCENDING_EAST
&& base != Rail.Orientation.ASCENDING_WEST)
- && (level.isBlockPowered(pos) || checkSurrounding(pos, relative, power + 1));
+ && (level.isBlockPowered(block) || checkSurrounding(block, 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..152ade0d0d1 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;
/**
@@ -23,21 +22,21 @@ public int getId() {
return RED_SANDSTONE;
}
+ private static final String[] NAMES = {
+ "Red Sandstone",
+ "Chiseled Red Sandstone",
+ "Cut Red Sandstone",
+ "Smooth Red Sandstone"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Red Sandstone",
- "Chiseled Red Sandstone",
- "Smooth Red Sandstone",
- ""
- };
-
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@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..ec2f96e0cd7 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 || block.level.isBlockWaterloggedAt(block.getChunk(), (int) block.x, (int) block.y, (int) block.z)) {
+ 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/BlockReinforcedDeeplsate.java b/src/main/java/cn/nukkit/block/BlockReinforcedDeeplsate.java
new file mode 100644
index 00000000000..d8d52b2fd5e
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockReinforcedDeeplsate.java
@@ -0,0 +1,42 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.utils.BlockColor;
+
+public class BlockReinforcedDeeplsate extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return REINFORCED_DEEPSLATE;
+ }
+
+ @Override
+ public String getName() {
+ return "Reinforced Deeplsate";
+ }
+
+ @Override
+ public double getHardness() {
+ return 55;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1200;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.DEEPSLATE_GRAY;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ return new Item[0];
+ }
+}
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..430bedc6a76
--- /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 true;
+ }
+
+ 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 item.getId() == GLOWSTONE;
+ }
+
+ @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..6c3b4f67ebc 100644
--- a/src/main/java/cn/nukkit/block/BlockSandstone.java
+++ b/src/main/java/cn/nukkit/block/BlockSandstone.java
@@ -6,13 +6,15 @@
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;
+ public static final int CUT = 2;
+ public static final int SMOOTH = 3;
public BlockSandstone() {
this(0);
@@ -29,29 +31,29 @@ public int getId() {
@Override
public double getHardness() {
- return 0.8;
+ return 2;
}
@Override
public double getResistance() {
- return 4;
+ return 6;
}
+ private static final String[] NAMES = {
+ "Sandstone",
+ "Chiseled Sandstone",
+ "Cut Sandstone",
+ "Smooth Sandstone"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Sandstone",
- "Chiseled Sandstone",
- "Smooth Sandstone",
- ""
- };
-
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@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..0bd6f6da2c4 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);
@@ -48,26 +46,32 @@ public int getId() {
return SAPLING;
}
+ private static final String[] NAMES = {
+ "Oak Sapling",
+ "Spruce Sapling",
+ "Birch Sapling",
+ "Jungle Sapling",
+ "Acacia Sapling",
+ "Dark Oak Sapling",
+ "",
+ ""
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Oak Sapling",
- "Spruce Sapling",
- "Birch Sapling",
- "Jungle Sapling",
- "Acacia Sapling",
- "Dark Oak Sapling",
- "",
- ""
- };
- return names[this.getDamage() & 0x07];
+ return NAMES[this.getDamage() & 0x07];
}
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
+ if (!(this instanceof BlockMangrovePropagule) &&
+ (block instanceof BlockWater || block.level.isBlockWaterloggedAt(block.getChunk(), (int) block.x, (int) block.y, (int) block.z))) {
+ return false;
+ }
+
Block down = this.down();
int id = down.getId();
- if (id == Block.GRASS || id == Block.DIRT || id == Block.FARMLAND || id == Block.PODZOL || id == MYCELIUM) {
+ if (id == Block.GRASS || id == Block.DIRT || id == Block.FARMLAND || id == Block.PODZOL || id == MYCELIUM || id == MOSS_BLOCK || id == MUD) {
this.getLevel().setBlock(block, this, true, true);
return true;
}
@@ -81,8 +85,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 +95,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 +118,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 +133,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.25f, 4);
+ 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 +194,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 +239,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/BlockSculk.java b/src/main/java/cn/nukkit/block/BlockSculk.java
new file mode 100644
index 00000000000..bb4ac659f42
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSculk.java
@@ -0,0 +1,64 @@
+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;
+
+public class BlockSculk extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return SCULK;
+ }
+
+ @Override
+ public String getName() {
+ return "Sculk";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1.5;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 1;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{
+ this.toItem()
+ };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public int getDropExp() {
+ return 1;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSculkCatalyst.java b/src/main/java/cn/nukkit/block/BlockSculkCatalyst.java
new file mode 100644
index 00000000000..d87a5ecb077
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSculkCatalyst.java
@@ -0,0 +1,69 @@
+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;
+
+public class BlockSculkCatalyst extends BlockSolid {
+
+ @Override
+ public int getId() {
+ return SCULK_CATALYST;
+ }
+
+ @Override
+ public String getName() {
+ return "Sculk Catalyst";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 6;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{
+ this.toItem()
+ };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public int getDropExp() {
+ return 5;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSculkSensor.java b/src/main/java/cn/nukkit/block/BlockSculkSensor.java
new file mode 100644
index 00000000000..5936625c55f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSculkSensor.java
@@ -0,0 +1,74 @@
+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;
+
+public class BlockSculkSensor extends BlockTransparent {
+
+ @Override
+ public int getId() {
+ return SCULK_SENSOR;
+ }
+
+ @Override
+ public String getName() {
+ return "Sculk Sensor";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 1.5;
+ }
+
+ @Override
+ public double getResistance() {
+ return 1.5;
+ }
+
+ @Override
+ public int getLightLevel() {
+ return 1;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{
+ this.toItem()
+ };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public int getDropExp() {
+ return 5;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSculkShrieker.java b/src/main/java/cn/nukkit/block/BlockSculkShrieker.java
new file mode 100644
index 00000000000..4790bcfd206
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSculkShrieker.java
@@ -0,0 +1,69 @@
+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;
+
+public class BlockSculkShrieker extends BlockTransparent {
+
+ @Override
+ public int getId() {
+ return SCULK_SHRIEKER;
+ }
+
+ @Override
+ public String getName() {
+ return "Sculk Shrieker";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 3;
+ }
+
+ @Override
+ public double getResistance() {
+ return 3;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public boolean canBePushed() {
+ return false;
+ }
+
+ @Override
+ public BlockColor getColor() {
+ return BlockColor.BLACK_BLOCK_COLOR;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{
+ this.toItem()
+ };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public int getDropExp() {
+ return 5;
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/BlockSculkVein.java b/src/main/java/cn/nukkit/block/BlockSculkVein.java
new file mode 100644
index 00000000000..4487d0a858c
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSculkVein.java
@@ -0,0 +1,77 @@
+package cn.nukkit.block;
+
+import cn.nukkit.item.Item;
+import cn.nukkit.item.ItemBlock;
+import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.enchantment.Enchantment;
+
+public class BlockSculkVein extends BlockTransparentMeta {
+
+ public BlockSculkVein() {
+ this(0);
+ }
+
+ public BlockSculkVein(int meta) {
+ super(meta);
+ }
+
+ @Override
+ public int getId() {
+ return SCULK_VEIN;
+ }
+
+ @Override
+ public String getName() {
+ return "Sculk Vein";
+ }
+
+ @Override
+ public int getToolType() {
+ return ItemTool.TYPE_HOE;
+ }
+
+ @Override
+ public double getHardness() {
+ return 0.2;
+ }
+
+ @Override
+ public double getResistance() {
+ return 0.2;
+ }
+
+ @Override
+ public boolean canHarvestWithHand() {
+ return false;
+ }
+
+ @Override
+ public Item[] getDrops(Item item) {
+ if (item.hasEnchantment(Enchantment.ID_SILK_TOUCH)) {
+ return new Item[]{
+ this.toItem()
+ };
+ }
+ return new Item[0];
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
+ }
+
+ @Override
+ public boolean canPassThrough() {
+ return true;
+ }
+
+ @Override
+ public boolean isSolid() {
+ return false;
+ }
+
+ @Override
+ public Item toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+}
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..16dc90bbf00
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockSeaPickle.java
@@ -0,0 +1,187 @@
+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));
+ level.getServer().getPluginManager().callEvent(event);
+ 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..d7d150ec66c 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.BaseInventory;
+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;
+
+ BaseInventory i = t.getInventory();
+
+ 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..dad661f8fce 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,18 +22,40 @@ 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
public double getHardness() {
- return 2;
+ return 3;
}
@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,39 @@ 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
+
+ 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..44edf725534 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) {
@@ -26,25 +25,25 @@ public int getId() {
return RED_SANDSTONE_SLAB;
}
+ private static final String[] NAMES = {
+ "Red Sandstone",
+ "Purpur",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Red Sandstone",
- "Purpur",
- "",
- "",
- "",
- "",
- "",
- ""
- };
-
- return ((this.getDamage() & 0x08) > 0 ? "Upper " : "") + names[this.getDamage() & 0x07] + " Slab";
+ return ((this.getDamage() & 0x08) > 0 ? "Upper " : "") + NAMES[this.getDamage() & 0x07] + " Slab";
}
@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..07369ac664e 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
@@ -31,25 +36,25 @@ public int getId() {
return STONE_SLAB;
}
+ private static final String[] NAMES = {
+ "Stone",
+ "Sandstone",
+ "Oak",
+ "Cobblestone",
+ "Brick",
+ "Stone Brick",
+ "Quartz",
+ "Nether Brick"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Stone",
- "Sandstone",
- "Wooden",
- "Cobblestone",
- "Brick",
- "Stone Brick",
- "Quartz",
- "Nether Brick"
- };
-
- return ((this.getDamage() & 0x08) > 0 ? "Upper " : "") + names[this.getDamage() & 0x07] + " Slab";
+ return ((this.getDamage() & 0x08) > 0 ? "Upper " : "") + NAMES[this.getDamage() & 0x07] + " Slab";
}
@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..2ad845f5639
--- /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 = {
+ "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..1e0e11ace9c
--- /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 = {
+ "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..374275411fc 100644
--- a/src/main/java/cn/nukkit/block/BlockSlabWood.java
+++ b/src/main/java/cn/nukkit/block/BlockSlabWood.java
@@ -19,19 +19,20 @@ public BlockSlabWood(int meta) {
super(meta, DOUBLE_WOODEN_SLAB);
}
+ private static final String[] NAMES = {
+ "Oak",
+ "Spruce",
+ "Birch",
+ "Jungle",
+ "Acacia",
+ "Dark Oak",
+ "",
+ ""
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Oak",
- "Spruce",
- "Birch",
- "Jungle",
- "Acacia",
- "Dark Oak",
- "",
- ""
- };
- return (((this.getDamage() & 0x08) == 0x08) ? "Upper " : "") + names[this.getDamage() & 0x07] + " Wooden Slab";
+ return (((this.getDamage() & 0x08) == 0x08) ? "Upper " : "") + NAMES[this.getDamage() & 0x07] + " Slab";
}
@Override
@@ -63,12 +64,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..aad7f8d08bc
--- /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..331b60d6066 100644
--- a/src/main/java/cn/nukkit/block/BlockSponge.java
+++ b/src/main/java/cn/nukkit/block/BlockSponge.java
@@ -3,26 +3,27 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
import cn.nukkit.item.ItemTool;
-import cn.nukkit.level.GlobalBlockPalette;
import cn.nukkit.level.Level;
-import cn.nukkit.level.particle.SmokeParticle;
+import cn.nukkit.level.particle.DestroyBlockParticle;
+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.Map;
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,33 +58,26 @@ 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++) {
- LevelEventPacket packet = new LevelEventPacket();
- packet.evid = LevelEventPacket.EVENT_PARTICLE_DESTROY;
- packet.x = (float) block.getX() + 0.5f;
- packet.y = (float) block.getY() + 1f;
- packet.z = (float) block.getZ() + 0.5f;
- packet.data = GlobalBlockPalette.getOrCreateRuntimeId(BlockID.WATER, 0);
- level.addChunkPacket(getChunkX(), getChunkZ(), packet);
- }
-
+ Map players = this.level.getChunkPlayers(block.getChunkX(), block.getChunkZ());
+ level.addParticle(new DestroyBlockParticle(block.add(0.5, 0.5, 0.5), Block.get(BlockID.WATER)), players.values().toArray(new Player[0]));
return true;
}
@@ -96,6 +85,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 +106,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.isWater(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..c602bac4b27
--- /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.canConnectToFullSolid(this.up())) {
+ 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 && !Block.canConnectToFullSolid(this.up())) {
+ 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..16739e52034 100644
--- a/src/main/java/cn/nukkit/block/BlockStairs.java
+++ b/src/main/java/cn/nukkit/block/BlockStairs.java
@@ -2,17 +2,19 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemTool;
+import cn.nukkit.item.ItemBlock;
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 +23,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]);
+ 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];
@@ -55,9 +56,7 @@ public Item[] getDrops(Item item) {
@Override
public Item toItem() {
- Item item = super.toItem();
- item.setDamage(0);
- return item;
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
@@ -88,49 +87,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 +131,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..0c9a950a771 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 {
@@ -23,12 +23,11 @@ public int getId() {
@Override
public String getName() {
- return "Acacia Wood Stairs";
+ return "Acacia Stairs";
}
@Override
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..05d9181ba05 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsBirch.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsBirch.java
@@ -23,12 +23,11 @@ public int getId() {
@Override
public String getName() {
- return "Birch Wood Stairs";
+ return "Birch Stairs";
}
@Override
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..da3f92cfc17 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsDarkOak.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsDarkOak.java
@@ -23,12 +23,11 @@ public int getId() {
@Override
public String getName() {
- return "Dark Oak Wood Stairs";
+ return "Dark Oak Stairs";
}
@Override
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..987ca37dc7a 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsJungle.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsJungle.java
@@ -23,12 +23,11 @@ public int getId() {
@Override
public String getName() {
- return "Jungle Wood Stairs";
+ return "Jungle Stairs";
}
@Override
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..68f12d0e88a 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsSpruce.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsSpruce.java
@@ -23,12 +23,11 @@ public int getId() {
@Override
public String getName() {
- return "Spruce Wood Stairs";
+ return "Spruce Stairs";
}
@Override
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..5b5a1425cf9 100644
--- a/src/main/java/cn/nukkit/block/BlockStairsWood.java
+++ b/src/main/java/cn/nukkit/block/BlockStairsWood.java
@@ -1,7 +1,6 @@
package cn.nukkit.block;
import cn.nukkit.item.Item;
-import cn.nukkit.item.ItemBlock;
import cn.nukkit.item.ItemTool;
import cn.nukkit.utils.BlockColor;
@@ -10,6 +9,7 @@
* Package cn.nukkit.block in project Nukkit .
*/
public class BlockStairsWood extends BlockStairs {
+
public BlockStairsWood() {
this(0);
}
@@ -33,11 +33,6 @@ public int getToolType() {
return ItemTool.TYPE_AXE;
}
- @Override
- public Item toItem() {
- return new ItemBlock(this, 0);
- }
-
@Override
public double getHardness() {
return 2;
@@ -62,6 +57,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..24c19b134ba
--- /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 = {
+ 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..568b3577624 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];
+ 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..479b1d4fd0c
--- /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..8fe6f21092e 100644
--- a/src/main/java/cn/nukkit/block/BlockTNT.java
+++ b/src/main/java/cn/nukkit/block/BlockTNT.java
@@ -2,15 +2,17 @@
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.enchantment.Enchantment;
import cn.nukkit.level.Level;
+import cn.nukkit.level.Sound;
import cn.nukkit.math.NukkitRandom;
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;
/**
@@ -19,9 +21,6 @@
*/
public class BlockTNT extends BlockSolid {
- public BlockTNT() {
- }
-
@Override
public String getName() {
return "TNT";
@@ -65,9 +64,11 @@ public void prime(int fuse) {
prime(fuse, null);
}
+ private static final NukkitRandom RANDOM = new NukkitRandom();
+
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 = RANDOM.nextSignedFloat() * 6.283185307179586;
CompoundTag nbt = new CompoundTag()
.putList(new ListTag("Pos")
.add(new DoubleTag("", this.x + 0.5))
@@ -80,21 +81,15 @@ 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),
- nbt, source
- );
- if(tnt == null) {
- return;
- }
- tnt.spawnToAll();
- this.getLevel().addLevelEvent(this, LevelEventPacket.EVENT_SOUND_TNT);
+ .putByte("Fuse", fuse);
+
+ Entity.createEntity(EntityPrimedTNT.NETWORK_ID,
+ this.getLevel().getChunk(this.getChunkX(), this.getChunkZ()), nbt, source).spawnToAll();
}
@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,6 +104,7 @@ 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)) {
@@ -116,6 +112,7 @@ public boolean onActivate(Item item, Player player) {
this.prime(80, player);
return true;
}
+
return false;
}
@@ -123,4 +120,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 077b82b101f..05820c18327 100644
--- a/src/main/java/cn/nukkit/block/BlockTallGrass.java
+++ b/src/main/java/cn/nukkit/block/BlockTallGrass.java
@@ -2,16 +2,16 @@
import cn.nukkit.Player;
import cn.nukkit.item.Item;
+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 {
@@ -29,15 +29,16 @@ public int getId() {
return TALL_GRASS;
}
+ private static final String[] NAMES = {
+ "Grass",
+ "Grass",
+ "Fern",
+ "Fern"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Grass",
- "Grass",
- "Fern",
- "Fern"
- };
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@Override
@@ -64,7 +65,7 @@ public int getBurnAbility() {
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
Block down = this.down();
int id = down.getId();
- if (id == Block.GRASS || id == Block.DIRT || id == Block.PODZOL || id == FARMLAND || id == MYCELIUM) {
+ if (id == Block.GRASS || id == Block.DIRT || id == Block.PODZOL || id == FARMLAND || id == MYCELIUM || id == MOSS_BLOCK) {
this.getLevel().setBlock(block, this, true);
return true;
}
@@ -84,7 +85,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) {
@@ -104,7 +105,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--;
}
@@ -120,6 +121,7 @@ public boolean onActivate(Item item, Player player) {
return false;
}
+
@Override
public Item[] getDrops(Item item) {
if (item.isShears()) {
@@ -128,7 +130,7 @@ public Item[] getDrops(Item item) {
};
}
- if (ThreadLocalRandom.current().nextInt(10) == 0) {
+ if (Utils.random.nextInt(10) == 0) {
return new Item[]{
Item.get(Item.WHEAT_SEEDS)
};
@@ -146,4 +148,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..381ed798412 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..4840a891caf 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[] FACES_2 = {
0, //0
4, //1
5, //2
@@ -59,12 +59,8 @@ public int getLightLevel() {
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
- Block below = this.down();
int side = this.getDamage();
- 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 ((side != 0 && !Block.canConnectToFullSolid(this.getSide(BlockFace.fromIndex(FACES_2[side])))) || (side == 0 && !isSupportValidBelow())) {
this.getLevel().useBreakOn(this);
return Level.BLOCK_UPDATE_NORMAL;
}
@@ -75,26 +71,37 @@ 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) {
- 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) {
- this.setDamage(side);
- this.getLevel().setBlock(block, this, true, true);
- return true;
+ if (block instanceof BlockWater || block.level.isBlockWaterloggedAt(block.getChunk(), (int) block.x, (int) block.y, (int) block.z)) {
+ return false;
}
- Block below = this.down();
- if (!below.isTransparent() || below instanceof BlockFence || below.getId() == COBBLE_WALL || below.getId() == GLASS || below.getId() == STAINED_GLASS) {
+ int side = FACES[face.getIndex()];
+ if (face != BlockFace.UP) {
+ if (Block.canConnectToFullSolid(this.getSide(BlockFace.fromIndex(FACES_2[side])))) {
+ this.setDamage(side);
+ return this.getLevel().setBlock(this, this, true, true);
+ }
+ return false;
+ }
+
+ if (isSupportValidBelow()) {
this.setDamage(0);
- this.getLevel().setBlock(block, this, true, true);
- return true;
+ return this.getLevel().setBlock(this, this, true, true);
}
return false;
}
+ private boolean isSupportValidBelow() {
+ Block block = this.down();
+ if (!block.isTransparent() || block.isNarrowSurface()) {
+ return true;
+ }
+ return Block.canStayOnFullSolid(block);
+ }
+
@Override
public Item toItem() {
- return new ItemBlock(this, 0);
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
}
@Override
@@ -121,4 +128,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..dca2500b26b 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);
}
@@ -37,7 +39,7 @@ public int getId() {
@Override
public String getName() {
- return "Wooden Trapdoor";
+ return "Oak Trapdoor";
}
@Override
@@ -60,7 +62,7 @@ public int getToolType() {
return ItemTool.TYPE_AXE;
}
- private static final AxisAlignedBB[] boundingBoxDamage = new AxisAlignedBB[16];
+ private static final AxisAlignedBB[] BOUNDING_BOX_DAMAGE = new AxisAlignedBB[16];
static {
for (int damage = 0; damage < 16; damage++) {
@@ -126,12 +128,12 @@ public int getToolType() {
);
}
}
- boundingBoxDamage[damage] = bb;
+ BOUNDING_BOX_DAMAGE[damage] = bb;
}
}
private AxisAlignedBB getRelativeBoundingBox() {
- return boundingBoxDamage[this.getDamage()];
+ return BOUNDING_BOX_DAMAGE[this.getDamage()];
}
@Override
@@ -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,16 @@ 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();
+ }
+
+ @Override
+ public WaterloggingType getWaterloggingType() {
+ return WaterloggingType.WHEN_PLACED_IN_WATER;
}
}
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..88ea7931980 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..73786b7f143 100644
--- a/src/main/java/cn/nukkit/block/BlockTripWireHook.java
+++ b/src/main/java/cn/nukkit/block/BlockTripWireHook.java
@@ -39,7 +39,7 @@ public BlockFace getFacing() {
@Override
public int onUpdate(int type) {
if (type == Level.BLOCK_UPDATE_NORMAL) {
- if (!this.getSide(this.getFacing().getOpposite()).isNormalBlock()) {
+ if (!isSupportValid(this.getSide(this.getFacing().getOpposite()))) {
this.level.useBreakOn(this);
}
@@ -54,7 +54,7 @@ public int onUpdate(int type) {
@Override
public boolean place(Item item, Block block, Block target, BlockFace face, double fx, double fy, double fz, Player player) {
- if (!this.getSide(face.getOpposite()).isNormalBlock() || face == BlockFace.DOWN || face == BlockFace.UP) {
+ if (face == BlockFace.DOWN || face == BlockFace.UP) {
return false;
}
@@ -62,6 +62,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
this.setFace(face);
}
+ if (!isSupportValid(this.getSide(this.getFacing().getOpposite()))) {
+ return false;
+ }
+
this.level.setBlock(this, this);
if (player != null) {
@@ -70,6 +74,10 @@ public boolean place(Item item, Block block, Block target, BlockFace face, doubl
return true;
}
+ private static boolean isSupportValid(Block block) {
+ return !block.isTransparent() || Block.canConnectToFullSolid(block);
+ }
+
@Override
public boolean onBreak(Item item) {
super.onBreak(item);
@@ -82,7 +90,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 +98,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 +106,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 +139,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 +238,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..82b3d4f795d
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockTypes.java
@@ -0,0 +1,714 @@
+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_block", BlockID.GRASS);
+ public static final BlockType GRASS_BLOCK = register("minecraft:grass_block", BlockID.GRASS_BLOCK);
+ 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 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 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 PURPUR_STAIRS = register("minecraft:purpur_stairs", BlockID.PURPUR_STAIRS);
+ 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 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 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 CANDLE_CAKE = register("minecraft:candle_cake", BlockID.CANDLE_CAKE);
+ public static final BlockType WHITE_CANDLE_CAKE = register("minecraft:white_candle_cake", BlockID.WHITE_CANDLE_CAKE);
+ public static final BlockType ORANGE_CANDLE_CAKE = register("minecraft:orange_candle_cake", BlockID.ORANGE_CANDLE_CAKE);
+ public static final BlockType MAGENTA_CANDLE_CAKE = register("minecraft:magenta_candle_cake", BlockID.MAGENTA_CANDLE_CAKE);
+ public static final BlockType LIGHT_BLUE_CANDLE_CAKE = register("minecraft:light_blue_candle_cake", BlockID.LIGHT_BLUE_CANDLE_CAKE);
+ public static final BlockType YELLOW_CANDLE_CAKE = register("minecraft:yellow_candle_cake", BlockID.YELLOW_CANDLE_CAKE);
+ public static final BlockType LIME_CANDLE_CAKE = register("minecraft:lime_candle_cake", BlockID.LIME_CANDLE_CAKE);
+ public static final BlockType PINK_CANDLE_CAKE = register("minecraft:pink_candle_cake", BlockID.PINK_CANDLE_CAKE);
+ public static final BlockType GRAY_CANDLE_CAKE = register("minecraft:gray_candle_cake", BlockID.GRAY_CANDLE_CAKE);
+ public static final BlockType LIGHT_GRAY_CANDLE_CAKE = register("minecraft:light_gray_candle_cake", BlockID.LIGHT_GRAY_CANDLE_CAKE);
+ public static final BlockType CYAN_CANDLE_CAKE = register("minecraft:cyan_candle_cake", BlockID.CYAN_CANDLE_CAKE);
+ public static final BlockType PURPLE_CANDLE_CAKE = register("minecraft:purple_candle_cake", BlockID.PURPLE_CANDLE_CAKE);
+ public static final BlockType BLUE_CANDLE_CAKE = register("minecraft:blue_candle_cake", BlockID.BLUE_CANDLE_CAKE);
+ public static final BlockType BROWN_CANDLE_CAKE = register("minecraft:brown_candle_cake", BlockID.BROWN_CANDLE_CAKE);
+ public static final BlockType GREEN_CANDLE_CAKE = register("minecraft:green_candle_cake", BlockID.GREEN_CANDLE_CAKE);
+ public static final BlockType RED_CANDLE_CAKE = register("minecraft:red_candle_cake", BlockID.RED_CANDLE_CAKE);
+ public static final BlockType BLACK_CANDLE_CAKE = register("minecraft:black_candle_cake", BlockID.BLACK_CANDLE_CAKE);
+ 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 SCULK = register("minecraft:sculk", BlockID.SCULK);
+ public static final BlockType SCULK_VEIN = register("minecraft:sculk_vein", BlockID.SCULK_VEIN);
+ public static final BlockType SCULK_CATALYST = register("minecraft:sculk_catalyst", BlockID.SCULK_CATALYST);
+ public static final BlockType SCULK_SHRIEKER = register("minecraft:sculk_shrieker", BlockID.SCULK_SHRIEKER);
+ public static final BlockType REINFORCED_DEEPSLATE = register("minecraft:reinforced_deepslate", BlockID.REINFORCED_DEEPSLATE);
+ public static final BlockType FROG_SPAWN = register("minecraft:frog_spawn", BlockID.FROG_SPAWN);
+ public static final BlockType PEARLESCENT_FROGLIGHT = register("minecraft:pearlescent_froglight", BlockID.PEARLESCENT_FROGLIGHT);
+ public static final BlockType VERDANT_FROGLIGHT = register("minecraft:verdant_froglight", BlockID.VERDANT_FROGLIGHT);
+ public static final BlockType OCHRE_FROGLIGHT = register("minecraft:ochre_froglight", BlockID.OCHRE_FROGLIGHT);
+ public static final BlockType MANGROVE_LEAVES = register("minecraft:mangrove_leaves", BlockID.MANGROVE_LEAVES);
+ public static final BlockType MUD = register("minecraft:mud", BlockID.MUD);
+ public static final BlockType MANGROVE_PROPAGULE = register("minecraft:mangrove_propagule", BlockID.MANGROVE_PROPAGULE);
+ 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);
+ public static final BlockType MANGROVE_ROOTS = register("minecraft:mangrove_roots", BlockID.MANGROVE_ROOTS);
+ public static final BlockType MUDDY_MANGROVE_ROOTS = register("minecraft:muddy_mangrove_roots", BlockID.MUDDY_MANGROVE_ROOTS);
+ public static final BlockType MANGROVE_LOG = register("minecraft:mangrove_log", BlockID.MANGROVE_LOG);
+ public static final BlockType STRIPPED_MANGROVE_LOG = register("minecraft:stripped_mangrove_log", BlockID.STRIPPED_MANGROVE_LOG);
+ public static final BlockType MANGROVE_PLANKS = register("minecraft:mangrove_planks", BlockID.MANGROVE_PLANKS);
+ public static final BlockType MANGROVE_BUTTON = register("minecraft:mangrove_button", BlockID.MANGROVE_BUTTON);
+ public static final BlockType MANGROVE_STAIRS = register("minecraft:mangrove_stairs", BlockID.MANGROVE_STAIRS);
+ public static final BlockType MANGROVE_SLAB = register("minecraft:mangrove_slab", BlockID.MANGROVE_SLAB);
+ public static final BlockType MANGROVE_PRESSURE_PLATE = register("minecraft:mangrove_pressure_plate", BlockID.MANGROVE_PRESSURE_PLATE);
+ public static final BlockType MANGROVE_FENCE = register("minecraft:mangrove_fence", BlockID.MANGROVE_FENCE);
+ public static final BlockType MANGROVE_FENCE_GATE = register("minecraft:mangrove_fence_gate", BlockID.MANGROVE_FENCE_GATE);
+ public static final BlockType MANGROVE_DOOR = register("minecraft:mangrove_door", BlockID.MANGROVE_DOOR);
+ public static final BlockType MANGROVE_STANDING_SIGN = register("minecraft:mangrove_standing_sign", BlockID.MANGROVE_STANDING_SIGN);
+ public static final BlockType MANGROVE_WALL_SIGN = register("minecraft:mangrove_wall_sign", BlockID.MANGROVE_WALL_SIGN);
+ public static final BlockType MANGROVE_TRAPDOOR = register("minecraft:mangrove_trapdoor", BlockID.MANGROVE_TRAPDOOR);
+ public static final BlockType MANGROVE_WOOD = register("minecraft:mangrove_wood", BlockID.MANGROVE_WOOD);
+ public static final BlockType STRIPPED_MANGROVE_WOOD = register("minecraft:stripped_mangrove_wood", BlockID.STRIPPED_MANGROVE_WOOD);
+ public static final BlockType MANGROVE_DOUBLE_SLAB = register("minecraft:mangrove_double_slab", BlockID.MANGROVE_DOUBLE_SLAB);
+
+ 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..0a6adeea540
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockVinesNether.java
@@ -0,0 +1,404 @@
+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.ItemBlock;
+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 < level.getMinBlockY() || pos.y > level.getMaxBlockY()) {
+ 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 < level.getMinBlockY() || pos.y > level.getMaxBlockY()) {
+ 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 toItem() {
+ return new ItemBlock(Block.get(this.getId(), 0), 0);
+ }
+
+ @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..95fe7feb0f8 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);
}
@@ -44,16 +44,42 @@ public double getResistance() {
@Override
public String getName() {
- if (this.getDamage() == 0x01) {
- return "Mossy Cobblestone Wall";
+ switch (this.getDamage()) {
+ case 0:
+ return "Cobblestone Wall";
+ case 1:
+ return "Mossy Cobblestone Wall";
+ case 2:
+ return "Granite Wall";
+ case 3:
+ return "Diorite Wall";
+ case 4:
+ return "Andesite Wall";
+ case 5:
+ return "Sandstone Wall";
+ case 6:
+ return "Brick Wall";
+ case 7:
+ return "Stone Brick Wall";
+ case 8:
+ return "Mossy Stone Brick Wall";
+ case 9:
+ return "Nether Brick Wall";
+ case 10:
+ return "End Stone Brick Wall";
+ case 11:
+ return "Prismarine Wall";
+ case 12:
+ return "Red Sandstone Wall";
+ case 13:
+ return "Red Nether Brick Wall";
}
- return "Cobblestone Wall";
+ return "Wall";
}
@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 +121,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..4f37c012312 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,15 +36,9 @@ 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) {
+ if (this.getSide(BlockFace.fromIndex(FACES[this.getDamage() - 2])).getId() == Item.AIR) {
this.getLevel().useBreakOn(this);
}
return Level.BLOCK_UPDATE_NORMAL;
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..74cbb0b4b5b
--- /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 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..fb275d6fc88
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/BlockWitherRose.java
@@ -0,0 +1,76 @@
+package cn.nukkit.block;
+
+import cn.nukkit.Player;
+import cn.nukkit.entity.Entity;
+import cn.nukkit.entity.EntityLiving;
+import cn.nukkit.event.entity.EntityPotionEffectEvent;
+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, EntityPotionEffectEvent.Cause.WITHER_ROSE);
+ }
+ }
+ }
+
+ @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..a4e37f9d0c1 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[] STRIPPED_IDS = {
+ STRIPPED_OAK_LOG,
+ STRIPPED_SPRUCE_LOG,
+ STRIPPED_BIRCH_LOG,
+ STRIPPED_JUNGLE_LOG
+ };
public BlockWood() {
this(0);
@@ -38,19 +54,19 @@ public double getHardness() {
@Override
public double getResistance() {
- return 10;
+ return 2;
}
+ private static final String[] NAMES = {
+ "Oak Wood",
+ "Spruce Wood",
+ "Birch Wood",
+ "Jungle Wood"
+ };
+
@Override
public String getName() {
- String[] names = new String[]{
- "Oak Wood",
- "Spruce Wood",
- "Birch Wood",
- "Jungle Wood"
- };
-
- return names[this.getDamage() & 0x03];
+ return NAMES[this.getDamage() & 0x03];
}
@Override
@@ -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 STRIPPED_IDS[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..a8d3a29f22f
--- /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..d2fe4e12819
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/Blocks.java
@@ -0,0 +1,640 @@
+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
+ //
+ 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
+ //
+ 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
+ //
+ 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
+ //
+ list[POWDER_SNOW] = BlockPowderSnow.class; // 561
+ list[SCULK_SENSOR] = BlockSculkSensor.class; // 562
+ list[POINTED_DRIPSTONE] = BlockPointedDripstone.class; // 563
+ //
+ list[COPPER_ORE] = BlockOreCopper.class; //566
+ list[LIGHTNING_ROD] = BlockLightningRod.class; //567
+ //
+ 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
+ /*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
+ list[CANDLE_CAKE] = BlockCandle.class; //684
+ list[WHITE_CANDLE_CAKE] = BlockCandleCakeWhite.class; //685
+ list[ORANGE_CANDLE_CAKE] = BlockCandleCakeOrange.class; //686
+ list[MAGENTA_CANDLE_CAKE] = BlockCandleCakeMagenta.class; //687
+ list[LIGHT_BLUE_CANDLE_CAKE] = BlockCandleCakeLightBlue.class; //688
+ list[YELLOW_CANDLE_CAKE] = BlockCandleCakeYellow.class; //689
+ list[LIME_CANDLE_CAKE] = BlockCandleCakeLime.class; //690
+ list[PINK_CANDLE_CAKE] = BlockCandleCakePink.class; //691
+ list[GRAY_CANDLE_CAKE] = BlockCandleCakeGray.class; //692
+ list[LIGHT_GRAY_CANDLE_CAKE] = BlockCandleCakeLightGray.class; //693
+ list[CYAN_CANDLE_CAKE] = BlockCandleCakeCyan.class; //694
+ list[PURPLE_CANDLE_CAKE] = BlockCandleCakePurple.class; //6965
+ list[BLUE_CANDLE_CAKE] = BlockCandleCakeBlue.class; //696
+ list[BROWN_CANDLE_CAKE] = BlockCandleCakeBrown.class; //697
+ list[GREEN_CANDLE_CAKE] = BlockCandleCakeGreen.class; //698
+ list[RED_CANDLE_CAKE] = BlockCandleCakeRed.class; //699
+ list[BLACK_CANDLE_CAKE] = BlockCandleCakeBlack.class; //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
+ //
+ list[SCULK] = BlockSculk.class; //713
+ //list[SCULK_VEIN] = BlockSculkVein.class; //714
+ list[SCULK_CATALYST] = BlockSculkCatalyst.class; //715
+ list[SCULK_SHRIEKER] = BlockSculkShrieker.class; //716
+ //
+ list[REINFORCED_DEEPSLATE] = BlockReinforcedDeeplsate.class; //721
+ //
+ list[FROG_SPAWN] = BlockFrogspawn.class; //723
+ list[PEARLESCENT_FROGLIGHT] = BlockFroglightPearlescent.class; //724
+ list[VERDANT_FROGLIGHT] = BlockFroglightVerdant.class; //725
+ list[OCHRE_FROGLIGHT] = BlockFroglightOchre.class; //726
+ //list[MANGROVE_LEAVES] = BlockLeavesMangrove.class; //727
+ list[MUD] = BlockMud.class; //728
+ list[MANGROVE_PROPAGULE] = BlockMangrovePropagule.class; //729
+ 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
+ list[MANGROVE_ROOTS] = BlockMangroveRoots.class; //737
+ //list[MUDDY_MANGROVE_ROOTS] = BlockMangroveRootsMuddy.class; //738
+ //list[MANGROVE_LOG] = .class; //739
+ //list[STRIPPED_MANGROVE_LOG] = .class; //740
+ //list[MANGROVE_PLANKS] = .class; //741
+ //list[MANGROVE_BUTTON] = .class; //742
+ //list[MANGROVE_STAIRS] = .class; //743
+ //list[MANGROVE_SLAB] = .class; //744
+ //list[MANGROVE_PRESSURE_PLATE] = .class; //745
+ //list[MANGROVE_FENCE] = .class; //746
+ //list[MANGROVE_FENCE_GATE] = .class; //747
+ //
+ //list[MANGROVE_STANDING_SIGN] = .class; //749
+ //list[MANGROVE_WALL_SIGN] = .class; //750
+ //list[MANGROVE_TRAPDOOR] = .class; //751
+ //list[MANGROVE_WOOD] = .class; //752
+ //list[STRIPPED_MANGROVE_WOOD] = .class; //753
+ //list[MANGROVE_DOUBLE_SLAB] = .class; //754
+ }
+
+ 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/custom/container/BlockContainer.java b/src/main/java/cn/nukkit/block/custom/container/BlockContainer.java
new file mode 100644
index 00000000000..22ee3ecd454
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/container/BlockContainer.java
@@ -0,0 +1,16 @@
+package cn.nukkit.block.custom.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/block/custom/container/BlockStorageContainer.java b/src/main/java/cn/nukkit/block/custom/container/BlockStorageContainer.java
new file mode 100644
index 00000000000..3801751c3bd
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/container/BlockStorageContainer.java
@@ -0,0 +1,78 @@
+package cn.nukkit.block.custom.container;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.block.custom.properties.BlockProperties;
+import cn.nukkit.block.custom.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 property, boolean value) {
+ this.setBooleanValue(property.getName(), value);
+ }
+
+ default void setBooleanValue(String propertyName, boolean value) {
+ this.setStorage(this.getBlockProperties().setBooleanValue(this.getStorage(), propertyName, value));
+ }
+
+ default void setPropertyValue(String propertyName, Serializable value) {
+ this.setStorage(this.getBlockProperties().setValue(this.getStorage(), propertyName, value));
+ }
+
+ default void setPropertyValue(BlockProperty property, T value) {
+ this.setPropertyValue(property.getName(), value);
+ }
+
+ default void setIntValue(String propertyName, int value) {
+ this.setStorage(this.getBlockProperties().setIntValue(this.getStorage(), propertyName, value));
+ }
+
+ default Serializable getPropertyValue(String propertyName) {
+ return this.getBlockProperties().getValue(this.getStorage(), propertyName);
+ }
+
+ default V getPropertyValue(BlockProperty property) {
+ return this.getCheckedPropertyValue(property.getName(), property.getValueClass());
+ }
+
+ default T getCheckedPropertyValue(String propertyName, Class tClass) {
+ return tClass.cast(this.getPropertyValue(propertyName));
+ }
+
+ default int getIntValue(String propertyName) {
+ return this.getBlockProperties().getIntValue(this.getStorage(), propertyName);
+ }
+
+ default boolean getBooleanValue(BlockProperty property) {
+ return this.getBooleanValue(property.getName());
+ }
+
+ default boolean getBooleanValue(String propertyName) {
+ return this.getBlockProperties().getBooleanValue(this.getStorage(), propertyName);
+ }
+
+ default void setStorageFromItem(int itemMeta) {
+ BlockProperties properties = this.getBlockProperties();
+ BlockProperties itemProperties = properties.getItemBlockProperties();
+ if (itemProperties.equals(properties)) {
+ this.setStorage(itemMeta);
+ return;
+ }
+
+ int damage = 0;
+ for (String propertyName : itemProperties.getItemPropertyNames()) {
+ damage = properties.setValue(damage, propertyName, itemProperties.getValue(itemMeta, propertyName));
+ }
+ this.setStorage(damage);
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/BlockProperties.java b/src/main/java/cn/nukkit/block/custom/properties/BlockProperties.java
new file mode 100644
index 00000000000..95db464a68f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/BlockProperties.java
@@ -0,0 +1,420 @@
+package cn.nukkit.block.custom.properties;
+
+import cn.nukkit.block.custom.properties.exception.BlockPropertyNotFoundException;
+import cn.nukkit.utils.functional.ToIntTriFunctionTwoInts;
+import cn.nukkit.utils.functional.ToLongTriFunctionOneIntOneLong;
+import com.google.common.base.Preconditions;
+import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
+import net.daporkchop.lib.common.function.plain.TriFunction;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.ObjIntConsumer;
+
+public class BlockProperties {
+ private final Map byName;
+ private final int bitSize;
+ private final BlockProperties itemBlockProperties;
+
+ public BlockProperties(BlockProperty>... properties) {
+ this(null, properties);
+ }
+
+ public BlockProperties(BlockProperties itemBlockProperties, BlockProperty>... properties) {
+ if (itemBlockProperties == null) {
+ this.itemBlockProperties = this;
+ } else {
+ this.itemBlockProperties = itemBlockProperties;
+ }
+
+ Map registry = new Object2ObjectLinkedOpenHashMap<>(properties.length);
+ Map byPersistenceName = new Object2ObjectLinkedOpenHashMap<>(properties.length);
+
+ int offset = 0;
+ boolean allowItemExport = true;
+ for (BlockProperty> property : properties) {
+ Preconditions.checkArgument(property != null, "The properties can not contains null values");
+ if (property.isExportedToItem()) {
+ Preconditions.checkArgument(allowItemExport, "Cannot export a property to item if the previous property does not export");
+ Preconditions.checkArgument(offset <= 6); // Only 6 bits of data can be stored in item blocks, client side limitation.
+ } else {
+ allowItemExport = false;
+ }
+
+ RegisteredBlockProperty register = new RegisteredBlockProperty(property, offset);
+ offset += property.getBitSize();
+
+ Preconditions.checkArgument(registry.put(property.getName(), register) == null, "The property %s is duplicated by it's normal name", property.getName());
+ Preconditions.checkArgument(byPersistenceName.put(property.getPersistenceName(), register) == null, "The property %s is duplicated by it's persistence name", property.getPersistenceName());
+ }
+
+ this.byName = Collections.unmodifiableMap(registry);
+ bitSize = offset;
+ }
+
+ public BlockProperties getItemBlockProperties() {
+ return this.itemBlockProperties;
+ }
+
+ @SuppressWarnings("unchecked")
+ public int setValue(int currentMeta, String propertyName, Serializable value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public long setValue(long currentMeta, String propertyName, Serializable value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public int setBooleanValue(int currentMeta, String propertyName, boolean value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty> property = registry.getProperty();
+ if (BooleanBlockProperty.class == property.getClass()) {
+ return ((BooleanBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public long setBooleanValue(long currentMeta, String propertyName, boolean value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty> property = registry.getProperty();
+ if (BooleanBlockProperty.class == property.getClass()) {
+ return ((BooleanBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public BigInteger setBooleanValue(BigInteger currentMeta, String propertyName, boolean value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty> property = registry.getProperty();
+ if (BooleanBlockProperty.class == property.getClass()) {
+ return ((BooleanBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public int setIntValue(int currentMeta, String propertyName, int value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty> property = registry.getProperty();
+ if (IntBlockProperty.class == property.getClass()) {
+ return ((IntBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ } else if (UnsignedIntBlockProperty.class == property.getClass()) {
+ return ((UnsignedIntBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public long setIntValue(long currentMeta, String propertyName, int value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty> property = registry.getProperty();
+ if (IntBlockProperty.class == property.getClass()) {
+ return ((IntBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ } else if (UnsignedIntBlockProperty.class == property.getClass()) {
+ return ((UnsignedIntBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public BigInteger setIntValue(BigInteger currentMeta, String propertyName, int value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty> property = registry.getProperty();
+ if (IntBlockProperty.class == property.getClass()) {
+ return ((IntBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ } else if (UnsignedIntBlockProperty.class == property.getClass()) {
+ return ((UnsignedIntBlockProperty) property).setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public int setPersistenceValue(int currentMeta, String propertyName, String persistenceValue) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty property = registry.getProperty();
+ int meta = property.getMetaForPersistenceValue(persistenceValue);
+ Serializable value = property.getValueForMeta(meta);
+ return property.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public long setPersistenceValue(long currentMeta, String propertyName, String persistenceValue) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty property = registry.getProperty();
+ int meta = property.getMetaForPersistenceValue(persistenceValue);
+ Serializable value = property.getValueForMeta(meta);
+ return property.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public BigInteger setPersistenceValue(BigInteger currentMeta, String propertyName, String persistenceValue) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty property = registry.getProperty();
+ int meta = property.getMetaForPersistenceValue(persistenceValue);
+ Serializable value = property.getValueForMeta(meta);
+ return property.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public BigInteger setValue(BigInteger currentMeta, String propertyName, Serializable value) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ BlockProperty unchecked = registry.getProperty();
+ return unchecked.setValue(currentMeta, registry.getOffset(), value);
+ }
+
+ public Serializable getValue(int currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ public Serializable getValue(long currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ public Serializable getValue(BigInteger currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ public T getCheckedValue(int currentMeta, String propertyName, Class clazz) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return clazz.cast(registry.getProperty().getValue(currentMeta, registry.getOffset()));
+ }
+
+ public T getCheckedValue(long currentMeta, String propertyName, Class clazz) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return clazz.cast(registry.getProperty().getValue(currentMeta, registry.getOffset()));
+ }
+
+ public T getCheckedValue(BigInteger currentMeta, String propertyName, Class clazz) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return clazz.cast(registry.getProperty().getValue(currentMeta, registry.getOffset()));
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getUncheckedValue(int currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return (T) registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getUncheckedValue(long currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return (T) registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getUncheckedValue(BigInteger currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return (T) registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ public int getIntValue(int currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getIntValue(currentMeta, registry.getOffset());
+ }
+
+ public int getIntValue(long currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getIntValue(currentMeta, registry.getOffset());
+ }
+
+ public int getIntValue(BigInteger currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getIntValue(currentMeta, registry.getOffset());
+ }
+
+ public Serializable getPersistenceValue(int currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getPersistenceValue(currentMeta, registry.getOffset());
+ }
+
+ public Serializable getPersistenceValue(long currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getPersistenceValue(currentMeta, registry.getOffset());
+ }
+
+ public Serializable getPersistenceValue(BigInteger currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ return registry.getProperty().getPersistenceValue(currentMeta, registry.getOffset());
+ }
+
+ public boolean getBooleanValue(int currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ if (registry.getProperty() instanceof BooleanBlockProperty) {
+ return ((BooleanBlockProperty) registry.getProperty()).getBooleanValue(currentMeta, registry.getOffset());
+ }
+
+ return (Boolean) registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ public boolean getBooleanValue(long currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ if (registry.getProperty() instanceof BooleanBlockProperty) {
+ return ((BooleanBlockProperty) registry.getProperty()).getBooleanValue(currentMeta, registry.getOffset());
+ }
+
+ return (Boolean) registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ public boolean getBooleanValue(BigInteger currentMeta, String propertyName) {
+ RegisteredBlockProperty registry = requireRegisteredProperty(propertyName);
+ if (registry.getProperty() instanceof BooleanBlockProperty) {
+ return ((BooleanBlockProperty) registry.getProperty()).getBooleanValue(currentMeta, registry.getOffset());
+ }
+
+ return (Boolean) registry.getProperty().getValue(currentMeta, registry.getOffset());
+ }
+
+ public R reduce(R identity, TriFunction, Integer, R, R> accumulator) {
+ R result = identity;
+ for (RegisteredBlockProperty registry : byName.values()) {
+ result = accumulator.apply(registry.getProperty(), registry.getOffset(), result);
+ }
+ return result;
+ }
+
+ public int reduceInt(int identity, ToIntTriFunctionTwoInts> accumulator) {
+ int result = identity;
+ for (RegisteredBlockProperty registry : byName.values()) {
+ result = accumulator.apply(registry.getProperty(), registry.getOffset(), result);
+ }
+ return result;
+ }
+
+ public long reduceLong(long identity, ToLongTriFunctionOneIntOneLong> accumulator) {
+ long result = identity;
+ for (RegisteredBlockProperty registry : byName.values()) {
+ result = accumulator.apply(registry.getProperty(), registry.getOffset(), result);
+ }
+ return result;
+ }
+
+ public boolean isDefaultValue(String propertyName, Serializable value) {
+ BlockProperty blockProperty = getBlockProperty(propertyName);
+ return blockProperty.isDefaultValue(value);
+ }
+
+ public boolean isDefaultValue(BlockProperty property, T value) {
+ return isDefaultValue(property.getName(), value);
+ }
+
+ public boolean isDefaultIntValue(String propertyName, int value) {
+ BlockProperty blockProperty = getBlockProperty(propertyName);
+ return blockProperty.isDefaultIntValue(value);
+ }
+
+ public boolean isDefaultIntValue(BlockProperty property, int value) {
+ return isDefaultIntValue(property.getName(), value);
+ }
+
+ public boolean isDefaultBooleanValue(String propertyName, boolean value) {
+ BlockProperty blockProperty = getBlockProperty(propertyName);
+ return blockProperty.isDefaultBooleanValue(value);
+ }
+
+ public boolean isDefaultBooleanValue(BlockProperty property, boolean value) {
+ return isDefaultBooleanValue(property.getName(), value);
+ }
+
+ public int getOffset(String propertyName) {
+ return this.requireRegisteredProperty(propertyName).getOffset();
+ }
+
+ public boolean contains(String propertyName) {
+ return this.byName.containsKey(propertyName);
+ }
+
+ public boolean contains(BlockProperty> property) {
+ RegisteredBlockProperty registry = this.byName.get(property.getName());
+ if (registry == null) {
+ return false;
+ }
+ return registry.getProperty().getValueClass().equals(property.getValueClass());
+ }
+
+ @SuppressWarnings("java:S1452")
+ public BlockProperty> getBlockProperty(String propertyName) {
+ return this.requireRegisteredProperty(propertyName).getProperty();
+ }
+
+ public > T getBlockProperty(String propertyName, Class clazz) {
+ return clazz.cast(this.requireRegisteredProperty(propertyName).getProperty());
+ }
+
+ public RegisteredBlockProperty requireRegisteredProperty(String propertyName) {
+ RegisteredBlockProperty registry = this.byName.get(propertyName);
+ if (registry == null) {
+ throw new BlockPropertyNotFoundException(propertyName, this);
+ }
+ return registry;
+ }
+
+ public Set getNames() {
+ return this.byName.keySet();
+ }
+
+ public Collection getAllProperties() {
+ return this.byName.values();
+ }
+
+ public int getBitSize() {
+ return this.bitSize;
+ }
+
+ public List getItemPropertyNames() {
+ List itemProperties = new ArrayList<>(this.byName.size());
+ for (RegisteredBlockProperty registry : this.byName.values()) {
+ if (registry.getProperty().isExportedToItem()) {
+ itemProperties.add(registry.getProperty().getName());
+ } else {
+ break;
+ }
+ }
+ return itemProperties;
+ }
+
+ public void forEach(ObjIntConsumer> consumer) {
+ for (RegisteredBlockProperty registry : this.byName.values()) {
+ consumer.accept(registry.getProperty(), registry.getOffset());
+ }
+ }
+
+ public void forEach(Consumer> consumer) {
+ for (RegisteredBlockProperty registry : this.byName.values()) {
+ consumer.accept(registry.getProperty());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "BlockProperties{" +
+ "bitSize=" + bitSize +
+ ", properties=" + byName.values() +
+ '}';
+ }
+
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/BlockProperty.java b/src/main/java/cn/nukkit/block/custom/properties/BlockProperty.java
new file mode 100644
index 00000000000..85341422eeb
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/BlockProperty.java
@@ -0,0 +1,333 @@
+package cn.nukkit.block.custom.properties;
+
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyMetaException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyValueException;
+import com.google.common.base.Preconditions;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+
+public abstract class BlockProperty implements Serializable {
+ private static final long serialVersionUID = -2594821043880025191L;
+
+ private final String name;
+ private final boolean exportedToItem;
+ private final String persistenceName;
+ private final int bitSize;
+
+ public BlockProperty(String name, boolean exportedToItem, String persistenceName, int bitSize) {
+ Preconditions.checkArgument(bitSize > 0, "Bit size (%s) must be positive", bitSize);
+ this.name = name;
+ this.exportedToItem = exportedToItem;
+ this.persistenceName = persistenceName;
+ this.bitSize = bitSize;
+ }
+
+ public abstract int getMetaForValue(T value);
+ public abstract T getValueForMeta(int meta);
+ public abstract int getIntValueForMeta(int meta);
+
+ public abstract Serializable getPersistenceValueForMeta(int meta);
+ public abstract int getMetaForPersistenceValue(String persistenceValue);
+
+ public abstract Class getValueClass();
+
+ public abstract boolean isDefaultValue(T value);
+ public abstract T getDefaultValue();
+ public abstract BlockProperty exportingToItems(boolean exportedToItem);
+
+ public int setValue(int currentMeta, int bitOffset, T newValue) {
+ int mask = computeValueMask(bitOffset);
+ try {
+ int value = getMetaForValue(newValue) << bitOffset;
+
+ if ((value & ~mask) != 0) {
+ throw new IllegalStateException("Attempted to set a value which overflows the size of " + bitSize + " bits. Current:" + currentMeta + ", offset:" + bitOffset + ", meta:" + value + ", value:" + newValue);
+ }
+
+ return currentMeta & ~mask | (value & mask);
+ } catch (Exception e) {
+ T oldValue = null;
+ InvalidBlockPropertyMetaException suppressed = null;
+ try {
+ oldValue = getValue(currentMeta, bitOffset);
+ } catch (Exception e2) {
+ suppressed = new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta & mask, e2);
+ }
+ InvalidBlockPropertyValueException toThrow = new InvalidBlockPropertyValueException(this, oldValue, newValue, e);
+ if (suppressed != null) {
+ toThrow.addSuppressed(suppressed);
+ }
+ throw toThrow;
+ }
+ }
+
+ public long setValue(long currentBigMeta, int bitOffset, T newValue) {
+ long mask = computeBigValueMask(bitOffset);
+ try {
+ long value = getMetaForValue(newValue) << bitOffset;
+
+ if ((value & ~mask) != 0L) {
+ throw new IllegalStateException("Attempted to set a value which overflows the size of " + bitSize + " bits. Current:" + currentBigMeta + ", offset:" + bitOffset + ", meta:" + value + ", value:" + newValue);
+ }
+
+ return currentBigMeta & ~mask | (value & mask);
+ } catch (Exception e) {
+ T oldValue = null;
+ InvalidBlockPropertyMetaException suppressed = null;
+ try {
+ oldValue = getValue(currentBigMeta, bitOffset);
+ } catch (Exception e2) {
+ suppressed = new InvalidBlockPropertyMetaException(this, currentBigMeta, currentBigMeta & mask, e2);
+ }
+ InvalidBlockPropertyValueException toThrow = new InvalidBlockPropertyValueException(this, oldValue, newValue, e);
+ if (suppressed != null) {
+ toThrow.addSuppressed(suppressed);
+ }
+ throw toThrow;
+ }
+ }
+
+ public BigInteger setValue(BigInteger currentHugeMeta, int bitOffset, T newValue) {
+ BigInteger mask = computeHugeValueMask(bitOffset);
+ try {
+ BigInteger value = BigInteger.valueOf(getMetaForValue(newValue)).shiftLeft(bitOffset);
+
+ if (!value.andNot(mask).equals(BigInteger.ZERO)) {
+ throw new IllegalStateException("Attempted to set a value which overflows the size of " + bitSize + " bits. Current:" + currentHugeMeta + ", offset:" + bitOffset + ", meta:" + value + ", value:" + newValue);
+ }
+
+ return currentHugeMeta.andNot(mask).or(value.and(mask));
+ } catch (Exception e) {
+ T oldValue = null;
+ InvalidBlockPropertyMetaException suppressed = null;
+ try {
+ oldValue = getValue(currentHugeMeta, bitOffset);
+ } catch (Exception e2) {
+ suppressed = new InvalidBlockPropertyMetaException(this, currentHugeMeta, currentHugeMeta.and(mask), e2);
+ }
+ InvalidBlockPropertyValueException toThrow = new InvalidBlockPropertyValueException(this, oldValue, newValue, e);
+ if (suppressed != null) {
+ toThrow.addSuppressed(suppressed);
+ }
+ throw toThrow;
+ }
+ }
+
+ public T getValue(int currentMeta, int bitOffset) {
+ int meta = getMetaFromInt(currentMeta, bitOffset);
+ try {
+ return getValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta & computeValueMask(bitOffset), e);
+ }
+ }
+
+ public T getValue(long currentBigMeta, int bitOffset) {
+ int meta = getMetaFromLong(currentBigMeta, bitOffset);
+ try {
+ return getValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentBigMeta, currentBigMeta & computeBigValueMask(bitOffset), e);
+ }
+ }
+
+ public T getValue(BigInteger currentHugeMeta, int bitOffset) {
+ int meta = getMetaFromBigInt(currentHugeMeta, bitOffset);
+ try {
+ return getValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentHugeMeta, currentHugeMeta.and(computeHugeValueMask(bitOffset)), e);
+ }
+ }
+
+ public int getIntValue(int currentMeta, int bitOffset) {
+ int meta = getMetaFromInt(currentMeta, bitOffset);
+ try {
+ return getIntValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta & computeValueMask(bitOffset), e);
+ }
+ }
+
+ public int getIntValue(long currentMeta, int bitOffset) {
+ int meta = getMetaFromLong(currentMeta, bitOffset);
+ try {
+ return getIntValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta & computeBigValueMask(bitOffset), e);
+ }
+ }
+
+ public int getIntValue(BigInteger currentMeta, int bitOffset) {
+ int meta = getMetaFromBigInt(currentMeta, bitOffset);
+ try {
+ return getIntValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta.and(computeHugeValueMask(bitOffset)), e);
+ }
+ }
+
+ public Serializable getPersistenceValue(int currentMeta, int bitOffset) {
+ int meta = getMetaFromInt(currentMeta, bitOffset);
+ try {
+ return this.getPersistenceValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta & computeValueMask(bitOffset), e);
+ }
+ }
+
+ public Serializable getPersistenceValue(long currentMeta, int bitOffset) {
+ int meta = getMetaFromLong(currentMeta, bitOffset);
+ try {
+ return this.getPersistenceValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta & computeBigValueMask(bitOffset), e);
+ }
+ }
+
+ public Serializable getPersistenceValue(BigInteger currentMeta, int bitOffset) {
+ int meta = getMetaFromBigInt(currentMeta, bitOffset);
+ try {
+ return this.getPersistenceValueForMeta(meta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, currentMeta, currentMeta.and(computeHugeValueMask(bitOffset)), e);
+ }
+ }
+
+
+ private int computeRightMask(int bitOffset) {
+ return bitOffset == 0? 0 : -1 >>> (32 - bitOffset);
+ }
+
+ private long computeBigRightMask(int bitOffset) {
+ return bitOffset == 0L? 0L : -1L >>> (64 - bitOffset);
+ }
+
+ private BigInteger computeHugeRightMask(int bitOffset) {
+ return BigInteger.ONE.shiftLeft(bitOffset).subtract(BigInteger.ONE);
+ }
+
+ private int computeValueMask(int bitOffset) {
+ Preconditions.checkArgument(bitOffset >= 0, "Bit offset can not be negative. Got %s", bitOffset);
+
+ int maskBits = bitSize + bitOffset;
+ Preconditions.checkArgument(0 < maskBits && maskBits <= 32, "The bit offset %s plus the bit size %s causes memory overflow (32 bits)", bitOffset, bitSize);
+
+ int rightMask = computeRightMask(bitOffset);
+ int leftMask = -1 << maskBits;
+ return ~rightMask & ~leftMask;
+ }
+
+ private long computeBigValueMask(int bitOffset) {
+ Preconditions.checkArgument(bitOffset >= 0, "Bit offset can not be negative. Got %s", bitOffset);
+
+ int maskBits = bitSize + bitOffset;
+ Preconditions.checkArgument(0 < maskBits && maskBits <= 64, "The bit offset %s plus the bit size %s causes memory overflow (64 bits)", bitOffset, bitSize);
+
+ long rightMask = computeBigRightMask(bitOffset);
+ long leftMask = -1L << maskBits;
+ return ~rightMask & ~leftMask;
+ }
+
+ private BigInteger computeHugeValueMask(int bitOffset) {
+ Preconditions.checkArgument(bitOffset >= 0, "Bit offset can not be negative. Got %s", bitOffset);
+
+ int maskBits = bitSize + bitOffset;
+ Preconditions.checkArgument(0 < maskBits, "The bit offset %s plus the bit size %s causes memory overflow (huge)", bitOffset, bitSize);
+
+ BigInteger rightMask = computeHugeRightMask(bitOffset);
+ BigInteger leftMask = BigInteger.valueOf(-1).shiftLeft(maskBits);
+
+ return rightMask.not().andNot(leftMask);
+ }
+
+ public final int getMetaFromInt(int currentMeta, int bitOffset) {
+ return (currentMeta & computeValueMask(bitOffset)) >>> bitOffset;
+ }
+
+ public final int getMetaFromLong(long currentMeta, int bitOffset) {
+ return (int) ((currentMeta & computeBigValueMask(bitOffset)) >>> bitOffset);
+ }
+
+ public final int getMetaFromBigInt(BigInteger currentMeta, int bitOffset) {
+ return currentMeta.and(computeHugeValueMask(bitOffset)).shiftRight(bitOffset).intValue();
+ }
+
+ protected void validateDirectly(T value) {
+ // Does nothing by default
+ }
+
+ protected abstract void validateMetaDirectly(int meta);
+
+ public final void validateMeta(int meta, int offset) {
+ int propMeta = getMetaFromInt(meta, offset);
+ try {
+ validateMetaDirectly(propMeta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta & computeValueMask(offset), e);
+ }
+ }
+
+ public final void validateMeta(long meta, int offset) {
+ int propMeta = getMetaFromLong(meta, offset);
+ try {
+ validateMetaDirectly(propMeta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta & computeBigValueMask(offset), e);
+ }
+ }
+
+ public final void validateMeta(BigInteger meta, int offset) {
+ int propMeta = getMetaFromBigInt(meta, offset);
+ try {
+ validateMetaDirectly(propMeta);
+ } catch (Exception e) {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta.and(computeHugeRightMask(offset)), e);
+ }
+ }
+
+ public boolean isDefaultIntValue(int value) {
+ return value == getDefaultIntValue();
+ }
+
+ public boolean isDefaultBooleanValue(boolean value) {
+ return value == getDefaultBooleanValue();
+ }
+
+ public int getDefaultIntValue() {
+ return 0;
+ }
+
+ public boolean getDefaultBooleanValue() {
+ return false;
+ }
+
+ public int getBitSize() {
+ return this.bitSize;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getPersistenceName() {
+ return this.persistenceName;
+ }
+
+ public boolean isExportedToItem() {
+ return this.exportedToItem;
+ }
+
+ public abstract BlockProperty copy();
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName()+"{" +
+ "name='" + name + '\'' +
+ ", bitSize=" + bitSize +
+ ", exportedToItem=" + exportedToItem +
+ ", persistenceName='" + persistenceName + '\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/BlockPropertyUtils.java b/src/main/java/cn/nukkit/block/custom/properties/BlockPropertyUtils.java
new file mode 100644
index 00000000000..383b39a4904
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/BlockPropertyUtils.java
@@ -0,0 +1,68 @@
+package cn.nukkit.block.custom.properties;
+
+import java.math.BigInteger;
+
+public class BlockPropertyUtils {
+
+ public static int bitLength(byte data) {
+ if (data < 0) {
+ return 32;
+ }
+
+ if (data == 0) {
+ return 1;
+ }
+
+ int bits = 0;
+ while (data != 0) {
+ data >>>= 1;
+ bits++;
+ }
+
+ return bits;
+ }
+
+ public static int bitLength(int data) {
+ if (data < 0) {
+ return 32;
+ }
+
+ if (data == 0) {
+ return 1;
+ }
+
+ int bits = 0;
+ while (data != 0) {
+ data >>>= 1;
+ bits++;
+ }
+
+ return bits;
+ }
+
+ public static int bitLength(long data) {
+ if (data < 0) {
+ return 64;
+ }
+
+ if (data == 0) {
+ return 1;
+ }
+
+ int bits = 0;
+ while (data != 0) {
+ data >>>= 1;
+ bits++;
+ }
+
+ return bits;
+ }
+
+ public static int bitLength(BigInteger data) {
+ if (data.compareTo(BigInteger.ZERO) < 0) {
+ throw new UnsupportedOperationException("Negative BigIntegers are not supported (nearly infinite bits)");
+ }
+
+ return data.bitLength();
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/BooleanBlockProperty.java b/src/main/java/cn/nukkit/block/custom/properties/BooleanBlockProperty.java
new file mode 100644
index 00000000000..06c844269c4
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/BooleanBlockProperty.java
@@ -0,0 +1,147 @@
+package cn.nukkit.block.custom.properties;
+
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyMetaException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyPersistenceValueException;
+import com.google.common.base.Preconditions;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+
+public class BooleanBlockProperty extends BlockProperty {
+ private static final long serialVersionUID = 8249827149092664486L;
+
+ public BooleanBlockProperty(String name, boolean exportedToItem, String persistenceName) {
+ super(name, exportedToItem, persistenceName, 1);
+ }
+
+ public BooleanBlockProperty(String name, boolean exportedToItem) {
+ super(name, exportedToItem, name, 1);
+ }
+
+ @Override
+ public int setValue(int currentMeta, int bitOffset, Boolean newValue) {
+ boolean value = newValue != null && newValue;
+ return this.setValue(currentMeta, bitOffset, value);
+ }
+
+ @Override
+ public long setValue(long currentBigMeta, int bitOffset, Boolean newValue) {
+ boolean value = newValue != null && newValue;
+ return this.setValue(currentBigMeta, bitOffset, value);
+ }
+
+ public int setValue(int currentMeta, int bitOffset, boolean newValue) {
+ int mask = 1 << bitOffset;
+ return newValue ? (currentMeta | mask) : (currentMeta & ~mask);
+ }
+
+ @Override
+ public Boolean getValue(int currentMeta, int bitOffset) {
+ return this.getBooleanValue(currentMeta, bitOffset);
+ }
+
+ @Override
+ public Boolean getValue(long currentBigMeta, int bitOffset) {
+ return this.getBooleanValue(currentBigMeta, bitOffset);
+ }
+
+ public boolean getBooleanValue(int currentMeta, int bitOffset) {
+ int mask = 1 << bitOffset;
+ return (currentMeta & mask) == mask;
+ }
+
+ public boolean getBooleanValue(long currentBigMeta, int bitOffset) {
+ long mask = 1L << bitOffset;
+ return (currentBigMeta & mask) == mask;
+ }
+
+ public boolean getBooleanValue(BigInteger currentHugeData, int bitOffset) {
+ BigInteger mask = BigInteger.ONE.shiftLeft(bitOffset);
+ return mask.equals(currentHugeData.and(mask));
+ }
+
+ @Override
+ public int getIntValue(int currentMeta, int bitOffset) {
+ return this.getBooleanValue(currentMeta, bitOffset)? 1 : 0;
+ }
+
+ @Override
+ public int getIntValueForMeta(int meta) {
+ if (meta == 1 || meta == 0) {
+ return meta;
+ }
+ throw new InvalidBlockPropertyMetaException(this, meta, meta, "Only 1 or 0 was expected");
+ }
+
+ @Override
+ public int getMetaForValue(Boolean value) {
+ return Boolean.TRUE.equals(value)? 1 : 0;
+ }
+
+ @Override
+ public Boolean getValueForMeta(int meta) {
+ return this.getBooleanValueForMeta(meta);
+ }
+
+ public boolean getBooleanValueForMeta(int meta) {
+ if (meta == 0) {
+ return false;
+ } else if (meta == 1) {
+ return true;
+ } else {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta, "Only 1 or 0 was expected");
+ }
+ }
+
+ @Override
+ public Serializable getPersistenceValueForMeta(int meta) {
+ if (meta == 1) {
+ return true;
+ } else if (meta == 0) {
+ return false;
+ } else {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta, "Only 1 or 0 was expected");
+ }
+ }
+
+ @Override
+ public int getMetaForPersistenceValue(String persistenceValue) {
+ if ("1".equals(persistenceValue)) {
+ return 1;
+ } else if ("0".equals(persistenceValue)) {
+ return 0;
+ } else {
+ throw new InvalidBlockPropertyPersistenceValueException(this, null, persistenceValue, "Only 1 or 0 was expected");
+ }
+ }
+
+ @Override
+ public Boolean getDefaultValue() {
+ return Boolean.FALSE;
+ }
+
+ @Override
+ public boolean isDefaultValue(Boolean value) {
+ return value == null || Boolean.FALSE.equals(value);
+ }
+
+ @Override
+ protected void validateMetaDirectly(int meta) {
+ Preconditions.checkArgument(meta == 1 || meta == 0, "Must be 1 or 0");
+ }
+
+ @Override
+ public Class getValueClass() {
+ return Boolean.class;
+ }
+
+ @Override
+ public BooleanBlockProperty exportingToItems(boolean exportedToItem) {
+ return new BooleanBlockProperty(this.getName(), exportedToItem, this.getPersistenceName());
+ }
+
+ @Override
+ public BooleanBlockProperty copy() {
+ return new BooleanBlockProperty(this.getName(), this.isExportedToItem(), this.getPersistenceName());
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/EnumBlockProperty.java b/src/main/java/cn/nukkit/block/custom/properties/EnumBlockProperty.java
new file mode 100644
index 00000000000..b21597ea486
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/EnumBlockProperty.java
@@ -0,0 +1,201 @@
+package cn.nukkit.block.custom.properties;
+
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyMetaException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyPersistenceValueException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyValueException;
+import com.google.common.base.Preconditions;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+
+public class EnumBlockProperty extends BlockProperty {
+ private static final long serialVersionUID = 507174531989068430L;
+
+ private final E[] values;
+ // This is null if ordinal = true
+ private final String[] persistenceNames;
+ private final Class typeOf;
+ private final boolean ordinal;
+
+
+ public EnumBlockProperty(String name, boolean exportedToItem, E[] values, int bitSize, String persistenceName) {
+ this(name, exportedToItem, values, bitSize, persistenceName, false);
+ }
+
+ public EnumBlockProperty(String name, boolean exportedToItem, E[] values, int bitSize, String persistenceName, boolean ordinal) {
+ this(name, exportedToItem, values, bitSize, persistenceName, ordinal, ordinal ? null :
+ Arrays.stream(values).map(Objects::toString).map(String::toLowerCase).toArray(String[]::new));
+ }
+
+ public EnumBlockProperty(String name, boolean exportedToItem, E[] values, int bitSize) {
+ this(name, exportedToItem, values, bitSize, name);
+ }
+
+ public EnumBlockProperty(String name, boolean exportedToItem, E[] values) {
+ this(name, exportedToItem, checkUniverseLength(values), BlockPropertyUtils.bitLength(values.length - 1));
+ }
+
+ public EnumBlockProperty(String name, boolean exportedToItem, Class enumClass) {
+ this(name, exportedToItem, enumClass.getEnumConstants());
+ }
+
+ public EnumBlockProperty(String name, boolean exportedToItem, E[] values, int bitSize, String persistenceName, boolean ordinal, String[] persistenceNames) {
+ super(name, exportedToItem, persistenceName, bitSize);
+ checkUniverseLength(values);
+
+ if (ordinal) {
+ this.persistenceNames = null;
+ } else {
+ Preconditions.checkArgument(persistenceNames != null, "persistenceNames can't be null when ordinal is false");
+ Preconditions.checkArgument(persistenceNames.length == values.length, "persistenceNames and universe must have the same length when ordinal is false");
+ this.persistenceNames = persistenceNames.clone();
+ }
+
+ this.ordinal = ordinal;
+ this.values = values.clone();
+ this.typeOf = (Class) values.getClass().getComponentType();
+
+ Set elements = new ObjectOpenHashSet<>();
+ Set persistenceNamesCheck = new ObjectOpenHashSet<>();
+
+ for (int i = 0; i < this.values.length; i++) {
+ E element = this.values[i];
+ Preconditions.checkNotNull(element, "Value can not be null");
+ Preconditions.checkArgument(elements.add(element), "Duplicates are not allowed");
+ if (!ordinal) {
+ String elementName = this.persistenceNames[i];
+ Preconditions.checkNotNull(elementName, "The persistenceNames can not contain null values");
+ Preconditions.checkArgument(persistenceNamesCheck.add(elementName), "The persistenceNames can not have duplicated elements");
+ }
+ }
+ }
+
+ public EnumBlockProperty ordinal(boolean ordinal) {
+ if (ordinal == this.ordinal) {
+ return this;
+ }
+ return new EnumBlockProperty<>(this.getName(), this.isExportedToItem(), this.values, this.getBitSize(), this.getPersistenceName(), ordinal);
+ }
+
+ @Override
+ public int getMetaForValue( E value) {
+ if (value == null) {
+ return 0;
+ }
+ for (int i = 0; i < this.values.length; i++) {
+ if (this.values[i].equals(value)) {
+ return i;
+ }
+ }
+ throw new InvalidBlockPropertyValueException(this, null, value, "Element is not part of this property");
+ }
+
+ @Override
+ public E getValueForMeta(int meta) {
+ return this.values[meta];
+ }
+
+ @Override
+ public int getIntValueForMeta(int meta) {
+ try {
+ this.validateMetaDirectly(meta);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta, e);
+ }
+ return meta;
+ }
+
+ @Override
+ protected void validateDirectly( E value) {
+ for (E object : this.values) {
+ if (object == value) {
+ return;
+ }
+ }
+ throw new IllegalArgumentException(value+" is not valid for this property");
+ }
+
+ @Override
+ protected void validateMetaDirectly(int meta) {
+ Preconditions.checkElementIndex(meta, this.values.length);
+ }
+
+ @Override
+ public Serializable getPersistenceValueForMeta(int meta) {
+ try {
+ this.validateMetaDirectly(meta);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta, e);
+ }
+
+ if (this.isOrdinal()) {
+ return meta;
+ }
+ return persistenceNames[meta];
+ }
+
+ @Override
+ public int getMetaForPersistenceValue(String persistenceValue) {
+ int meta;
+ if (this.isOrdinal()) {
+ try {
+ meta = Integer.parseInt(persistenceValue);
+ this.validateMetaDirectly(meta);
+ } catch (IndexOutOfBoundsException|IllegalArgumentException e) {
+ throw new InvalidBlockPropertyPersistenceValueException(this, null, persistenceValue,
+ "Expected a number from 0 to " + (this.values.length - 1), e);
+ }
+ return meta;
+ }
+
+ for (int index = 0; index < persistenceNames.length; index++) {
+ if (persistenceNames[index].equals(persistenceValue)) {
+ return index;
+ }
+ }
+
+ throw new InvalidBlockPropertyPersistenceValueException(this, null, persistenceValue, "The value does not exists in this property.");
+ }
+
+ public E[] getValues() {
+ return this.values.clone();
+ }
+
+ public boolean isOrdinal() {
+ return this.ordinal;
+ }
+
+ @Override
+ public E getDefaultValue() {
+ return this.values[0];
+ }
+
+ @Override
+ public boolean isDefaultValue( E value) {
+ return value == null || this.values[0].equals(value);
+ }
+
+ @Override
+ public Class getValueClass() {
+ return this.typeOf;
+ }
+
+ @Override
+ public EnumBlockProperty copy() {
+ return new EnumBlockProperty<>(this.getName(), this.isExportedToItem(), values, this.getBitSize(), this.getPersistenceName(), this.isOrdinal(), persistenceNames);
+ }
+
+ @Override
+ public EnumBlockProperty exportingToItems(boolean exportedToItem) {
+ return new EnumBlockProperty<>(this.getName(), exportedToItem, values, this.getBitSize(), this.getPersistenceName(), this.isOrdinal(), persistenceNames);
+ }
+
+ private static E[] checkUniverseLength(E[] universe) {
+ Preconditions.checkNotNull(universe, "universe can't be null");
+ Preconditions.checkArgument(universe.length > 0, "The universe can't be empty");
+ return universe;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/IntBlockProperty.java b/src/main/java/cn/nukkit/block/custom/properties/IntBlockProperty.java
new file mode 100644
index 00000000000..263a8b0a568
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/IntBlockProperty.java
@@ -0,0 +1,154 @@
+package cn.nukkit.block.custom.properties;
+
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyMetaException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyPersistenceValueException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyValueException;
+import cn.nukkit.math.NukkitMath;
+import com.google.common.base.Preconditions;
+
+import java.io.Serializable;
+
+public class IntBlockProperty extends BlockProperty {
+ private static final long serialVersionUID = -2239010977496415152L;
+
+ private final int minValue;
+ private final int maxValue;
+
+ public IntBlockProperty(String name, boolean exportedToItem, int maxValue, int minValue, int bitSize) {
+ this(name, exportedToItem, maxValue, minValue, bitSize, name);
+ }
+
+ public IntBlockProperty(String name, boolean exportedToItem, int maxValue, int minValue) {
+ this(name, exportedToItem, maxValue, minValue, BlockPropertyUtils.bitLength(maxValue - minValue));
+ }
+
+ public IntBlockProperty(String name, boolean exportedToItem, int maxValue) {
+ this(name, exportedToItem, maxValue, 0);
+ }
+
+ public IntBlockProperty(String name, boolean exportedToItem, int maxValue, int minValue, int bitSize, String persistenceName) {
+ super(name, exportedToItem, persistenceName, bitSize);
+ int delta = maxValue - minValue;
+ Preconditions.checkArgument(delta > 0, "maxValue must be higher than minValue. Got min:%s and max:%s", minValue, maxValue);
+
+ int mask = -1 >>> (32 - bitSize);
+ Preconditions.checkArgument(delta <= mask, "The data range from %s to %s can't be stored in %s bits", minValue, maxValue, bitSize);
+
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ }
+
+ @Override
+ public int getMetaForValue(Integer value) {
+ if (value == null) {
+ return 0;
+ }
+ return this.getMetaForValue(value.intValue());
+ }
+
+ public int getMetaForValue(int value) {
+ try {
+ this.validateDirectly(value);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBlockPropertyValueException(this, null, value, e);
+ }
+ return value - minValue;
+ }
+
+ @Override
+ public Integer getValueForMeta(int meta) {
+ return this.getIntValueForMeta(meta);
+ }
+
+ @Override
+ public int getIntValueForMeta(int meta) {
+ try {
+ this.validateMetaDirectly(meta);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta, e);
+ }
+ return minValue + meta;
+ }
+
+ @Override
+ protected void validateDirectly(Integer value) {
+ if (value == null) {
+ return;
+ }
+ this.validateDirectly(value.intValue());
+ }
+
+ private void validateDirectly(int newValue) {
+ Preconditions.checkArgument(newValue >= minValue, "New value (%s) must be higher or equals to %s", newValue, minValue);
+ Preconditions.checkArgument(maxValue >= newValue, "New value (%s) must be less or equals to %s", newValue, maxValue);
+ }
+
+
+ @Override
+ protected void validateMetaDirectly(int meta) {
+ int max = maxValue - minValue;
+ Preconditions.checkArgument(0 <= meta && meta <= max, "The meta %s is outside the range of 0 .. ", meta, max);
+ }
+
+ @Override
+ public Serializable getPersistenceValueForMeta(int meta) {
+ return this.getIntValueForMeta(meta);
+ }
+
+ @Override
+ public int getMetaForPersistenceValue(String persistenceValue) {
+ try {
+ return this.getMetaForValue(Integer.parseInt(persistenceValue));
+ } catch (NumberFormatException | InvalidBlockPropertyValueException e) {
+ throw new InvalidBlockPropertyPersistenceValueException(this, null, persistenceValue, e);
+ }
+ }
+
+ public int clamp(int value) {
+ return NukkitMath.clamp(value, this.getMinValue(), this.getMaxValue());
+ }
+
+
+ public int getMaxValue() {
+ return this.maxValue;
+ }
+
+ public int getMinValue() {
+ return this.minValue;
+ }
+
+ @Override
+ public Integer getDefaultValue() {
+ return this.minValue;
+ }
+
+ @Override
+ public boolean isDefaultIntValue(int value) {
+ return this.minValue == value;
+ }
+
+ @Override
+ public int getDefaultIntValue() {
+ return this.minValue;
+ }
+
+ @Override
+ public boolean isDefaultValue(Integer value) {
+ return value == null || this.minValue == value;
+ }
+
+ @Override
+ public Class getValueClass() {
+ return Integer.class;
+ }
+
+ @Override
+ public IntBlockProperty exportingToItems(boolean exportedToItem) {
+ return new IntBlockProperty(this.getName(), exportedToItem, this.getMaxValue(), this.getMinValue(), this.getBitSize(), this.getPersistenceName());
+ }
+
+ @Override
+ public IntBlockProperty copy() {
+ return new IntBlockProperty(this.getName(), this.isExportedToItem(), this.getMaxValue(), this.getMinValue(), this.getBitSize(), this.getPersistenceName());
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/RegisteredBlockProperty.java b/src/main/java/cn/nukkit/block/custom/properties/RegisteredBlockProperty.java
new file mode 100644
index 00000000000..aaa02cba3c1
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/RegisteredBlockProperty.java
@@ -0,0 +1,28 @@
+package cn.nukkit.block.custom.properties;
+
+import lombok.Value;
+
+import java.math.BigInteger;
+
+@Value
+public class RegisteredBlockProperty {
+ BlockProperty> property;
+ int offset;
+
+ public void validateMeta(int meta) {
+ this.property.validateMeta(meta, this.offset);
+ }
+
+ public void validateMeta(long meta) {
+ this.property.validateMeta(meta, this.offset);
+ }
+
+ public void validateMeta(BigInteger meta) {
+ this.property.validateMeta(meta, this.offset);
+ }
+
+ @Override
+ public String toString() {
+ return this.offset + "-" + (this.offset + this.property.getBitSize()) + ":" + this.property.getName();
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/UnsignedIntBlockProperty.java b/src/main/java/cn/nukkit/block/custom/properties/UnsignedIntBlockProperty.java
new file mode 100644
index 00000000000..5af9052a071
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/UnsignedIntBlockProperty.java
@@ -0,0 +1,155 @@
+package cn.nukkit.block.custom.properties;
+
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyMetaException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyPersistenceValueException;
+import cn.nukkit.block.custom.properties.exception.InvalidBlockPropertyValueException;
+import com.google.common.base.Preconditions;
+
+import java.io.Serializable;
+
+public class UnsignedIntBlockProperty extends BlockProperty {
+ private static final long serialVersionUID = 7896101036099245755L;
+
+ private final long minValue;
+ private final long maxValue;
+
+ public UnsignedIntBlockProperty(String name, boolean exportedToItem, int maxValue, int minValue, int bitSize) {
+ this(name, exportedToItem, maxValue, minValue, bitSize, name);
+ }
+
+ public UnsignedIntBlockProperty(String name, boolean exportedToItem, int maxValue, int minValue) {
+ this(name, exportedToItem, maxValue, minValue, BlockPropertyUtils.bitLength(maxValue - minValue));
+ }
+
+ public UnsignedIntBlockProperty(String name, boolean exportedToItem, int maxValue) {
+ this(name, exportedToItem, maxValue, 0);
+ }
+
+ public UnsignedIntBlockProperty(String name, boolean exportedToItem, int maxValue, int minValue, int bitSize, String persistenceName) {
+ super(name, exportedToItem, persistenceName, bitSize);
+ long unsignedMinValue = removeSign(minValue);
+ long unsignedMaxValue = removeSign(maxValue);
+ long delta = unsignedMaxValue - unsignedMinValue;
+ Preconditions.checkArgument(delta > 0, "maxValue must be higher than minValue. Got min:%s and max:%s", unsignedMinValue, unsignedMaxValue);
+
+ long mask = removeSign(-1 >>> (32 - bitSize));
+ Preconditions.checkArgument(delta <= mask, "The data range from %s to %s can't be stored in %s bits", unsignedMinValue, unsignedMaxValue, bitSize);
+
+ this.minValue = unsignedMinValue;
+ this.maxValue = unsignedMaxValue;
+ }
+
+ @Override
+ public int getMetaForValue(Integer value) {
+ if (value == null) {
+ return 0;
+ }
+
+ long unsigned = removeSign(value);
+ try {
+ this.validateDirectly(unsigned);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBlockPropertyValueException(this, null, value, e);
+ }
+ return (int) (unsigned - minValue);
+ }
+
+ @Override
+ public Integer getValueForMeta(int meta) {
+ return this.getIntValueForMeta(meta);
+ }
+
+ @Override
+ public int getIntValueForMeta(int meta) {
+ try {
+ this.validateMetaDirectly(meta);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidBlockPropertyMetaException(this, meta, meta, e);
+ }
+ return (int) (this.minValue + meta);
+ }
+
+ @Override
+ protected void validateDirectly(Integer value) {
+ if (value == null) {
+ return;
+ }
+ this.validateDirectly(removeSign(value));
+ }
+
+ private void validateDirectly(long unsigned) {
+ Preconditions.checkArgument(unsigned >= this.minValue, "New value (%s) must be higher or equals to %s", unsigned, this.minValue);
+ Preconditions.checkArgument(this.maxValue >= unsigned, "New value (%s) must be less or equals to %s", unsigned, this.maxValue);
+ }
+
+ @Override
+ protected void validateMetaDirectly(int meta) {
+ long max = this.maxValue - this.minValue;
+ Preconditions.checkArgument(0 <= meta && meta <= max, "The meta %s is outside the range of 0 .. ", meta, max);
+ }
+
+ public long getMaxValue() {
+ return this.maxValue;
+ }
+
+ public long getMinValue() {
+ return this.minValue;
+ }
+
+ @Override
+ public Integer getDefaultValue() {
+ return (int) this.minValue;
+ }
+
+ @Override
+ public boolean isDefaultValue(Integer value) {
+ return value == null || removeSign(value) == this.minValue;
+ }
+
+ @Override
+ public boolean isDefaultIntValue(int value) {
+ return removeSign(value) == this.minValue;
+ }
+
+ @Override
+ public int getDefaultIntValue() {
+ return (int) this.minValue;
+ }
+
+ @Override
+ public Serializable getPersistenceValueForMeta(int meta) {
+ return removeSign(this.getIntValueForMeta(meta));
+ }
+
+ @Override
+ public int getMetaForPersistenceValue(String persistenceValue) {
+ try {
+ return this.getMetaForValue(addSign(Long.parseLong(persistenceValue)));
+ } catch (NumberFormatException | InvalidBlockPropertyValueException e) {
+ throw new InvalidBlockPropertyPersistenceValueException(this, null, persistenceValue, e);
+ }
+ }
+
+ @Override
+ public UnsignedIntBlockProperty copy() {
+ return new UnsignedIntBlockProperty(this.getName(), this.isExportedToItem(), (int) this.getMaxValue(), (int) this.getMinValue(), this.getBitSize(), this.getPersistenceName());
+ }
+
+ @Override
+ public UnsignedIntBlockProperty exportingToItems(boolean exportedToItem) {
+ return new UnsignedIntBlockProperty(this.getName(), exportedToItem, (int) this.getMaxValue(), (int) this.getMinValue(), this.getBitSize(), this.getPersistenceName());
+ }
+
+ @Override
+ public Class getValueClass() {
+ return Integer.class;
+ }
+
+ private static long removeSign(int value) {
+ return (long) value & 0xFFFFFFFFL;
+ }
+
+ private static int addSign(long value) {
+ return (int) (value & 0xFFFFFFFFL);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/custom/properties/exception/BlockPropertyNotFoundException.java b/src/main/java/cn/nukkit/block/custom/properties/exception/BlockPropertyNotFoundException.java
new file mode 100644
index 00000000000..c10b65b3787
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/exception/BlockPropertyNotFoundException.java
@@ -0,0 +1,28 @@
+package cn.nukkit.block.custom.properties.exception;
+
+import cn.nukkit.block.custom.properties.BlockProperties;
+
+import java.util.NoSuchElementException;
+
+public class BlockPropertyNotFoundException extends NoSuchElementException {
+ private final String propertyName;
+
+ public BlockPropertyNotFoundException(String propertyName) {
+ super("The property \""+propertyName+"\" was not found.");
+ this.propertyName = propertyName;
+ }
+
+ public BlockPropertyNotFoundException(String propertyName, String details) {
+ super(propertyName+": " + details);
+ this.propertyName = propertyName;
+ }
+
+ public BlockPropertyNotFoundException(String propertyName, BlockProperties properties) {
+ super("The property " + propertyName + " was not found. Valid properties: " + properties.getNames());
+ this.propertyName = propertyName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyException.java b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyException.java
new file mode 100644
index 00000000000..7bb55c4ee76
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyException.java
@@ -0,0 +1,37 @@
+package cn.nukkit.block.custom.properties.exception;
+
+import cn.nukkit.block.custom.properties.BlockProperty;
+
+public class InvalidBlockPropertyException extends IllegalArgumentException {
+ private static final long serialVersionUID = -6934630506175381230L;
+
+ private final BlockProperty> property;
+
+ public InvalidBlockPropertyException(BlockProperty> property) {
+ super(buildMessage(property));
+ this.property = property;
+ }
+
+ public InvalidBlockPropertyException(BlockProperty> property, String message) {
+ super(buildMessage(property) + ". "+message);
+ this.property = property;
+ }
+
+ public InvalidBlockPropertyException(BlockProperty> property, String message, Throwable cause) {
+ super(buildMessage(property) + ". "+message, cause);
+ this.property = property;
+ }
+
+ public InvalidBlockPropertyException(BlockProperty> property, Throwable cause) {
+ super(buildMessage(property), cause);
+ this.property = property;
+ }
+
+ private static String buildMessage(BlockProperty> property) {
+ return "Property: " + property.getName();
+ }
+
+ public BlockProperty> getProperty() {
+ return property;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyMetaException.java b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyMetaException.java
new file mode 100644
index 00000000000..db889ed8d51
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyMetaException.java
@@ -0,0 +1,46 @@
+package cn.nukkit.block.custom.properties.exception;
+
+import cn.nukkit.block.custom.properties.BlockProperty;
+
+public class InvalidBlockPropertyMetaException extends InvalidBlockPropertyException {
+ private static final long serialVersionUID = -8493494844859767053L;
+
+ private final Number currentMeta;
+ private final Number invalidMeta;
+
+ public InvalidBlockPropertyMetaException(BlockProperty> property, Number currentMeta, Number invalidMeta) {
+ super(property, buildMessage(currentMeta, invalidMeta));
+ this.currentMeta = currentMeta;
+ this.invalidMeta = invalidMeta;
+ }
+
+ public InvalidBlockPropertyMetaException(BlockProperty> property, Number currentMeta, Number invalidMeta, String message) {
+ super(property, buildMessage(currentMeta, invalidMeta)+". "+message);
+ this.currentMeta = currentMeta;
+ this.invalidMeta = invalidMeta;
+ }
+
+ public InvalidBlockPropertyMetaException(BlockProperty> property, Number currentMeta, Number invalidMeta, String message, Throwable cause) {
+ super(property, buildMessage(currentMeta, invalidMeta)+". "+message, cause);
+ this.currentMeta = currentMeta;
+ this.invalidMeta = invalidMeta;
+ }
+
+ public InvalidBlockPropertyMetaException(BlockProperty> property, Number currentMeta, Number invalidMeta, Throwable cause) {
+ super(property, buildMessage(currentMeta, invalidMeta), cause);
+ this.currentMeta = currentMeta;
+ this.invalidMeta = invalidMeta;
+ }
+
+ private static String buildMessage(Object currentValue, Object invalidValue) {
+ return "Current Meta: "+currentValue+", Invalid Meta: "+invalidValue;
+ }
+
+ public Number getCurrentMeta() {
+ return this.currentMeta;
+ }
+
+ public Number getInvalidMeta() {
+ return this.invalidMeta;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyPersistenceValueException.java b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyPersistenceValueException.java
new file mode 100644
index 00000000000..95ac8d1a6f5
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyPersistenceValueException.java
@@ -0,0 +1,47 @@
+package cn.nukkit.block.custom.properties.exception;
+
+import cn.nukkit.block.custom.properties.BlockProperty;
+
+public class InvalidBlockPropertyPersistenceValueException extends InvalidBlockPropertyException {
+ private static final long serialVersionUID = 1L;
+
+ private final String currentValue;
+ private final String invalidValue;
+
+ public InvalidBlockPropertyPersistenceValueException(BlockProperty> property, String currentValue, String invalidValue) {
+ super(property, buildMessage(currentValue, invalidValue));
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+
+ public InvalidBlockPropertyPersistenceValueException(BlockProperty> property, String currentValue, String invalidValue, String message) {
+ super(property, buildMessage(currentValue, invalidValue)+". "+message);
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+ public InvalidBlockPropertyPersistenceValueException(BlockProperty> property, String currentValue, String invalidValue, String message, Throwable cause) {
+ super(property, buildMessage(currentValue, invalidValue)+". "+message, cause);
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+ public InvalidBlockPropertyPersistenceValueException(BlockProperty> property, String currentValue, String invalidValue, Throwable cause) {
+ super(property, buildMessage(currentValue, invalidValue), cause);
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+ private static String buildMessage(Object currentValue, Object invalidValue) {
+ return "Current Value: "+currentValue+", Invalid Value: "+invalidValue;
+ }
+
+ public String getCurrentValue() {
+ return this.currentValue;
+ }
+
+ public String getInvalidValue() {
+ return this.invalidValue;
+ }
+}
diff --git a/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyValueException.java b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyValueException.java
new file mode 100644
index 00000000000..f68d28edc11
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/custom/properties/exception/InvalidBlockPropertyValueException.java
@@ -0,0 +1,49 @@
+package cn.nukkit.block.custom.properties.exception;
+
+import cn.nukkit.block.custom.properties.BlockProperty;
+
+import java.io.Serializable;
+
+public class InvalidBlockPropertyValueException extends InvalidBlockPropertyException {
+ private static final long serialVersionUID = -1087431932428639175L;
+
+ private final Serializable currentValue;
+ private final Serializable invalidValue;
+
+ public InvalidBlockPropertyValueException(BlockProperty> property, Serializable currentValue, Serializable invalidValue) {
+ super(property, buildMessage(currentValue, invalidValue));
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+ public InvalidBlockPropertyValueException(BlockProperty> property, Serializable currentValue, Serializable invalidValue, String message) {
+ super(property, buildMessage(currentValue, invalidValue)+". "+message);
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+ public InvalidBlockPropertyValueException(BlockProperty> property, Serializable currentValue, Serializable invalidValue, String message, Throwable cause) {
+ super(property, buildMessage(currentValue, invalidValue)+". "+message, cause);
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+ public InvalidBlockPropertyValueException(BlockProperty> property, Serializable currentValue, Serializable invalidValue, Throwable cause) {
+ super(property, buildMessage(currentValue, invalidValue), cause);
+ this.currentValue = currentValue;
+ this.invalidValue = invalidValue;
+ }
+
+ private static String buildMessage(Object currentValue, Object invalidValue) {
+ return "Current Value: "+currentValue+", Invalid Value: "+invalidValue;
+ }
+
+
+ public Serializable getCurrentValue() {
+ return this.currentValue;
+ }
+
+ public Serializable getInvalidValue() {
+ return this.invalidValue;
+ }
+}
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..6107ac6e16f
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/BlockPropertiesHelper.java
@@ -0,0 +1,26 @@
+package cn.nukkit.block.properties;
+
+import cn.nukkit.block.custom.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..5215b3fad33
--- /dev/null
+++ b/src/main/java/cn/nukkit/block/properties/VanillaProperties.java
@@ -0,0 +1,20 @@
+package cn.nukkit.block.properties;
+
+import cn.nukkit.block.custom.properties.BlockProperty;
+import cn.nukkit.block.custom.properties.BooleanBlockProperty;
+import cn.nukkit.block.custom.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..a7433392530 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";
@@ -32,6 +34,7 @@ public abstract class BlockEntity extends Position {
public static final String DAYLIGHT_DETECTOR = "DaylightDetector";
public static final String MUSIC = "Music";
public static final String ITEM_FRAME = "ItemFrame";
+ public static final String GLOW_ITEM_FRAME = "GlowItemFrame";
public static final String CAULDRON = "Cauldron";
public static final String BEACON = "Beacon";
public static final String PISTON_ARM = "PistonArm";
@@ -42,41 +45,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();
@@ -89,7 +100,6 @@ 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 +109,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 +130,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 +168,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.isEmpty()) {
return tag;
} else {
return null;
@@ -170,6 +179,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 +198,9 @@ public boolean onUpdate() {
}
public final void scheduleUpdate() {
+ if (this.level.isBeingConverted) {
+ return;
+ }
this.level.scheduleBlockEntityUpdate(this);
}
@@ -198,7 +222,9 @@ public void onBreak() {
}
public void setDirty() {
- chunk.setChanged();
+ if (this.chunk != null) {
+ this.chunk.setChanged();
+ }
}
public String getName() {
@@ -210,10 +236,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 ac72e7a1771..0b60b55f955 100644
--- a/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java
+++ b/src/main/java/cn/nukkit/blockentity/BlockEntityBeacon.java
@@ -4,6 +4,7 @@
import cn.nukkit.Server;
import cn.nukkit.block.Block;
import cn.nukkit.event.entity.EntityPotionEffectEvent;
+import cn.nukkit.inventory.BeaconInventory;
import cn.nukkit.inventory.Inventory;
import cn.nukkit.item.Item;
import cn.nukkit.level.format.FullChunk;
@@ -16,10 +17,12 @@
import java.util.Map;
/**
- * author: Rover656
+ * @author Rover656
*/
public class BlockEntityBeacon extends BlockEntitySpawnable {
+ private long currentTick;
+
public BlockEntityBeacon(FullChunk chunk, CompoundTag nbt) {
super(chunk, nbt);
}
@@ -42,15 +45,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
@@ -66,22 +68,28 @@ public CompoundTag getSpawnCompound() {
.putInt("Secondary", this.namedTag.getInt("Secondary"));
}
- private long currentTick = 0;
-
@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);
}
@@ -92,18 +100,15 @@ public boolean onUpdate() {
this.getLevel().addLevelSoundEvent(this, LevelSoundEventPacket.SOUND_BEACON_AMBIENT);
}
- //Get all players in game
- Map players = this.level.getPlayers();
+ int powerLevel = getPowerLevel();
+ //In seconds
+ int duration = (9 + (powerLevel << 1)) * 20;
- //Calculate vars for beacon power
- int range = 10 + getPowerLevel() * 10;
- int duration = 9 + getPowerLevel() * 2;
-
- 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) {
@@ -111,7 +116,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()) {
@@ -133,10 +138,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);
@@ -144,6 +149,10 @@ public boolean onUpdate() {
//Add effect
p.addEffect(e, EntityPotionEffectEvent.Cause.BEACON);
}
+
+ if (powerLevel >= POWER_LEVEL_MAX) {
+ p.awardAchievement("fullBeacon");
+ }
}
}
@@ -158,9 +167,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;
}
@@ -181,16 +190,20 @@ 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 &&
testBlockId != Block.EMERALD_BLOCK &&
- testBlockId != Block.DIAMOND_BLOCK
- ) {
+ testBlockId != Block.DIAMOND_BLOCK &&
+ testBlockId != Block.NETHERITE_BLOCK
+ ) {
return powerLevel - 1;
}
-
}
}
}
@@ -198,6 +211,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");
}
@@ -207,7 +240,6 @@ public void setPowerLevel(int level) {
if (level != currentLevel) {
namedTag.putInt("Level", level);
setDirty();
- this.spawnToAll();
}
}
@@ -220,7 +252,6 @@ public void setPrimaryPower(int power) {
if (power != currentPower) {
namedTag.putInt("Primary", power);
setDirty();
- this.spawnToAll();
}
}
@@ -233,7 +264,6 @@ public void setSecondaryPower(int power) {
if (power != currentPower) {
namedTag.putInt("Secondary", power);
setDirty();
- this.spawnToAll();
}
}
@@ -247,6 +277,10 @@ public boolean updateCompoundTag(CompoundTag nbt, Player player) {
Inventory inv = player.getWindowById(Player.BEACON_WINDOW_ID);
if (inv != null) {
+ if (!BeaconInventory.ITEMS.contains(inv.getItemFast(0).getId())) {
+ Server.getInstance().getLogger().debug(player.getName() + " tried to set effect but there's no payment in beacon inventory");
+ return false;
+ }
inv.setItem(0, Item.get(Item.AIR));
} else {
Server.getInstance().getLogger().debug(player.getName() + " tried to set effect but beacon inventory is null");
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..30e788bf4d6
--- /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 (this.closed) {
+ return false;
+ }
+
+ 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() {
+ 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..2ba09419372 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