diff --git a/pom.xml b/pom.xml
index 9e9bea97006..b7532d2ad66 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,12 @@
+
+ com.nukkitx.network
+ raknet
+ 1.6.15
+ compile
+ com.nukkitxfastutil-lite
@@ -90,19 +96,6 @@
-
- io.netty
- netty-handler
- 4.1.6.Final
- compile
-
-
- io.netty
- netty-transport-native-epoll
- 4.1.6.Final
- compile
- linux-x86_64
- org.junit.jupiterjunit-jupiter-api
@@ -191,6 +184,20 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.2
+
+
+
+ true
+ lib/
+ cn.nukkit.Nukkit
+
+
+
+ maven-compiler-plugin3.1
@@ -257,14 +264,10 @@
-
- cn.nukkit.Nukkit
-
- ${project.build.directory}/dependency-reduced-pom.xml
diff --git a/src/main/java/cn/nukkit/Nukkit.java b/src/main/java/cn/nukkit/Nukkit.java
index ce0fea09dd0..89b2f6344b5 100644
--- a/src/main/java/cn/nukkit/Nukkit.java
+++ b/src/main/java/cn/nukkit/Nukkit.java
@@ -3,6 +3,9 @@
import cn.nukkit.network.protocol.ProtocolInfo;
import cn.nukkit.utils.ServerKiller;
import com.google.common.base.Preconditions;
+import io.netty.util.ResourceLeakDetector;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.netty.util.internal.logging.Log4J2LoggerFactory;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
@@ -63,6 +66,10 @@ public static void main(String[] args) {
// Force Mapped ByteBuffers for LevelDB till fixed.
System.setProperty("leveldb.mmap", "true");
+ // Netty logger for debug info
+ InternalLoggerFactory.setDefaultFactory(Log4J2LoggerFactory.INSTANCE);
+ ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
+
// Define args
OptionParser parser = new OptionParser();
parser.allowsUnrecognizedOptions();
diff --git a/src/main/java/cn/nukkit/Player.java b/src/main/java/cn/nukkit/Player.java
index eb9c7666813..5d2bc81bd84 100644
--- a/src/main/java/cn/nukkit/Player.java
+++ b/src/main/java/cn/nukkit/Player.java
@@ -62,7 +62,6 @@
import cn.nukkit.potion.Effect;
import cn.nukkit.resourcepacks.ResourcePack;
import cn.nukkit.scheduler.AsyncTask;
-import cn.nukkit.scheduler.Task;
import cn.nukkit.utils.*;
import co.aikar.timings.Timing;
import co.aikar.timings.Timings;
@@ -161,10 +160,9 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde
protected Vector3 teleportPosition = null;
protected boolean connected = true;
- protected final String ip;
+ protected final InetSocketAddress socketAddress;
protected boolean removeFormat = true;
- protected final int port;
protected String username;
protected String iusername;
protected String displayName;
@@ -202,8 +200,6 @@ public class Player extends EntityHuman implements CommandSender, InventoryHolde
protected boolean checkMovement = true;
- private final Int2ObjectOpenHashMap needACK = new Int2ObjectOpenHashMap<>();
-
private final Map> batchedPackets = new TreeMap<>();
private PermissibleBase perm = null;
@@ -591,16 +587,7 @@ public void sendCommandData() {
if (count > 0) {
//TODO: structure checking
pk.commands = data;
- int identifier = this.dataPacket(pk, true); // We *need* ACK so we can be sure that the client received the packet or not
- Server.getInstance().getScheduler().scheduleDelayedTask(new Task() {
- @Override
- public void onRun(int currentTick) {
- Boolean status = needACK.get(identifier);
- if ((status == null || !status) && isOnline()) {
- sendCommandData();
- }
- }
- }, 60, true);
+ this.dataPacket(pk);
}
}
@@ -609,14 +596,13 @@ public Map getEffectivePermissions() {
return this.perm.getEffectivePermissions();
}
- public Player(SourceInterface interfaz, Long clientID, String ip, int port) {
+ public Player(SourceInterface interfaz, Long clientID, InetSocketAddress socketAddress) {
super(null, new CompoundTag());
this.interfaz = interfaz;
this.perm = new PermissibleBase(this);
this.server = Server.getInstance();
this.lastBreak = -1;
- this.ip = ip;
- this.port = port;
+ this.socketAddress = socketAddress;
this.clientID = clientID;
this.loaderId = Level.generateChunkLoaderId(this);
this.chunksPerTick = this.server.getConfig("chunk-sending.per-tick", 4);
@@ -679,11 +665,15 @@ public void setSkin(Skin skin) {
}
public String getAddress() {
- return this.ip;
+ return this.socketAddress.getAddress().getHostAddress();
}
public int getPort() {
- return port;
+ return this.socketAddress.getPort();
+ }
+
+ public InetSocketAddress getSocketAddress() {
+ return this.socketAddress;
}
public Position getNextPosition() {
@@ -1060,33 +1050,24 @@ public boolean batchDataPacket(DataPacket packet) {
* @return packet successfully sent
*/
public boolean dataPacket(DataPacket packet) {
- return this.dataPacket(packet, false) != -1;
- }
-
- public int dataPacket(DataPacket packet, boolean needACK) {
if (!this.connected) {
- return -1;
+ return false;
}
-
try (Timing timing = Timings.getSendDataPacketTiming(packet)) {
DataPacketSendEvent ev = new DataPacketSendEvent(this, packet);
this.server.getPluginManager().callEvent(ev);
if (ev.isCancelled()) {
- return -1;
- }
-
- if (log.isTraceEnabled() && !(packet instanceof BatchPacket)) {
- log.trace("Outbound {}: {}", this.getName(), packet);
+ return false;
}
- Integer identifier = this.interfaz.putPacket(this, packet, needACK, false);
-
- if (needACK && identifier != null) {
- this.needACK.put(identifier, Boolean.FALSE);
- return identifier;
- }
+ this.interfaz.putPacket(this, packet, false, false);
}
- return 0;
+ return true;
+ }
+
+ @Deprecated
+ public int dataPacket(DataPacket packet, boolean needACK) {
+ return this.dataPacket(packet) ? 0 : -1;
}
/**
@@ -1097,29 +1078,25 @@ public int dataPacket(DataPacket packet, boolean needACK) {
* @return packet successfully sent
*/
public boolean directDataPacket(DataPacket packet) {
- return this.directDataPacket(packet, false) != -1;
- }
-
- public int directDataPacket(DataPacket packet, boolean needACK) {
if (!this.connected) {
- return -1;
+ return false;
}
try (Timing timing = Timings.getSendDataPacketTiming(packet)) {
DataPacketSendEvent ev = new DataPacketSendEvent(this, packet);
this.server.getPluginManager().callEvent(ev);
if (ev.isCancelled()) {
- return -1;
+ return false;
}
- Integer identifier = this.interfaz.putPacket(this, packet, needACK, true);
-
- if (needACK && identifier != null) {
- this.needACK.put(identifier, Boolean.FALSE);
- return identifier;
- }
+ this.interfaz.putPacket(this, packet, false, true);
}
- return 0;
+ return true;
+ }
+
+ @Deprecated
+ public int directDataPacket(DataPacket packet, boolean needACK) {
+ return this.directDataPacket(packet) ? 0 : -1;
}
public int getPing() {
@@ -2060,8 +2037,8 @@ protected void completeLoginSequence() {
this.server.getLogger().info(this.getServer().getLanguage().translateString("nukkit.player.logIn",
TextFormat.AQUA + this.username + TextFormat.WHITE,
- this.ip,
- String.valueOf(this.port),
+ this.getAddress(),
+ String.valueOf(this.getPort()),
String.valueOf(this.id),
this.level.getName(),
String.valueOf(NukkitMath.round(this.x, 4)),
@@ -2199,7 +2176,7 @@ public void handleDataPacket(DataPacket packet) {
@Override
public void onRun() {
- e = new PlayerAsyncPreLoginEvent(username, uuid, ip, port);
+ e = new PlayerAsyncPreLoginEvent(username, uuid, Player.this.getAddress(), Player.this.getPort());
server.getPluginManager().callEvent(e);
}
@@ -3025,7 +3002,7 @@ public void onCompletion(Server server) {
if (spamBug) {
return;
}
-
+
this.setDataFlag(DATA_FLAGS, DATA_FLAG_ACTION, false);
if (this.canInteract(blockVector.add(0.5, 0.5, 0.5), this.isCreative() ? 13 : 7)) {
@@ -3630,8 +3607,8 @@ public void close(TextContainer message, String reason, boolean notify) {
this.spawned = false;
this.server.getLogger().info(this.getServer().getLanguage().translateString("nukkit.player.logOut",
TextFormat.AQUA + (this.getName() == null ? "" : this.getName()) + TextFormat.WHITE,
- this.ip,
- String.valueOf(this.port),
+ this.getAddress(),
+ String.valueOf(this.getPort()),
this.getServer().getLanguage().translateString(reason)));
this.windows.clear();
this.usedChunks.clear();
@@ -4875,15 +4852,6 @@ public boolean equals(Object obj) {
return Objects.equals(this.getUniqueId(), other.getUniqueId()) && this.getId() == other.getId();
}
- /**
- * Notifies an ACK response from the client
- *
- * @param identification packet identity
- */
- public void notifyACK(int identification) {
- needACK.put(identification, Boolean.TRUE);
- }
-
public boolean isBreakingBlock() {
return this.breakingBlock != null;
}
diff --git a/src/main/java/cn/nukkit/Server.java b/src/main/java/cn/nukkit/Server.java
index 570f3c68488..ab6c5054540 100644
--- a/src/main/java/cn/nukkit/Server.java
+++ b/src/main/java/cn/nukkit/Server.java
@@ -80,6 +80,7 @@
import co.aikar.timings.Timings;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
+import io.netty.buffer.ByteBuf;
import lombok.extern.log4j.Log4j2;
import org.iq80.leveldb.CompressionType;
import org.iq80.leveldb.DB;
@@ -90,6 +91,9 @@
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.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
@@ -204,12 +208,10 @@ public class Server {
private Config properties;
private Config config;
- private final Map players = new HashMap<>();
+ private final Map players = new HashMap<>();
private final Map playerList = new HashMap<>();
- private final Map identifier = new HashMap<>();
-
private final Map levels = new HashMap() {
public Level put(Integer key, Level value) {
Level result = super.put(key, value);
@@ -654,10 +656,6 @@ public static void broadcastPacket(Player[] players, DataPacket packet) {
} else {
getInstance().batchPackets(players, new DataPacket[]{packet}, true);
}
-
- if (packet.encapsulatedPacket != null) {
- packet.encapsulatedPacket = null;
- }
}
public void batchPackets(Player[] players, DataPacket[] packets) {
@@ -691,10 +689,10 @@ public void batchPackets(Player[] players, DataPacket[] packets, boolean forceSy
size += payload[i * 2 + 1].length;
}
- List targets = new ArrayList<>();
+ List targets = new ArrayList<>();
for (Player p : players) {
if (p.isConnected()) {
- targets.add(this.identifier.get(p.rawHashCode()));
+ targets.add(p.getSocketAddress());
}
}
@@ -711,11 +709,11 @@ public void batchPackets(Player[] players, DataPacket[] packets, boolean forceSy
Timings.playerNetworkSendTimer.stopTiming();
}
- public void broadcastPacketsCallback(byte[] data, List identifiers) {
+ public void broadcastPacketsCallback(byte[] data, List targets) {
BatchPacket pk = new BatchPacket();
pk.payload = data;
- for (String i : identifiers) {
+ for (InetSocketAddress i : targets) {
if (this.players.containsKey(i)) {
this.players.get(i).dataPacket(pk);
}
@@ -798,7 +796,11 @@ public void reload() {
this.operators.reload();
for (BanEntry entry : this.getIPBans().getEntires().values()) {
- this.getNetwork().blockAddress(entry.getName(), -1);
+ try {
+ this.getNetwork().blockAddress(InetAddress.getByName(entry.getName()), -1);
+ } catch (UnknownHostException e) {
+ // ignore
+ }
}
this.pluginManager.registerInterface(JavaPluginLoader.class);
@@ -876,7 +878,11 @@ public void start() {
}
for (BanEntry entry : this.getIPBans().getEntires().values()) {
- this.network.blockAddress(entry.getName(), -1);
+ try {
+ this.network.blockAddress(InetAddress.getByName(entry.getName()), -1);
+ } catch (UnknownHostException e) {
+ // ignore
+ }
}
//todo send usage setting
@@ -890,15 +896,24 @@ public void start() {
this.forceShutdown();
}
- public void handlePacket(String address, int port, byte[] payload) {
+ public void handlePacket(InetSocketAddress address, ByteBuf payload) {
try {
- if (payload.length > 2 && Arrays.equals(Binary.subBytes(payload, 0, 2), new byte[]{(byte) 0xfe, (byte) 0xfd}) && this.queryHandler != null) {
- this.queryHandler.handle(address, port, payload);
+ if (!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) {
+ this.queryHandler.handle(address, payload);
}
} catch (Exception e) {
log.error("Error whilst handling packet", e);
- this.getNetwork().blockAddress(address, 600);
+ this.network.blockAddress(address.getAddress(), -1);
}
}
@@ -955,9 +970,8 @@ public void onPlayerLogin(Player player) {
}
}
- public void addPlayer(String identifier, Player player) {
- this.players.put(identifier, player);
- this.identifier.put(player.rawHashCode(), identifier);
+ public void addPlayer(InetSocketAddress socketAddress, Player player) {
+ this.players.put(socketAddress, player);
}
public void addOnlinePlayer(Player player) {
@@ -1807,18 +1821,15 @@ public Player[] matchPlayer(String partialName) {
}
public void removePlayer(Player player) {
- if (this.identifier.containsKey(player.rawHashCode())) {
- String identifier = this.identifier.get(player.rawHashCode());
- this.players.remove(identifier);
- this.identifier.remove(player.rawHashCode());
+ Player toRemove = this.players.remove(player.getSocketAddress());
+ if (toRemove != null) {
return;
}
- for (String identifier : new ArrayList<>(this.players.keySet())) {
- Player p = this.players.get(identifier);
+ for (InetSocketAddress socketAddress : new ArrayList<>(this.players.keySet())) {
+ Player p = this.players.get(socketAddress);
if (player == p) {
- this.players.remove(identifier);
- this.identifier.remove(player.rawHashCode());
+ this.players.remove(socketAddress);
break;
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java b/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java
index 44a6c65d9f7..3a6f9f404fc 100644
--- a/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/BanIpCommand.java
@@ -13,6 +13,8 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.regex.Pattern;
@@ -101,6 +103,10 @@ private void processIPBan(String ip, CommandSender sender, String reason) {
}
}
- sender.getServer().getNetwork().blockAddress(ip, -1);
+ try {
+ sender.getServer().getNetwork().blockAddress(InetAddress.getByName(ip), -1);
+ } catch (UnknownHostException e) {
+ // ignore
+ }
}
}
diff --git a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java
index 0fea580f328..bc61968664f 100644
--- a/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java
+++ b/src/main/java/cn/nukkit/command/defaults/PardonIpCommand.java
@@ -5,6 +5,8 @@
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.lang.TranslationContainer;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.regex.Pattern;
/**
@@ -39,7 +41,13 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
if (Pattern.matches("^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$", value)) {
sender.getServer().getIPBans().remove(value);
- sender.getServer().getNetwork().unblockAddress(value);
+
+ try {
+ sender.getServer().getNetwork().unblockAddress(InetAddress.getByName(value));
+ } catch (UnknownHostException e) {
+ sender.sendMessage(new TranslationContainer("commands.unbanip.invalid"));
+ return true;
+ }
Command.broadcastCommandMessage(sender, new TranslationContainer("commands.unbanip.success", value));
} else {
diff --git a/src/main/java/cn/nukkit/event/player/PlayerCreationEvent.java b/src/main/java/cn/nukkit/event/player/PlayerCreationEvent.java
index dd408ec13b5..be48b28a203 100644
--- a/src/main/java/cn/nukkit/event/player/PlayerCreationEvent.java
+++ b/src/main/java/cn/nukkit/event/player/PlayerCreationEvent.java
@@ -5,6 +5,8 @@
import cn.nukkit.event.HandlerList;
import cn.nukkit.network.SourceInterface;
+import java.net.InetSocketAddress;
+
/**
* author: MagicDroidX
* Nukkit Project
@@ -21,19 +23,16 @@ public static HandlerList getHandlers() {
private final Long clientId;
- private final String address;
-
- private final int port;
+ private final InetSocketAddress socketAddress;
private Class extends Player> baseClass;
private Class extends Player> playerClass;
- public PlayerCreationEvent(SourceInterface interfaz, Class extends Player> baseClass, Class extends Player> playerClass, Long clientId, String address, int port) {
+ public PlayerCreationEvent(SourceInterface interfaz, Class extends Player> baseClass, Class extends Player> playerClass, Long clientId, InetSocketAddress socketAddress) {
this.interfaz = interfaz;
this.clientId = clientId;
- this.address = address;
- this.port = port;
+ this.socketAddress = socketAddress;
this.baseClass = baseClass;
this.playerClass = playerClass;
@@ -44,11 +43,15 @@ public SourceInterface getInterface() {
}
public String getAddress() {
- return address;
+ return this.socketAddress.getAddress().toString();
}
public int getPort() {
- return port;
+ return this.socketAddress.getPort();
+ }
+
+ public InetSocketAddress getSocketAddress() {
+ return socketAddress;
}
public Long getClientId() {
diff --git a/src/main/java/cn/nukkit/level/biome/impl/beach/ColdBeachBiome.java b/src/main/java/cn/nukkit/level/biome/impl/beach/ColdBeachBiome.java
index 8ec3e1c8c55..98c85216fda 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/beach/ColdBeachBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/beach/ColdBeachBiome.java
@@ -13,8 +13,8 @@ public ColdBeachBiome() {
}
@Override
- public int getCoverBlock() {
- return SNOW_LAYER;
+ public int getCoverId(int x, int z) {
+ return SNOW_LAYER << 4;
}
@Override
diff --git a/src/main/java/cn/nukkit/level/biome/impl/extremehills/ExtremeHillsMBiome.java b/src/main/java/cn/nukkit/level/biome/impl/extremehills/ExtremeHillsMBiome.java
index c14909d49a5..df49bc00fc1 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/extremehills/ExtremeHillsMBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/extremehills/ExtremeHillsMBiome.java
@@ -32,24 +32,18 @@ public String getName() {
}
@Override
- public int getSurfaceBlock(int y) {
- return isGravel ? GRAVEL : super.getSurfaceBlock(y);
+ public int getSurfaceId(int x, int y, int z) {
+ return gravelNoise.noise2D(x, z, true) < -0.75f ? GRAVEL << 4 : super.getSurfaceId(x, y, z);
}
@Override
- public int getSurfaceDepth(int y) {
- return isGravel ? 4 : super.getSurfaceDepth(y);
+ public int getSurfaceDepth(int x, int y, int z) {
+ return gravelNoise.noise2D(x, z, true) < -0.75f ? 4 : super.getSurfaceDepth(x, y, z);
}
@Override
- public int getGroundDepth(int y) {
- return isGravel ? 0 : super.getGroundDepth(y);
- }
-
- @Override
- public void preCover(int x, int z) {
- //-0.75 is farily rare, so there'll be much more gravel than grass
- isGravel = gravelNoise.noise2D(x, z, true) < -0.75f;
+ public int getGroundDepth(int x, int y, int z) {
+ return gravelNoise.noise2D(x, z, true) < -0.75f ? 0 : super.getGroundDepth(x, y, z);
}
@Override
diff --git a/src/main/java/cn/nukkit/level/biome/impl/extremehills/StoneBeachBiome.java b/src/main/java/cn/nukkit/level/biome/impl/extremehills/StoneBeachBiome.java
index 26830acaef5..f5b192bf217 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/extremehills/StoneBeachBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/extremehills/StoneBeachBiome.java
@@ -16,22 +16,22 @@ public StoneBeachBiome() {
}
@Override
- public int getSurfaceDepth(int y) {
+ public int getSurfaceDepth(int x, int y, int z) {
return 0;
}
@Override
- public int getSurfaceBlock(int y) {
+ public int getSurfaceId(int x, int y, int z) {
return 0;
}
@Override
- public int getGroundDepth(int y) {
+ public int getGroundDepth(int x, int y, int z) {
return 0;
}
@Override
- public int getGroundBlock(int y) {
+ public int getGroundId(int x, int y, int z) {
return 0;
}
diff --git a/src/main/java/cn/nukkit/level/biome/impl/iceplains/IcePlainsSpikesBiome.java b/src/main/java/cn/nukkit/level/biome/impl/iceplains/IcePlainsSpikesBiome.java
index 7e9aeea8ff5..9675180e674 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/iceplains/IcePlainsSpikesBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/iceplains/IcePlainsSpikesBiome.java
@@ -19,8 +19,8 @@ public IcePlainsSpikesBiome() {
}
@Override
- public int getSurfaceBlock(int y) {
- return SNOW_BLOCK;
+ public int getSurfaceId(int x, int y, int z) {
+ return SNOW_BLOCK << 4;
}
public String getName() {
diff --git a/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaBiome.java b/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaBiome.java
index 3bf8fbd84e4..67a3867a159 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaBiome.java
@@ -16,11 +16,9 @@
* Handles the placement of stained clay for all mesa variants
*/
public class MesaBiome extends CoveredBiome {
- static final int[] colorLayer = new int[64];
+ static final int[] colorLayer = new int[64];
static final SimplexF redSandNoise = new SimplexF(new NukkitRandom(937478913), 2f, 1 / 4f, 1 / 4f);
- static final SimplexF colorNoise = new SimplexF(new NukkitRandom(193759875), 2f, 1 / 4f, 1 / 32f);
- private SimplexF moundNoise = new SimplexF(new NukkitRandom(347228794), 2f, 1 / 4f, getMoundFrequency());
- protected int moundHeight;
+ static final SimplexF colorNoise = new SimplexF(new NukkitRandom(193759875), 2f, 1 / 4f, 1 / 32f);
static {
Random random = new Random(29864);
@@ -32,7 +30,7 @@ public class MesaBiome extends CoveredBiome {
setRandomLayerColor(random, 10, 14); // red
for (int i = 0, j = 0; i < random.nextInt(3) + 3; i++) {
j += random.nextInt(6) + 4;
- if (j >= colorLayer.length -3) {
+ if (j >= colorLayer.length - 3) {
break;
}
if (random.nextInt(2) == 0 || j < colorLayer.length - 1 && random.nextInt(2) == 0) {
@@ -54,12 +52,8 @@ private static void setRandomLayerColor(Random random, int sliceCount, int color
}
}
- int randY = 0;
- int redSandThreshold = 0;
- boolean isRedSand = false;
- //cache this too so we can access it in getSurfaceBlock and getSurfaceMeta without needing to calculate it twice
- int currMeta = 0;
- int startY = 0;
+ private SimplexF moundNoise = new SimplexF(new NukkitRandom(347228794), 2f, 1 / 4f, getMoundFrequency());
+ protected int moundHeight;
public MesaBiome() {
PopulatorCactus cactus = new PopulatorCactus();
@@ -75,45 +69,33 @@ public MesaBiome() {
this.setMoundHeight(17);
}
- public void setMoundHeight(int height) {
+ public void setMoundHeight(int height) {
this.moundHeight = height;
}
@Override
- public int getSurfaceDepth(int y) {
- isRedSand = y < redSandThreshold;
- startY = y;
- //if true, we'll be generating red sand
- return isRedSand ? 3 : y - 66;
- }
-
- @Override
- public int getSurfaceBlock(int y) {
- if (isRedSand) {
- return SAND;
- } else {
- currMeta = colorLayer[(y + randY) & 0x3F];
- return currMeta == -1 ? TERRACOTTA : STAINED_TERRACOTTA;
- }
+ public int getSurfaceDepth(int x, int y, int z) {
+ return y < (71 + Math.round((redSandNoise.noise2D(x, z, true) + 1) * 1.5f)) ? 3 : y - 66;
}
@Override
- public int getSurfaceMeta(int y) {
- if (isRedSand) {
- return BlockSand.RED;
+ public int getSurfaceId(int x, int y, int z) {
+ if (y < (71 + Math.round((redSandNoise.noise2D(x, z, true) + 1) * 1.5f))) {
+ return (SAND << 4) | BlockSand.RED;
} else {
- return Math.max(0, currMeta);
+ int meta = colorLayer[(y + Math.round((colorNoise.noise2D(x, z, true) + 1) * 1.5f)) & 0x3F];
+ return (meta == -1 ? TERRACOTTA << 4 : STAINED_TERRACOTTA << 4) | Math.max(0, meta);
}
}
@Override
- public int getGroundDepth(int y) {
- return isRedSand ? 2 : 0;
+ public int getGroundDepth(int x, int y, int z) {
+ return y < (71 + Math.round((redSandNoise.noise2D(x, z, true) + 1) * 1.5f)) ? 2 : 0;
}
@Override
- public int getGroundBlock(int y) {
- return RED_SANDSTONE;
+ public int getGroundId(int x, int y, int z) {
+ return RED_SANDSTONE << 4;
}
@Override
@@ -121,14 +103,7 @@ public String getName() {
return "Mesa";
}
- @Override
- public void preCover(int x, int z) {
- //random noise from 0-3
- randY = Math.round((colorNoise.noise2D(x, z, true) + 1) * 1.5f);
- redSandThreshold = 71 + Math.round((redSandNoise.noise2D(x, z, true) + 1) * 1.5f);
- }
-
- protected float getMoundFrequency() {
+ protected float getMoundFrequency() {
return 1 / 128f;
}
@@ -139,7 +114,7 @@ public int getHeightOffset(int x, int z) {
return (n > a && n < a + 0.2f) ? (int) ((n - a) * 5f * moundHeight) : n < a + 0.1f ? 0 : moundHeight;
}
- protected float minHill() {
+ protected float minHill() {
return -0.1f;
}
}
diff --git a/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaPlateauFBiome.java b/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaPlateauFBiome.java
index a6cb36cf931..3a5433c70af 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaPlateauFBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/mesa/MesaPlateauFBiome.java
@@ -17,8 +17,8 @@ public MesaPlateauFBiome() {
}
@Override
- public int getCoverBlock() {
- return GRASS;
+ public int getCoverId(int x, int z) {
+ return GRASS << 4;
}
@Override
diff --git a/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java b/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java
index 06b30f9e588..b9e3706034b 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/mushroom/MushroomIslandBiome.java
@@ -20,7 +20,7 @@ public String getName() {
}
@Override
- public int getSurfaceBlock(int y) {
- return Block.MYCELIUM;
+ public int getSurfaceId(int x, int y, int z) {
+ return MYCELIUM << 4;
}
}
diff --git a/src/main/java/cn/nukkit/level/biome/impl/ocean/OceanBiome.java b/src/main/java/cn/nukkit/level/biome/impl/ocean/OceanBiome.java
index 786e163e2dc..cda7e9df1ea 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/ocean/OceanBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/ocean/OceanBiome.java
@@ -18,8 +18,7 @@ public String getName() {
return "Ocean";
}
- @Override
- public int getGroundBlock(int y) {
- return GRAVEL;
+ public int getGroundId(int y) {
+ return GRAVEL << 4;
}
}
diff --git a/src/main/java/cn/nukkit/level/biome/impl/taiga/ColdTaigaBiome.java b/src/main/java/cn/nukkit/level/biome/impl/taiga/ColdTaigaBiome.java
index 9b8450cc2a6..54332c48d17 100644
--- a/src/main/java/cn/nukkit/level/biome/impl/taiga/ColdTaigaBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/impl/taiga/ColdTaigaBiome.java
@@ -23,8 +23,8 @@ public String getName() {
}
@Override
- public int getCoverBlock() {
- return SNOW_LAYER;
+ public int getCoverId(int x, int z) {
+ return SNOW_LAYER << 4;
}
@Override
diff --git a/src/main/java/cn/nukkit/level/biome/type/CoveredBiome.java b/src/main/java/cn/nukkit/level/biome/type/CoveredBiome.java
index 9a5ad67cd92..b934cd3b1d1 100644
--- a/src/main/java/cn/nukkit/level/biome/type/CoveredBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/type/CoveredBiome.java
@@ -1,6 +1,8 @@
package cn.nukkit.level.biome.type;
import cn.nukkit.level.biome.Biome;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.level.generator.Normal;
/**
* author: DaPorkchop_
@@ -10,97 +12,63 @@
*
*/
public abstract class CoveredBiome extends Biome {
- public final Object synchronizeCover = new Object();
-
- /**
- * A single block placed on top of the surface blocks
- *
- * @return cover block
- */
- public int getCoverBlock() {
- return AIR;
+ public int getCoverId(int x, int z) {
+ return AIR << 4;
}
- /**
- * The amount of times the surface block should be used
- *
- * If < 0 bad things will happen!
- *
- *
- * @param y y
- * @return surface depth
- */
- public int getSurfaceDepth(int y) {
+ public int getSurfaceDepth(int x, int y, int z) {
return 1;
}
- /**
- * Between cover and ground
- *
- * @param y y
- * @return surface block
- */
- public abstract int getSurfaceBlock(int y);
-
- /**
- * The metadata of the surface block
- *
- * @param y y
- * @return surface meta
- */
- public int getSurfaceMeta(int y) {
- return 0;
- }
+ public abstract int getSurfaceId(int x, int y, int z);
- /**
- * The amount of times the ground block should be used
- *
- * If < 0 bad things will happen!
- *
- * @param y y
- * @return ground depth
- */
- public int getGroundDepth(int y) {
+ public int getGroundDepth(int x, int y, int z) {
return 4;
}
- /**
- * Between surface and stone
- *
- * @param y y
- * @return ground block
- */
- public abstract int getGroundBlock(int y);
+ public abstract int getGroundId(int x, int y, int z);
- /**
- * The metadata of the ground block
- *
- * @param y y
- * @return ground meta
- */
- public int getGroundMeta(int y) {
- return 0;
- }
-
- /**
- * The block used as stone/below all other surface blocks
- *
- * @return stone block
- */
- public int getStoneBlock() {
- return STONE;
- }
+ public void doCover(int x, int z, FullChunk chunk) {
+ final int fullX = (chunk.getX() << 4) | x;
+ final int fullZ = (chunk.getZ() << 4) | z;
- /**
- * Called before a new block column is covered. Biomes can update any relevant variables here before covering.
- *
- * Biome covering is synchronized on the biome, so thread safety isn't an issue.
- *
- *
- * @param x x
- * @param z z
- */
- public void preCover(int x, int z) {
+ final int coverBlock = this.getCoverId(fullX, fullZ);
+ boolean hasCovered = false;
+ int realY;
+ //start one below build limit in case of cover blocks
+ for (int y = 254; y > 32; y--) {
+ if (chunk.getFullBlock(x, y, z) == STONE << 4) {
+ COVER:
+ if (!hasCovered) {
+ if (y >= Normal.seaHeight) {
+ chunk.setFullBlockId(x, y + 1, z, coverBlock);
+ int surfaceDepth = this.getSurfaceDepth(fullX, y, fullZ);
+ for (int i = 0; i < surfaceDepth; i++) {
+ realY = y - i;
+ if (chunk.getFullBlock(x, realY, z) == STONE << 4) {
+ chunk.setFullBlockId(x, realY, z, this.getSurfaceId(fullX, realY, fullZ));
+ } else break COVER;
+ }
+ y -= surfaceDepth;
+ }
+ int groundDepth = this.getGroundDepth(fullX, y, fullZ);
+ for (int i = 0; i < groundDepth; i++) {
+ realY = y - i;
+ if (chunk.getFullBlock(x, realY, z) == STONE << 4) {
+ chunk.setFullBlockId(x, realY, z, this.getGroundId(fullX, realY, fullZ));
+ } else break COVER;
+ }
+ //don't take all of groundDepth away because we do y-- in the loop
+ y -= groundDepth - 1;
+ }
+ hasCovered = true;
+ } else {
+ if (hasCovered) {
+ //reset it if this isn't a valid stone block (allows us to place ground cover on top and below overhangs)
+ hasCovered = false;
+ }
+ }
+ }
}
}
diff --git a/src/main/java/cn/nukkit/level/biome/type/GrassyBiome.java b/src/main/java/cn/nukkit/level/biome/type/GrassyBiome.java
index 3d7afbd9369..91048cab4b7 100644
--- a/src/main/java/cn/nukkit/level/biome/type/GrassyBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/type/GrassyBiome.java
@@ -20,12 +20,12 @@ public GrassyBiome() {
}
@Override
- public int getSurfaceBlock(int y) {
- return GRASS;
+ public int getSurfaceId(int x, int y, int z) {
+ return GRASS << 4;
}
@Override
- public int getGroundBlock(int y) {
- return DIRT;
+ public int getGroundId(int x, int y, int z) {
+ return DIRT << 4;
}
}
diff --git a/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java b/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java
index f094309143c..e7b854e0ae8 100644
--- a/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/type/SandyBiome.java
@@ -7,24 +7,23 @@
* Nukkit Project
*/
public abstract class SandyBiome extends CoveredBiome {
-
@Override
- public int getSurfaceDepth(int y) {
+ public int getSurfaceDepth(int x, int y, int z) {
return 3;
}
@Override
- public int getSurfaceBlock(int y) {
- return Block.SAND;
+ public int getSurfaceId(int x, int y, int z) {
+ return SAND << 4;
}
@Override
- public int getGroundDepth(int y) {
+ public int getGroundDepth(int x, int y, int z) {
return 2;
}
@Override
- public int getGroundBlock(int y) {
- return Block.SANDSTONE;
+ public int getGroundId(int x, int y, int z) {
+ return SANDSTONE << 4;
}
}
diff --git a/src/main/java/cn/nukkit/level/biome/type/SnowyBiome.java b/src/main/java/cn/nukkit/level/biome/type/SnowyBiome.java
index 81445bbf943..686d941e415 100644
--- a/src/main/java/cn/nukkit/level/biome/type/SnowyBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/type/SnowyBiome.java
@@ -15,7 +15,7 @@ public SnowyBiome() {
}
@Override
- public int getCoverBlock() {
- return SNOW_LAYER;
+ public int getCoverId(int x, int z) {
+ return SNOW_LAYER << 4;
}
}
diff --git a/src/main/java/cn/nukkit/level/biome/type/WateryBiome.java b/src/main/java/cn/nukkit/level/biome/type/WateryBiome.java
index 2caa9e8c834..17ba88e2aa4 100644
--- a/src/main/java/cn/nukkit/level/biome/type/WateryBiome.java
+++ b/src/main/java/cn/nukkit/level/biome/type/WateryBiome.java
@@ -6,23 +6,23 @@
*/
public abstract class WateryBiome extends CoveredBiome {
@Override
- public int getSurfaceDepth(int y) {
+ public int getSurfaceDepth(int x, int y, int z) {
return 0;
}
@Override
- public int getSurfaceBlock(int y) {
+ public int getSurfaceId(int x, int y, int z) {
//doesn't matter, surface depth is 0
return 0;
}
@Override
- public int getGroundDepth(int y) {
+ public int getGroundDepth(int x, int y, int z) {
return 5;
}
@Override
- public int getGroundBlock(int y) {
- return DIRT;
+ public int getGroundId(int x, int y, int z) {
+ return DIRT << 4;
}
}
diff --git a/src/main/java/cn/nukkit/level/generator/Flat.java b/src/main/java/cn/nukkit/level/generator/Flat.java
index 005b1129513..2739c083a9b 100644
--- a/src/main/java/cn/nukkit/level/generator/Flat.java
+++ b/src/main/java/cn/nukkit/level/generator/Flat.java
@@ -6,8 +6,8 @@
import cn.nukkit.level.format.FullChunk;
import cn.nukkit.level.format.generic.BaseFullChunk;
import cn.nukkit.level.generator.object.ore.OreType;
-import cn.nukkit.level.generator.populator.type.Populator;
import cn.nukkit.level.generator.populator.impl.PopulatorOre;
+import cn.nukkit.level.generator.populator.type.Populator;
import cn.nukkit.math.NukkitRandom;
import cn.nukkit.math.Vector3;
@@ -70,8 +70,7 @@ public Flat(Map options) {
this.options = options;
if (this.options.containsKey("decoration")) {
- PopulatorOre ores = new PopulatorOre();
- ores.setOreTypes(new OreType[]{
+ PopulatorOre ores = new PopulatorOre(BlockID.STONE, new OreType[]{
new OreType(new BlockOreCoal(), 20, 16, 0, 128),
new OreType(new BlockOreIron(), 20, 8, 0, 64),
new OreType(new BlockOreRedstone(), 8, 7, 0, 16),
diff --git a/src/main/java/cn/nukkit/level/generator/Nether.java b/src/main/java/cn/nukkit/level/generator/Nether.java
index 205af838f65..ad1a801def4 100644
--- a/src/main/java/cn/nukkit/level/generator/Nether.java
+++ b/src/main/java/cn/nukkit/level/generator/Nether.java
@@ -15,6 +15,7 @@
import cn.nukkit.level.generator.populator.type.Populator;
import cn.nukkit.math.NukkitRandom;
import cn.nukkit.math.Vector3;
+
import java.util.*;
public class Nether extends Generator {
@@ -81,8 +82,7 @@ public void init(ChunkManager level, NukkitRandom random) {
this.localSeed1 = this.random.nextLong();
this.localSeed2 = this.random.nextLong();
- PopulatorOre ores = new PopulatorOre(Block.NETHERRACK);
- ores.setOreTypes(new OreType[]{
+ PopulatorOre ores = new PopulatorOre(Block.NETHERRACK, new OreType[]{
new OreType(new BlockOreQuartz(), 20, 16, 0, 128),
new OreType(new BlockSoulSand(), 5, 64, 0, 128),
new OreType(new BlockGravel(), 5, 64, 0, 128),
@@ -100,8 +100,7 @@ public void init(ChunkManager level, NukkitRandom random) {
lava.setRandomAmount(2);
this.populators.add(lava);
this.populators.add(new PopulatorGlowStone());
- PopulatorOre ore = new PopulatorOre(Block.NETHERRACK);
- ore.setOreTypes(new OreType[]{
+ PopulatorOre ore = new PopulatorOre(Block.NETHERRACK, new OreType[]{
new OreType(new BlockOreQuartz(), 40, 16, 0, 128, NETHERRACK),
new OreType(new BlockSoulSand(), 1, 64, 30, 35, NETHERRACK),
new OreType(new BlockLava(), 32, 1, 0, 32, NETHERRACK),
diff --git a/src/main/java/cn/nukkit/level/generator/Normal.java b/src/main/java/cn/nukkit/level/generator/Normal.java
index f509e8c8155..defa3f0dbfa 100644
--- a/src/main/java/cn/nukkit/level/generator/Normal.java
+++ b/src/main/java/cn/nukkit/level/generator/Normal.java
@@ -9,14 +9,12 @@
import cn.nukkit.level.generator.noise.vanilla.f.NoiseGeneratorOctavesF;
import cn.nukkit.level.generator.noise.vanilla.f.NoiseGeneratorPerlinF;
import cn.nukkit.level.generator.object.ore.OreType;
-import cn.nukkit.level.generator.populator.impl.PopulatorBedrock;
-import cn.nukkit.level.generator.populator.impl.PopulatorCaves;
-import cn.nukkit.level.generator.populator.impl.PopulatorGroundCover;
-import cn.nukkit.level.generator.populator.impl.PopulatorOre;
+import cn.nukkit.level.generator.populator.impl.*;
import cn.nukkit.level.generator.populator.type.Populator;
import cn.nukkit.math.MathHelper;
import cn.nukkit.math.NukkitRandom;
import cn.nukkit.math.Vector3;
+import com.google.common.collect.ImmutableList;
import java.util.*;
@@ -113,8 +111,8 @@ public class Normal extends Generator {
}
}
- private final List populators = new ArrayList<>();
- private final List generationPopulators = new ArrayList<>();
+ private List populators = Collections.emptyList();
+ private List generationPopulators = Collections.emptyList();
public static final int seaHeight = 64;
public NoiseGeneratorOctavesF scaleNoise;
public NoiseGeneratorOctavesF depthNoise;
@@ -136,7 +134,7 @@ public class Normal extends Generator {
private NoiseGeneratorPerlinF surfaceNoise;
public Normal() {
- this(new HashMap<>());
+ this(Collections.emptyMap());
}
public Normal(Map options) {
@@ -150,7 +148,7 @@ public int getId() {
@Override
public ChunkManager getChunkManager() {
- return level;
+ return this.level;
}
@Override
@@ -160,7 +158,7 @@ public String getName() {
@Override
public Map getSettings() {
- return new HashMap<>();
+ return Collections.emptyMap();
}
public Biome pickBiome(int x, int z) {
@@ -186,43 +184,37 @@ public void init(ChunkManager level, NukkitRandom random) {
this.depthNoise = new NoiseGeneratorOctavesF(random, 16);
//this should run before all other populators so that we don't do things like generate ground cover on bedrock or something
- PopulatorGroundCover cover = new PopulatorGroundCover();
- this.generationPopulators.add(cover);
-
- PopulatorBedrock bedrock = new PopulatorBedrock();
- this.generationPopulators.add(bedrock);
-
- PopulatorOre ores = new PopulatorOre();
- ores.setOreTypes(new OreType[]{
- new OreType(new BlockOreCoal(), 20, 17, 0, 128),
- new OreType(new BlockOreIron(), 20, 9, 0, 64),
- new OreType(new BlockOreRedstone(), 8, 8, 0, 16),
- new OreType(new BlockOreLapis(), 1, 7, 0, 16),
- new OreType(new BlockOreGold(), 2, 9, 0, 32),
- new OreType(new BlockOreDiamond(), 1, 8, 0, 16),
- new OreType(new BlockDirt(), 10, 33, 0, 128),
- new OreType(new BlockGravel(), 8, 33, 0, 128),
- new OreType(new BlockStone(BlockStone.GRANITE), 10, 33, 0, 80),
- new OreType(new BlockStone(BlockStone.DIORITE), 10, 33, 0, 80),
- new OreType(new BlockStone(BlockStone.ANDESITE), 10, 33, 0, 80)
- });
- this.populators.add(ores);
-
- PopulatorCaves caves = new PopulatorCaves();
- this.populators.add(caves);
-
- //TODO: fix ravines
- //PopulatorRavines ravines = new PopulatorRavines();
- //this.populators.add(ravines);
+ this.generationPopulators = ImmutableList.of(
+ new PopulatorBedrock(),
+ new PopulatorGroundCover()
+ );
+
+ this.populators = ImmutableList.of(
+ new PopulatorOre(STONE, new OreType[]{
+ new OreType(new BlockOreCoal(), 20, 17, 0, 128),
+ new OreType(new BlockOreIron(), 20, 9, 0, 64),
+ new OreType(new BlockOreRedstone(), 8, 8, 0, 16),
+ new OreType(new BlockOreLapis(), 1, 7, 0, 16),
+ new OreType(new BlockOreGold(), 2, 9, 0, 32),
+ new OreType(new BlockOreDiamond(), 1, 8, 0, 16),
+ new OreType(new BlockDirt(), 10, 33, 0, 128),
+ new OreType(new BlockGravel(), 8, 33, 0, 128),
+ new OreType(new BlockStone(BlockStone.GRANITE), 10, 33, 0, 80),
+ new OreType(new BlockStone(BlockStone.DIORITE), 10, 33, 0, 80),
+ new OreType(new BlockStone(BlockStone.ANDESITE), 10, 33, 0, 80)
+ }),
+ new PopulatorCaves()//,
+ //new PopulatorRavines()
+ );
}
@Override
public void generateChunk(final int chunkX, final int chunkZ) {
int baseX = chunkX << 4;
int baseZ = chunkZ << 4;
- this.nukkitRandom.setSeed(chunkX * localSeed1 ^ chunkZ * localSeed2 ^ this.level.getSeed());
+ this.nukkitRandom.setSeed(chunkX * this.localSeed1 ^ chunkZ * this.localSeed2 ^ this.level.getSeed());
- BaseFullChunk chunk = level.getChunk(chunkX, chunkZ);
+ BaseFullChunk chunk = this.level.getChunk(chunkX, chunkZ);
//generate base noise values
float[] depthRegion = this.depthNoise.generateNoiseOctaves(this.depthRegion.get(), chunkX * 4, chunkZ * 4, 5, 5, 200f, 200f, 0.5f);
@@ -243,11 +235,11 @@ public void generateChunk(final int chunkX, final int chunkZ) {
float heightVariationSum = 0.0F;
float baseHeightSum = 0.0F;
float biomeWeightSum = 0.0F;
- Biome biome = pickBiome(baseX + (xSeg * 4), baseZ + (zSeg * 4));
+ Biome biome = this.pickBiome(baseX + (xSeg * 4), baseZ + (zSeg * 4));
for (int xSmooth = -2; xSmooth <= 2; ++xSmooth) {
for (int zSmooth = -2; zSmooth <= 2; ++zSmooth) {
- Biome biome1 = pickBiome(baseX + (xSeg * 4) + xSmooth, baseZ + (zSeg * 4) + zSmooth);
+ Biome biome1 = this.pickBiome(baseX + (xSeg * 4) + xSmooth, baseZ + (zSeg * 4) + zSmooth);
float baseHeight = biome1.getBaseHeight();
float heightVariation = biome1.getHeightVariation();
@@ -376,7 +368,7 @@ public void generateChunk(final int chunkX, final int chunkZ) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
- Biome biome = selector.pickBiome(baseX | x, baseZ | z);
+ Biome biome = this.selector.pickBiome(baseX | x, baseZ | z);
chunk.setBiome(x, z, biome);
}
@@ -390,12 +382,13 @@ public void generateChunk(final int chunkX, final int chunkZ) {
@Override
public void populateChunk(int chunkX, int chunkZ) {
- BaseFullChunk chunk = level.getChunk(chunkX, chunkZ);
+ BaseFullChunk chunk = this.level.getChunk(chunkX, chunkZ);
this.nukkitRandom.setSeed(0xdeadbeef ^ (chunkX << 8) ^ chunkZ ^ this.level.getSeed());
for (Populator populator : this.populators) {
populator.populate(this.level, chunkX, chunkZ, this.nukkitRandom, chunk);
}
+ @SuppressWarnings("deprecation")
Biome biome = EnumBiome.getBiome(chunk.getBiomeId(7, 7));
biome.populateChunk(this.level, chunkX, chunkZ, this.nukkitRandom);
}
@@ -404,4 +397,4 @@ public void populateChunk(int chunkX, int chunkZ) {
public Vector3 getSpawn() {
return new Vector3(0.5, 256, 0.5);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorCaves.java b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorCaves.java
index bfadd32b2a8..59eaed143aa 100644
--- a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorCaves.java
+++ b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorCaves.java
@@ -206,7 +206,7 @@ protected void generateCaveNode(long seed, FullChunk chunk, double x, double y,
// If grass was just deleted, try to
// move it down
if (grassFound && (chunk.getBlockId(xx, yy - 1, zz) == Block.DIRT)) {
- chunk.setBlock(xx, yy - 1, zz, ((CoveredBiome) biome).getSurfaceBlock(yy - 1));
+ chunk.setFullBlockId(xx, yy - 1, zz, ((CoveredBiome) biome).getSurfaceId(xx, yy - 1, zz));
}
}
}
diff --git a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java
index 09806df1066..b34290591ca 100644
--- a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java
+++ b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorGroundCover.java
@@ -11,63 +11,17 @@
import cn.nukkit.math.NukkitRandom;
/**
- * author: DaPorkchop_
- * Nukkit Project
+ * @author DaPorkchop_
*/
public class PopulatorGroundCover extends Populator {
- public static final int STONE = BlockID.STONE << 4;
-
@Override
public void populate(ChunkManager level, int chunkX, int chunkZ, NukkitRandom random, FullChunk chunk) {
- int realX = chunkX << 4;
- int realZ = chunkZ << 4;
- for (int x = 0; x < 16; ++x) {
- for (int z = 0; z < 16; ++z) {
+ //reverse iteration to 0 is faster
+ for (int x = 15; x >= 0; x--) {
+ for (int z = 15; z >= 0; z--) {
Biome realBiome = EnumBiome.getBiome(chunk.getBiomeId(x, z));
if (realBiome instanceof CoveredBiome) {
- final CoveredBiome biome = (CoveredBiome) realBiome;
- //just in case!
- synchronized (biome.synchronizeCover) {
- biome.preCover(realX | x, realZ | z);
- int coverBlock = biome.getCoverBlock() << 4;
-
- boolean hasCovered = false;
- int realY;
- //start one below build limit in case of cover blocks
- for (int y = 254; y > 32; y--) {
- if (chunk.getFullBlock(x, y, z) == STONE) {
- COVER:
- if (!hasCovered) {
- if (y >= Normal.seaHeight) {
- chunk.setFullBlockId(x, y + 1, z, coverBlock);
- int surfaceDepth = biome.getSurfaceDepth(y);
- for (int i = 0; i < surfaceDepth; i++) {
- realY = y - i;
- if (chunk.getFullBlock(x, realY, z) == STONE) {
- chunk.setFullBlockId(x, realY, z, (biome.getSurfaceBlock(realY) << 4) | biome.getSurfaceMeta(realY));
- } else break COVER;
- }
- y -= surfaceDepth;
- }
- int groundDepth = biome.getGroundDepth(y);
- for (int i = 0; i < groundDepth; i++) {
- realY = y - i;
- if (chunk.getFullBlock(x, realY, z) == STONE) {
- chunk.setFullBlockId(x, realY, z, (biome.getGroundBlock(realY) << 4) | biome.getGroundMeta(realY));
- } else break COVER;
- }
- //don't take all of groundDepth away because we do y-- in the loop
- y -= groundDepth - 1;
- }
- hasCovered = true;
- } else {
- if (hasCovered) {
- //reset it if this isn't a valid stone block (allows us to place ground cover on top and below overhangs)
- hasCovered = false;
- }
- }
- }
- }
+ ((CoveredBiome) realBiome).doCover(x, z, chunk);
}
}
}
diff --git a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java
index ee762f6def8..472d587354e 100644
--- a/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java
+++ b/src/main/java/cn/nukkit/level/generator/populator/impl/PopulatorOre.java
@@ -9,19 +9,15 @@
import cn.nukkit.math.NukkitRandom;
/**
- * author: MagicDroidX
- * Nukkit Project
+ * @author DaPorkchop_
*/
public class PopulatorOre extends Populator {
private final int replaceId;
- private OreType[] oreTypes = new OreType[0];
+ private final OreType[] oreTypes;
- public PopulatorOre() {
- this(Block.STONE);
- }
-
- public PopulatorOre(int id) {
- this.replaceId = id;
+ public PopulatorOre(int replaceId, OreType[] oreTypes) {
+ this.replaceId = replaceId;
+ this.oreTypes = oreTypes;
}
@Override
@@ -42,8 +38,4 @@ public void populate(ChunkManager level, int chunkX, int chunkZ, NukkitRandom ra
}
}
}
-
- public void setOreTypes(OreType[] oreTypes) {
- this.oreTypes = oreTypes;
- }
}
diff --git a/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java b/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java
index 6902856b74c..6a982696c63 100644
--- a/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java
+++ b/src/main/java/cn/nukkit/network/AdvancedSourceInterface.java
@@ -1,18 +1,23 @@
package cn.nukkit.network;
+import io.netty.buffer.ByteBuf;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
/**
* author: MagicDroidX
* Nukkit Project
*/
public interface AdvancedSourceInterface extends SourceInterface {
- void blockAddress(String address);
+ void blockAddress(InetAddress address);
- void blockAddress(String address, int timeout);
+ void blockAddress(InetAddress address, int timeout);
- void unblockAddress(String address);
+ void unblockAddress(InetAddress address);
void setNetwork(Network network);
- void sendRawPacket(String address, int port, byte[] payload);
+ void sendRawPacket(InetSocketAddress socketAddress, ByteBuf payload);
}
diff --git a/src/main/java/cn/nukkit/network/CacheEncapsulatedPacket.java b/src/main/java/cn/nukkit/network/CacheEncapsulatedPacket.java
deleted file mode 100644
index 4272d0d41e8..00000000000
--- a/src/main/java/cn/nukkit/network/CacheEncapsulatedPacket.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package cn.nukkit.network;
-
-import cn.nukkit.raknet.protocol.EncapsulatedPacket;
-
-/**
- * author: MagicDroidX
- * Nukkit Project
- */
-public class CacheEncapsulatedPacket extends EncapsulatedPacket {
-
- public byte[] internalData = null;
-
- @Override
- public byte[] toBinary() {
- return this.toBinary(false);
- }
-
- @Override
- public byte[] toBinary(boolean internal) {
- if (this.internalData == null) {
- this.internalData = super.toBinary(internal);
- }
- return this.internalData;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/cn/nukkit/network/CompressBatchedPacket.java b/src/main/java/cn/nukkit/network/CompressBatchedPacket.java
index 6cc6ab3f738..b45811951e2 100644
--- a/src/main/java/cn/nukkit/network/CompressBatchedPacket.java
+++ b/src/main/java/cn/nukkit/network/CompressBatchedPacket.java
@@ -4,7 +4,7 @@
import cn.nukkit.scheduler.AsyncTask;
import cn.nukkit.utils.Zlib;
-import java.util.ArrayList;
+import java.net.InetSocketAddress;
import java.util.List;
/**
@@ -17,17 +17,17 @@ public class CompressBatchedPacket extends AsyncTask {
public byte[] data;
public byte[] finalData;
public int channel = 0;
- public List targets = new ArrayList<>();
+ public List targets;
- public CompressBatchedPacket(byte[] data, List targets) {
+ public CompressBatchedPacket(byte[] data, List targets) {
this(data, targets, 7);
}
- public CompressBatchedPacket(byte[] data, List targets, int level) {
+ public CompressBatchedPacket(byte[] data, List targets, int level) {
this(data, targets, level, 0);
}
- public CompressBatchedPacket(byte[] data, List targets, int level, int channel) {
+ public CompressBatchedPacket(byte[] data, List targets, int level, int channel) {
this.data = data;
this.targets = targets;
this.level = level;
diff --git a/src/main/java/cn/nukkit/network/CompressBatchedTask.java b/src/main/java/cn/nukkit/network/CompressBatchedTask.java
index bafffba677d..5f0880f4248 100644
--- a/src/main/java/cn/nukkit/network/CompressBatchedTask.java
+++ b/src/main/java/cn/nukkit/network/CompressBatchedTask.java
@@ -4,7 +4,7 @@
import cn.nukkit.scheduler.AsyncTask;
import cn.nukkit.utils.Zlib;
-import java.util.ArrayList;
+import java.net.InetSocketAddress;
import java.util.List;
/**
@@ -17,17 +17,17 @@ public class CompressBatchedTask extends AsyncTask {
public byte[][] data;
public byte[] finalData;
public int channel = 0;
- public List targets = new ArrayList<>();
+ public List targets;
- public CompressBatchedTask(byte[][] data, List targets) {
+ public CompressBatchedTask(byte[][] data, List targets) {
this(data, targets, 7);
}
- public CompressBatchedTask(byte[][] data, List targets, int level) {
+ public CompressBatchedTask(byte[][] data, List targets, int level) {
this(data, targets, level, 0);
}
- public CompressBatchedTask(byte[][] data, List targets, int level, int channel) {
+ public CompressBatchedTask(byte[][] data, List targets, int level, int channel) {
this.data = data;
this.targets = targets;
this.level = level;
diff --git a/src/main/java/cn/nukkit/network/Network.java b/src/main/java/cn/nukkit/network/Network.java
index d939f284f2f..7386c0d9681 100644
--- a/src/main/java/cn/nukkit/network/Network.java
+++ b/src/main/java/cn/nukkit/network/Network.java
@@ -4,15 +4,19 @@
import cn.nukkit.Player;
import cn.nukkit.Server;
import cn.nukkit.network.protocol.*;
+import cn.nukkit.utils.Binary;
import cn.nukkit.utils.BinaryStream;
import cn.nukkit.utils.MainLogger;
import cn.nukkit.utils.Utils;
import cn.nukkit.utils.VarInt;
import cn.nukkit.utils.Zlib;
+import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import lombok.extern.log4j.Log4j2;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
@@ -103,9 +107,11 @@ public void registerInterface(SourceInterface interfaz) {
interfaz.setName(this.name + "!@#" + this.subName);
}
- public void unregisterInterface(SourceInterface interfaz) {
- this.interfaces.remove(interfaz);
- this.advancedInterfaces.remove(interfaz);
+ public void unregisterInterface(SourceInterface sourceInterface) {
+ this.interfaces.remove(sourceInterface);
+ if (sourceInterface instanceof AdvancedSourceInterface) {
+ this.advancedInterfaces.remove(sourceInterface);
+ }
}
public void setName(String name) {
@@ -217,25 +223,27 @@ public DataPacket getPacket(byte id) {
return null;
}
- public void sendPacket(String address, int port, byte[] payload) {
- for (AdvancedSourceInterface interfaz : this.advancedInterfaces) {
- interfaz.sendRawPacket(address, port, payload);
+ public void sendPacket(InetSocketAddress socketAddress, ByteBuf payload) {
+ for (AdvancedSourceInterface sourceInterface : this.advancedInterfaces) {
+ sourceInterface.sendRawPacket(socketAddress, payload);
}
}
- public void blockAddress(String address) {
- this.blockAddress(address, 300);
+ public void blockAddress(InetAddress address) {
+ for (AdvancedSourceInterface sourceInterface : this.advancedInterfaces) {
+ sourceInterface.blockAddress(address);
+ }
}
- public void blockAddress(String address, int timeout) {
- for (AdvancedSourceInterface interfaz : this.advancedInterfaces) {
- interfaz.blockAddress(address, timeout);
+ public void blockAddress(InetAddress address, int timeout) {
+ for (AdvancedSourceInterface sourceInterface : this.advancedInterfaces) {
+ sourceInterface.blockAddress(address, timeout);
}
}
- public void unblockAddress(String address) {
- for (AdvancedSourceInterface interfaz : this.advancedInterfaces) {
- interfaz.unblockAddress(address);
+ public void unblockAddress(InetAddress address) {
+ for (AdvancedSourceInterface sourceInterface : this.advancedInterfaces) {
+ sourceInterface.unblockAddress(address);
}
}
diff --git a/src/main/java/cn/nukkit/network/RakNetInterface.java b/src/main/java/cn/nukkit/network/RakNetInterface.java
index 1f0ebbe1b5a..1bda4d466f8 100644
--- a/src/main/java/cn/nukkit/network/RakNetInterface.java
+++ b/src/main/java/cn/nukkit/network/RakNetInterface.java
@@ -1,6 +1,5 @@
package cn.nukkit.network;
-import cn.nukkit.Nukkit;
import cn.nukkit.Player;
import cn.nukkit.Server;
import cn.nukkit.event.player.PlayerCreationEvent;
@@ -8,27 +7,33 @@
import cn.nukkit.network.protocol.BatchPacket;
import cn.nukkit.network.protocol.DataPacket;
import cn.nukkit.network.protocol.ProtocolInfo;
-import cn.nukkit.raknet.RakNet;
-import cn.nukkit.raknet.protocol.EncapsulatedPacket;
-import cn.nukkit.raknet.protocol.packet.PING_DataPacket;
-import cn.nukkit.raknet.server.RakNetServer;
-import cn.nukkit.raknet.server.ServerHandler;
-import cn.nukkit.raknet.server.ServerInstance;
-import cn.nukkit.utils.Binary;
-import cn.nukkit.utils.MainLogger;
import cn.nukkit.utils.Utils;
-import cn.nukkit.utils.Zlib;
+import com.google.common.base.Strings;
+import com.nukkitx.network.raknet.*;
+import com.nukkitx.network.util.DisconnectReason;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.socket.DatagramPacket;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
-import java.util.Map;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
/**
* author: MagicDroidX
* Nukkit Project
*/
-public class RakNetInterface implements ServerInstance, AdvancedSourceInterface {
+@Log4j2
+public class RakNetInterface implements RakNetServerListener, AdvancedSourceInterface {
private final Server server;
@@ -36,23 +41,18 @@ public class RakNetInterface implements ServerInstance, AdvancedSourceInterface
private final RakNetServer raknet;
- private final Map players = new ConcurrentHashMap<>();
+ private Set sessionListeners = Collections.newSetFromMap(new ConcurrentHashMap<>());
- private final Map networkLatency = new ConcurrentHashMap<>();
-
- private final Map identifiers = new ConcurrentHashMap<>();
-
- private final Map identifiersACK = new ConcurrentHashMap<>();
-
- private final ServerHandler handler;
-
- private int[] channelCounts = new int[256];
+ private byte[] advertisement;
public RakNetInterface(Server server) {
this.server = server;
- this.raknet = new RakNetServer(this.server.getLogger(), this.server.getPort(), this.server.getIp().equals("") ? "0.0.0.0" : this.server.getIp());
- this.handler = new ServerHandler(this.raknet, this);
+ InetSocketAddress bindAddress = new InetSocketAddress(Strings.isNullOrEmpty(this.server.getIp()) ? "0.0.0.0" : this.server.getIp(), this.server.getPort());
+
+ this.raknet = new RakNetServer(bindAddress, Runtime.getRuntime().availableProcessors());
+ this.raknet.bind().join();
+ this.raknet.setListener(this);
}
@Override
@@ -62,32 +62,27 @@ public void setNetwork(Network network) {
@Override
public boolean process() {
- boolean work = false;
- if (this.handler.handlePacket()) {
- work = true;
- while (this.handler.handlePacket()) {
-
+ Iterator iterator = this.sessionListeners.iterator();
+ while (iterator.hasNext()) {
+ NukkitSessionListener listener = iterator.next();
+ Player player = listener.player;
+ if (listener.disconnectReason != null) {
+ player.close(player.getLeaveMessage(), listener.disconnectReason, false);
+ iterator.remove();
+ continue;
+ }
+ DataPacket packet;
+ while ((packet = listener.packets.poll()) != null) {
+ listener.player.handleDataPacket(packet);
}
}
-
- return work;
- }
-
- @Override
- public void closeSession(String identifier, String reason) {
- if (this.players.containsKey(identifier)) {
- Player player = this.players.get(identifier);
- this.identifiers.remove(player.rawHashCode());
- this.players.remove(identifier);
- this.networkLatency.remove(identifier);
- this.identifiersACK.remove(identifier);
- player.close(player.getLeaveMessage(), reason);
- }
+ return true;
}
@Override
public int getNetworkLatency(Player player) {
- return this.networkLatency.get(this.identifiers.get(player.rawHashCode()));
+ RakNetServerSession session = this.raknet.getSession(player.getSocketAddress());
+ return session == null ? -1 : (int) session.getPing();
}
@Override
@@ -97,235 +92,177 @@ public void close(Player player) {
@Override
public void close(Player player, String reason) {
- if (this.identifiers.containsKey(player.rawHashCode())) {
- String id = this.identifiers.get(player.rawHashCode());
- this.players.remove(id);
- this.networkLatency.remove(id);
- this.identifiersACK.remove(id);
- this.closeSession(id, reason);
- this.identifiers.remove(player.rawHashCode());
+ RakNetServerSession session = this.raknet.getSession(player.getSocketAddress());
+ if (session != null) {
+ session.close();
}
}
@Override
public void shutdown() {
- this.handler.shutdown();
+ this.raknet.close();
}
@Override
public void emergencyShutdown() {
- this.handler.emergencyShutdown();
+ this.raknet.close();
}
@Override
- public void openSession(String identifier, String address, int port, long clientID) {
- PlayerCreationEvent ev = new PlayerCreationEvent(this, Player.class, Player.class, null, address, port);
- this.server.getPluginManager().callEvent(ev);
- Class extends Player> clazz = ev.getPlayerClass();
-
- try {
- Constructor constructor = clazz.getConstructor(SourceInterface.class, Long.class, String.class, int.class);
- Player player = (Player) constructor.newInstance(this, ev.getClientId(), ev.getAddress(), ev.getPort());
- this.players.put(identifier, player);
- this.networkLatency.put(identifier, 0);
- this.identifiersACK.put(identifier, 0);
- this.identifiers.put(player.rawHashCode(), identifier);
- this.server.addPlayer(identifier, player);
- } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
- Server.getInstance().getLogger().logException(e);
- }
+ public void blockAddress(InetAddress address) {
+ this.raknet.block(address);
}
@Override
- public void handleEncapsulated(String identifier, EncapsulatedPacket packet, int flags) {
- if (this.players.containsKey(identifier)) {
- DataPacket pk = null;
- try {
- if (packet.buffer.length > 0) {
- if (packet.buffer[0] == PING_DataPacket.ID) {
- PING_DataPacket pingPacket = new PING_DataPacket();
- pingPacket.buffer = packet.buffer;
- pingPacket.decode();
-
- this.networkLatency.put(identifier, (int) pingPacket.pingID);
- return;
- }
-
- pk = this.getPacket(packet.buffer);
- if (pk != null) {
- pk.decode();
- this.players.get(identifier).handleDataPacket(pk);
- }
- }
- } catch (Exception e) {
- this.server.getLogger().logException(e);
- if (Nukkit.DEBUG > 1 && pk != null) {
- MainLogger logger = this.server.getLogger();
-// if (logger != null) {
- logger.debug("Packet " + pk.getClass().getName() + " 0x" + Binary.bytesToHexString(packet.buffer));
- //logger.logException(e);
-// }
- }
-
- if (this.players.containsKey(identifier)) {
- this.handler.blockAddress(this.players.get(identifier).getAddress(), 5);
- }
- }
- }
+ public void blockAddress(InetAddress address, int timeout) {
+ this.raknet.block(address, timeout, TimeUnit.SECONDS);
}
@Override
- public void blockAddress(String address) {
- this.blockAddress(address, 300);
+ public void unblockAddress(InetAddress address) {
+ this.raknet.unblock(address);
}
@Override
- public void blockAddress(String address, int timeout) {
- this.handler.blockAddress(address, timeout);
+ public void sendRawPacket(InetSocketAddress socketAddress, ByteBuf payload) {
+ this.raknet.send(socketAddress, payload);
}
@Override
- public void unblockAddress(String address) {
- this.handler.unblockAddress(address);
+ public void setName(String name) {
+ QueryRegenerateEvent info = this.server.getQueryInformation();
+ String[] names = name.split("!@#"); //Split double names within the program
+ String motd = Utils.rtrim(names[0].replace(";", "\\;"), '\\');
+ String subMotd = names.length > 1 ? Utils.rtrim(names[1].replace(";", "\\;"), '\\') : "";
+ StringJoiner joiner = new StringJoiner(";")
+ .add("MCPE")
+ .add(motd)
+ .add(Integer.toString(ProtocolInfo.CURRENT_PROTOCOL))
+ .add(ProtocolInfo.MINECRAFT_VERSION_NETWORK)
+ .add(Integer.toString(info.getPlayerCount()))
+ .add(Integer.toString(info.getMaxPlayerCount()))
+ .add(Long.toString(this.raknet.getGuid()))
+ .add(subMotd)
+ .add(Server.getGamemodeString(this.server.getDefaultGamemode(), true))
+ .add("1");
+
+ this.advertisement = joiner.toString().getBytes(StandardCharsets.UTF_8);
}
@Override
- public void handleRaw(String address, int port, byte[] payload) {
- this.server.handlePacket(address, port, payload);
+ public Integer putPacket(Player player, DataPacket packet) {
+ return this.putPacket(player, packet, false);
}
@Override
- public void sendRawPacket(String address, int port, byte[] payload) {
- this.handler.sendRaw(address, port, payload);
+ public Integer putPacket(Player player, DataPacket packet, boolean needACK) {
+ return this.putPacket(player, packet, needACK, false);
}
@Override
- public void notifyACK(String identifier, int identifierACK) {
- // TODO: Better ACK notification implementation!
- for (Player p : server.getOnlinePlayers().values()) {
- p.notifyACK(identifierACK);
+ public Integer putPacket(Player player, DataPacket packet, boolean needACK, boolean immediate) {
+ RakNetServerSession session = this.raknet.getSession(player.getSocketAddress());
+ if (session == null) {
+ return null;
}
- }
- @Override
- public void setName(String name) {
- QueryRegenerateEvent info = this.server.getQueryInformation();
- String[] names = name.split("!@#"); //Split double names within the program
- this.handler.sendOption("name",
- "MCPE;" + Utils.rtrim(names[0].replace(";", "\\;"), '\\') + ";" +
- ProtocolInfo.CURRENT_PROTOCOL + ";" +
- ProtocolInfo.MINECRAFT_VERSION_NETWORK + ";" +
- info.getPlayerCount() + ";" +
- info.getMaxPlayerCount() + ";" +
- this.server.getServerUniqueId().toString() + ";" +
- (names.length > 1 ? Utils.rtrim(names[1].replace(";", "\\;"), '\\') : "") + ";" +
- Server.getGamemodeString(this.server.getDefaultGamemode(), true) + ";");
- }
+ byte[] buffer;
+ if (packet.pid() == ProtocolInfo.BATCH_PACKET) {
+ buffer = ((BatchPacket) packet).payload;
+ if (buffer == null) {
+ return null;
+ }
+ } else {
+ this.server.batchPackets(new Player[]{player}, new DataPacket[]{packet}, true);
+ return null;
+ }
+
+ ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer(1 + buffer.length);
+ byteBuf.writeByte(0xfe);
+ byteBuf.writeBytes(buffer);
+ byteBuf.readerIndex(0);
+
+ session.send(byteBuf, immediate ? RakNetPriority.IMMEDIATE : RakNetPriority.MEDIUM, packet.reliability,
+ packet.getChannel());
- public void setPortCheck(boolean value) {
- this.handler.sendOption("portChecking", String.valueOf(value));
+ return null;
}
@Override
- public void handleOption(String name, String value) {
- if ("bandwidth".equals(name)) {
- String[] v = value.split(";");
- this.network.addStatistics(Double.valueOf(v[0]), Double.valueOf(v[1]));
- }
+ public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {
+ return true;
}
@Override
- public Integer putPacket(Player player, DataPacket packet) {
- return this.putPacket(player, packet, false);
+ public byte[] onQuery(InetSocketAddress inetSocketAddress) {
+ return this.advertisement;
}
@Override
- public Integer putPacket(Player player, DataPacket packet, boolean needACK) {
- return this.putPacket(player, packet, needACK, false);
+ public void onSessionCreation(RakNetServerSession session) {
+ PlayerCreationEvent ev = new PlayerCreationEvent(this, Player.class, Player.class, null, session.getAddress());
+ this.server.getPluginManager().callEvent(ev);
+ Class extends Player> clazz = ev.getPlayerClass();
+
+ try {
+ Constructor constructor = clazz.getConstructor(SourceInterface.class, Long.class, InetSocketAddress.class);
+ Player player = (Player) constructor.newInstance(this, ev.getClientId(), ev.getSocketAddress());
+ this.server.addPlayer(session.getAddress(), player);
+ NukkitSessionListener listener = new NukkitSessionListener(player);
+ this.sessionListeners.add(listener);
+ session.setListener(listener);
+ } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
+ Server.getInstance().getLogger().logException(e);
+ }
}
@Override
- public Integer putPacket(Player player, DataPacket packet, boolean needACK, boolean immediate) {
- if (this.identifiers.containsKey(player.rawHashCode())) {
- byte[] buffer;
- if (packet.pid() == ProtocolInfo.BATCH_PACKET) {
- buffer = ((BatchPacket) packet).payload;
- } else if (!needACK) {
- this.server.batchPackets(new Player[]{player}, new DataPacket[]{packet}, true);
- return null;
- } else {
- if (!packet.isEncoded) {
- packet.encode();
- packet.isEncoded = true;
- }
- buffer = packet.getBuffer();
- try {
- buffer = Zlib.deflate(
- Binary.appendBytes(Binary.writeUnsignedVarInt(buffer.length), buffer),
- Server.getInstance().networkCompressionLevel);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- String identifier = this.identifiers.get(player.rawHashCode());
- EncapsulatedPacket pk = null;
- if (!needACK) {
- if (packet.encapsulatedPacket == null) {
- packet.encapsulatedPacket = new CacheEncapsulatedPacket();
- packet.encapsulatedPacket.identifierACK = null;
- packet.encapsulatedPacket.buffer = Binary.appendBytes((byte) 0xfe, buffer);
- if (packet.getChannel() != 0) {
- packet.encapsulatedPacket.reliability = 3;
- packet.encapsulatedPacket.orderChannel = packet.getChannel();
- packet.encapsulatedPacket.orderIndex = 0;
- } else {
- packet.encapsulatedPacket.reliability = 2;
- }
- }
- pk = packet.encapsulatedPacket;
- }
+ public void onUnhandledDatagram(ChannelHandlerContext ctx, DatagramPacket datagramPacket) {
+ this.server.handlePacket(datagramPacket.sender(), datagramPacket.content());
+ }
- if (pk == null) {
- pk = new EncapsulatedPacket();
- pk.buffer = Binary.appendBytes((byte) 0xfe, buffer);
- if (packet.getChannel() != 0) {
- packet.reliability = 3;
- packet.orderChannel = packet.getChannel();
- packet.orderIndex = 0;
- } else {
- packet.reliability = 2;
- }
+ @RequiredArgsConstructor
+ private class NukkitSessionListener implements RakNetSessionListener {
+ private final Player player;
+ private final Queue packets = new ConcurrentLinkedQueue<>();
+ private String disconnectReason = null;
- if (needACK) {
- int iACK = this.identifiersACK.get(identifier);
- iACK++;
- pk.identifierACK = iACK;
- this.identifiersACK.put(identifier, iACK);
- }
- }
+ @Override
+ public void onSessionChangeState(RakNetState rakNetState) {
- this.handler.sendEncapsulated(identifier, pk, (needACK ? RakNet.FLAG_NEED_ACK : 0) | (immediate ? RakNet.PRIORITY_IMMEDIATE : RakNet.PRIORITY_NORMAL));
+ }
- return pk.identifierACK;
+ @Override
+ public void onDisconnect(DisconnectReason disconnectReason) {
+ if (disconnectReason == DisconnectReason.TIMED_OUT) {
+ this.disconnectReason = "Timed out";
+ } else {
+ this.disconnectReason = "Disconnected from Server";
+ }
}
- return null;
- }
+ @Override
+ public void onEncapsulated(EncapsulatedPacket packet) {
+ ByteBuf buffer = packet.getBuffer();
+ short packetId = buffer.readUnsignedByte();
+ if (packetId == 0xfe) {
+ DataPacket batchPacket = RakNetInterface.this.network.getPacket(ProtocolInfo.BATCH_PACKET);
+ if (batchPacket == null) {
+ return;
+ }
- private DataPacket getPacket(byte[] buffer) {
- int start = 0;
+ byte[] packetBuffer = new byte[buffer.readableBytes()];
+ buffer.readBytes(packetBuffer);
+ batchPacket.setBuffer(packetBuffer);
+ batchPacket.decode();
- if (buffer[0] == (byte) 0xfe) {
- start++;
+ packets.offer(batchPacket);
+ }
}
- DataPacket data = this.network.getPacket(ProtocolInfo.BATCH_PACKET);
- if (data == null) {
- return null;
+ @Override
+ public void onDirect(ByteBuf byteBuf) {
+ // We don't allow any direct packets so ignore.
}
-
- data.setBuffer(buffer, start);
-
- return data;
}
}
diff --git a/src/main/java/cn/nukkit/network/protocol/BatchPacket.java b/src/main/java/cn/nukkit/network/protocol/BatchPacket.java
index c6ba45f9c2d..5e67c79ef79 100644
--- a/src/main/java/cn/nukkit/network/protocol/BatchPacket.java
+++ b/src/main/java/cn/nukkit/network/protocol/BatchPacket.java
@@ -1,7 +1,5 @@
package cn.nukkit.network.protocol;
-import cn.nukkit.network.CacheEncapsulatedPacket;
-
/**
* author: MagicDroidX
* Nukkit Project
@@ -28,12 +26,5 @@ public void encode() {
public void trim() {
setBuffer(null);
- if (encapsulatedPacket != null) {
- payload = null;
- if (encapsulatedPacket instanceof CacheEncapsulatedPacket && !encapsulatedPacket.hasSplit) {
- CacheEncapsulatedPacket cached = (CacheEncapsulatedPacket) encapsulatedPacket;
- if (cached.internalData != null) cached.buffer = null;
- }
- }
}
}
diff --git a/src/main/java/cn/nukkit/network/protocol/DataPacket.java b/src/main/java/cn/nukkit/network/protocol/DataPacket.java
index 601add933c7..f3b82956922 100644
--- a/src/main/java/cn/nukkit/network/protocol/DataPacket.java
+++ b/src/main/java/cn/nukkit/network/protocol/DataPacket.java
@@ -1,10 +1,10 @@
package cn.nukkit.network.protocol;
import cn.nukkit.Server;
-import cn.nukkit.raknet.protocol.EncapsulatedPacket;
import cn.nukkit.utils.Binary;
import cn.nukkit.utils.BinaryStream;
import cn.nukkit.utils.Zlib;
+import com.nukkitx.network.raknet.RakNetReliability;
/**
* author: MagicDroidX
@@ -15,10 +15,7 @@ public abstract class DataPacket extends BinaryStream implements Cloneable {
public boolean isEncoded = false;
private int channel = 0;
- public EncapsulatedPacket encapsulatedPacket;
- public byte reliability;
- public Integer orderIndex = null;
- public Integer orderChannel = null;
+ public RakNetReliability reliability = RakNetReliability.RELIABLE_ORDERED;
public abstract byte pid();
diff --git a/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java b/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java
index 9a01f9e2f14..9bce9daee48 100644
--- a/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java
+++ b/src/main/java/cn/nukkit/network/protocol/ResourcePacksInfoPacket.java
@@ -9,7 +9,7 @@ public class ResourcePacksInfoPacket extends DataPacket {
public static final byte NETWORK_ID = ProtocolInfo.RESOURCE_PACKS_INFO_PACKET;
public boolean mustAccept;
- public boolean unknownBool;
+ public boolean scripting;
public ResourcePack[] behaviourPackEntries = new ResourcePack[0];
public ResourcePack[] resourcePackEntries = new ResourcePack[0];
@@ -22,7 +22,7 @@ public void decode() {
public void encode() {
this.reset();
this.putBoolean(this.mustAccept);
- this.putBoolean(this.unknownBool);
+ this.putBoolean(this.scripting);
encodePacks(this.resourcePackEntries);
encodePacks(this.behaviourPackEntries);
@@ -37,7 +37,7 @@ private void encodePacks(ResourcePack[] packs) {
this.putString(""); // encryption key
this.putString(""); // sub-pack name
this.putString(""); // content identity
- this.putBoolean(false); // ???
+ this.putBoolean(false); // scripting
}
}
diff --git a/src/main/java/cn/nukkit/network/query/QueryHandler.java b/src/main/java/cn/nukkit/network/query/QueryHandler.java
index c0e886e7a03..d9b45fb9c06 100644
--- a/src/main/java/cn/nukkit/network/query/QueryHandler.java
+++ b/src/main/java/cn/nukkit/network/query/QueryHandler.java
@@ -2,11 +2,18 @@
import cn.nukkit.Server;
import cn.nukkit.event.server.QueryRegenerateEvent;
-import cn.nukkit.utils.Binary;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
/**
* author: MagicDroidX
@@ -54,53 +61,57 @@ public void regenerateToken() {
this.token = token;
}
- public static String getTokenString(byte[] token, String salt) {
- return getTokenString(new String(token), salt);
+ public static byte[] getTokenString(String token, InetAddress address) {
+ return getTokenString(token.getBytes(StandardCharsets.UTF_8), address);
}
-
- public static String getTokenString(String token, String salt) {
+ public static byte[] getTokenString(byte[] token, InetAddress address) {
try {
- return String.valueOf(Binary.readInt(Binary.subBytes(MessageDigest.getInstance("SHA-512").digest((salt + ":" + token).getBytes()), 7, 4)));
+ MessageDigest digest = MessageDigest.getInstance("MD5");
+ digest.update(address.toString().getBytes(StandardCharsets.UTF_8));
+ digest.update(token);
+ return Arrays.copyOf(digest.digest(), 4);
} catch (NoSuchAlgorithmException e) {
- return String.valueOf(new Random().nextInt());
+ return ByteBuffer.allocate(4).putInt(ThreadLocalRandom.current().nextInt()).array();
}
}
- public void handle(String address, int port, byte[] packet) {
- int offset = 2; //skip MAGIC
- byte packetType = packet[offset++];
- int sessionID = Binary.readInt(Binary.subBytes(packet, offset, 4));
- offset += 4;
- byte[] payload = Binary.subBytes(packet, offset);
+ public void handle(InetSocketAddress address, ByteBuf packet) {
+ short packetId = packet.readUnsignedByte();
+ int sessionId = packet.readInt();
- switch (packetType) {
+ switch (packetId) {
case HANDSHAKE:
- byte[] reply = Binary.appendBytes(
- HANDSHAKE,
- Binary.writeInt(sessionID),
- getTokenString(this.token, address).getBytes(),
- new byte[]{0x00}
- );
-
- this.server.getNetwork().sendPacket(address, port, reply);
+ ByteBuf reply = ByteBufAllocator.DEFAULT.ioBuffer(10); // 1 + 4 + 4 + 1
+ reply.writeByte(HANDSHAKE);
+ reply.writeInt(sessionId);
+ reply.writeBytes(getTokenString(this.token, address.getAddress()));
+ reply.writeByte(0);
+
+ this.server.getNetwork().sendPacket(address, reply);
break;
case STATISTICS:
- String token = String.valueOf(Binary.readInt(Binary.subBytes(payload, 0, 4)));
- if (!token.equals(getTokenString(this.token, address)) && !token.equals(getTokenString(this.lastToken, address))) {
+ byte[] token = new byte[4];
+ packet.readBytes(token);
+
+ if (!Arrays.equals(token, getTokenString(this.token, address.getAddress())) &&
+ !Arrays.equals(token, getTokenString(this.lastToken, address.getAddress()))) {
break;
}
if (this.timeout < System.currentTimeMillis()) {
this.regenerateInfo();
}
- reply = Binary.appendBytes(
- STATISTICS,
- Binary.writeInt(sessionID),
- payload.length == 8 ? this.longData : this.shortData
- );
+ reply = ByteBufAllocator.DEFAULT.ioBuffer(64);
+ reply.writeByte(STATISTICS);
+ reply.writeInt(sessionId);
+ if (packet.readableBytes() == 8) {
+ reply.writeBytes(this.longData);
+ } else {
+ reply.writeBytes(this.shortData);
+ }
- this.server.getNetwork().sendPacket(address, port, reply);
+ this.server.getNetwork().sendPacket(address, reply);
break;
}
}
diff --git a/src/main/java/cn/nukkit/raknet/RakNet.java b/src/main/java/cn/nukkit/raknet/RakNet.java
deleted file mode 100644
index 29d52788602..00000000000
--- a/src/main/java/cn/nukkit/raknet/RakNet.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package cn.nukkit.raknet;
-
-/**
- * author: MagicDroidX
- * Nukkit Project
- * UDP network library that follows the RakNet protocol for Nukkit Project
- * This is not affiliated with Jenkins Software LLC nor RakNet.
- */
-public abstract class RakNet {
-
- public static final String VERSION = "1.1.0";
- public static final byte PROTOCOL = 9;
- public static final byte[] MAGIC = new byte[]{
- (byte) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0x00,
- (byte) 0xfe, (byte) 0xfe, (byte) 0xfe, (byte) 0xfe,
- (byte) 0xfd, (byte) 0xfd, (byte) 0xfd, (byte) 0xfd,
- (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78
- };
-
- public static final byte PRIORITY_NORMAL = 0;
- public static final byte PRIORITY_IMMEDIATE = 1;
-
- public static final byte FLAG_NEED_ACK = 0b00001000;
-
- /*
- * ENCAPSULATED payload:
- * byte (identifier length)
- * byte[] (identifier)
- * byte (flags, last 3 bits, priority)
- * payload (binary internal EncapsulatedPacket)
- */
- public static final byte PACKET_ENCAPSULATED = 0x01;
-
- /*
- * OPEN_SESSION payload:
- * byte (identifier length)
- * byte[] (identifier)
- * byte (address length)
- * byte[] (address)
- * short (port)
- * long (clientID)
- */
- public static final byte PACKET_OPEN_SESSION = 0x02;
-
- /*
- * CLOSE_SESSION payload:
- * byte (identifier length)
- * byte[] (identifier)
- * string (reason)
- */
- public static final byte PACKET_CLOSE_SESSION = 0x03;
-
- /*
- * INVALID_SESSION payload:
- * byte (identifier length)
- * byte[] (identifier)
- */
- public static final byte PACKET_INVALID_SESSION = 0x04;
-
- /* SEND_QUEUE payload:
- * byte (identifier length)
- * byte[] (identifier)
- */
- public static final byte PACKET_SEND_QUEUE = 0x05;
-
- /*
- * ACK_NOTIFICATION payload:
- * byte (identifier length)
- * byte[] (identifier)
- * int (identifierACK)
- */
- public static final byte PACKET_ACK_NOTIFICATION = 0x06;
-
- /*
- * SET_OPTION payload:
- * byte (option name length)
- * byte[] (option name)
- * byte[] (option value)
- */
- public static final byte PACKET_SET_OPTION = 0x07;
-
- /*
- * RAW payload:
- * byte (address length)
- * byte[] (address from/to)
- * short (port)
- * byte[] (payload)
- */
- public static final byte PACKET_RAW = 0x08;
-
- /*
- * BLOCK_ADDRESS payload:
- * byte (address length)
- * byte[] (address)
- * int (timeout)
- */
- public static final byte PACKET_BLOCK_ADDRESS = 0x09;
-
- /*
- * UNBLOCK_ADDRESS payload:
- * byte (adress length)
- * byte[] (address)
- */
- public static final byte PACKET_UNBLOCK_ADDRESS = 0x10;
-
- /*
- * No payload
- *
- * Sends the disconnect message, removes sessions correctly, closes sockets.
- */
- public static final byte PACKET_SHUTDOWN = 0x7e;
-
- /*
- * No payload
- *
- * Leaves everything as-is and halts, other Threads can be in a post-crash condition.
- */
- public static final byte PACKET_EMERGENCY_SHUTDOWN = 0x7f;
-
-}
diff --git a/src/main/java/cn/nukkit/raknet/protocol/AcknowledgePacket.java b/src/main/java/cn/nukkit/raknet/protocol/AcknowledgePacket.java
deleted file mode 100644
index c61ba24fbf5..00000000000
--- a/src/main/java/cn/nukkit/raknet/protocol/AcknowledgePacket.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package cn.nukkit.raknet.protocol;
-
-import cn.nukkit.utils.Binary;
-import cn.nukkit.utils.BinaryStream;
-
-import java.util.TreeMap;
-
-/**
- * author: MagicDroidX
- * Nukkit Project
- */
-public abstract class AcknowledgePacket extends Packet {
-
- public TreeMap packets;
-
- @Override
- public void encode() {
- super.encode();
- int count = this.packets.size();
- int[] packets = new int[count];
-
- int index = 0;
- for (int i : this.packets.values()) {
- packets[index++] = i;
- }
- short records = 0;
- BinaryStream payload = new BinaryStream();
-
- if (count > 0) {
- int pointer = 1;
- int start = packets[0];
- int last = packets[0];
-
- while (pointer < count) {
- int current = packets[pointer++];
- int diff = current - last;
- if (diff == 1) {
- last = current;
- } else if (diff > 1) {
-
- if (start == last) {
- payload.putByte((byte) 0x01);
- payload.put(Binary.writeLTriad(start));
- start = last = current;
- } else {
- payload.putByte((byte) 0x00);
- payload.put(Binary.writeLTriad(start));
- payload.put(Binary.writeLTriad(last));
- start = last = current;
- }
- ++records;
- }
- }
-
- if (start == last) {
- payload.putByte((byte) 0x01);
- payload.put(Binary.writeLTriad(start));
- } else {
- payload.putByte((byte) 0x00);
- payload.put(Binary.writeLTriad(start));
- payload.put(Binary.writeLTriad(last));
- }
- ++records;
- }
-
- this.putShort(records);
- this.buffer = Binary.appendBytes(
- this.buffer,
- payload.getBuffer()
- );
- }
-
- @Override
- public void decode() {
- super.decode();
- short count = this.getSignedShort();
- this.packets = new TreeMap<>();
- int cnt = 0;
- for (int i = 0; i < count && !this.feof() && cnt < 4096; ++i) {
- if (this.getByte() == 0) {
- int start = this.getLTriad();
- int end = this.getLTriad();
- if ((end - start) > 512) {
- end = start + 512;
- }
- for (int c = start; c <= end; ++c) {
- packets.put(cnt++, c);
- }
- } else {
- this.packets.put(cnt++, this.getLTriad());
- }
- }
- }
-
- @Override
- public Packet clean() {
- this.packets = new TreeMap<>();
- return super.clean();
- }
-
- @Override
- public AcknowledgePacket clone() throws CloneNotSupportedException {
- AcknowledgePacket packet = (AcknowledgePacket) super.clone();
- packet.packets = new TreeMap<>(this.packets);
- return packet;
- }
-}
diff --git a/src/main/java/cn/nukkit/raknet/protocol/DataPacket.java b/src/main/java/cn/nukkit/raknet/protocol/DataPacket.java
deleted file mode 100644
index 5ab3dfda0d8..00000000000
--- a/src/main/java/cn/nukkit/raknet/protocol/DataPacket.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package cn.nukkit.raknet.protocol;
-
-import cn.nukkit.utils.Binary;
-
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * author: MagicDroidX
- * Nukkit Project
- */
-public abstract class DataPacket extends Packet {
-
- public ConcurrentLinkedQueue