This commit is contained in:
LOOHP 2020-08-03 22:54:12 +08:00
parent ad0d9b5c85
commit 39b631c89d
105 changed files with 346837 additions and 0 deletions

20
.classpath Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

23
.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Limbo</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,8 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

52
pom.xml Normal file
View File

@ -0,0 +1,52 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Limbo</groupId>
<artifactId>Limbo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.Querz</groupId>
<artifactId>NBT</artifactId>
<version>5.5</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.26</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>1.16-R0.3</version>
</dependency>
</dependencies>
</project>

171852
src/blocks.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
package com.loohp.limbo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import com.loohp.limbo.Server.ClientConnection;
import com.loohp.limbo.Server.ClientConnection.ClientState;
import com.loohp.limbo.Utils.CustomStringUtils;
public class Console {
private InputStream in;
private PrintStream out;
public Console(InputStream in, PrintStream out) {
this.in = in;
System.setOut(new ConsoleOutputStream(out));
this.out = System.out;
}
public void sendMessage(String message) {
out.println(message);
}
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while (true) {
try {
String[] input = CustomStringUtils.splitStringToArgs(reader.readLine());
if (input[0].equalsIgnoreCase("stop")) {
for (ClientConnection client : Limbo.getInstance().getServerConnection().getClients()) {
client.getSocket().close();
while (client.getSocket().isConnected()) {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.exit(0);
} else if (input[0].equalsIgnoreCase("say")) {
if (input.length > 1) {
String message = "[Server] " + String.join(" ", Arrays.copyOfRange(input, 1, input.length));
sendMessage(message);
for (ClientConnection client : Limbo.getInstance().getServerConnection().getClients()) {
if (client.getClientState().equals(ClientState.PLAY)) {
client.sendMessage(message);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ConsoleOutputStream extends PrintStream {
public ConsoleOutputStream(OutputStream out) {
super(out);
}
@Override
public void println(String string) {
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
super.println("[" + date + "] " + string);
}
}
}

View File

@ -0,0 +1,44 @@
package com.loohp.limbo.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Map;
import org.yaml.snakeyaml.Yaml;
public class FileConfiguration {
File file;
Map<String, Object> mapping;
public FileConfiguration(File file) throws FileNotFoundException {
this.file = file;
reloadConfig();
}
public FileConfiguration reloadConfig() {
try {
Yaml yml = new Yaml();
mapping = yml.load(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return this;
}
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> returnType) {
try {
String[] tree = key.split("\\.");
Map<String, Object> map = mapping;
for (int i = 0; i < tree.length - 1; i++) {
map = (Map<String, Object>) map.get(tree[i]);
}
return returnType.cast(map.get(tree[tree.length - 1]));
} catch (Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,155 @@
package com.loohp.limbo.File;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Optional;
import java.util.Properties;
import javax.imageio.ImageIO;
import com.loohp.limbo.Limbo;
import com.loohp.limbo.Location.Location;
import com.loohp.limbo.Utils.GameMode;
import com.loohp.limbo.Utils.NamespacedKey;
import com.loohp.limbo.World.World;
public class ServerProperties {
public static final String JSON_BASE_RESPONSE = "{\"version\":{\"name\":\"%VERSION%\",\"protocol\":%PROTOCOL%},\"players\":{\"max\":%MAXPLAYERS%,\"online\":%ONLINECLIENTS%},\"description\":%MOTD%,%FAVICON%\"modinfo\":{\"type\":\"FML\",\"modList\":[]}}";
File file;
int maxPlayers;
int serverPort;
String serverIp;
NamespacedKey levelName;
String schemFileName;
NamespacedKey levelDimension;
GameMode defaultGamemode;
Location worldSpawn;
boolean reducedDebugInfo;
boolean allowFlight;
String motdJson;
String versionString;
int protocol;
Optional<BufferedImage> favicon;
public ServerProperties(File file) throws IOException {
this.file = file;
Properties prop = new Properties();
prop.load(new FileInputStream(file));
protocol = 736;
maxPlayers = Integer.parseInt(prop.getProperty("max-players"));
serverPort = Integer.parseInt(prop.getProperty("server-port"));
serverIp = prop.getProperty("server-ip");
String[] level = prop.getProperty("level-name").split(";");
levelName = new NamespacedKey(level[0]);
schemFileName = level[1];
levelDimension = new NamespacedKey(prop.getProperty("level-dimension"));
defaultGamemode = GameMode.fromName(new NamespacedKey(prop.getProperty("default-gamemode")).getKey());
String[] locStr = prop.getProperty("world-spawn").split(";");
World world = Limbo.getInstance().getWorld(locStr[0]);
double x = Double.parseDouble(locStr[1]);
double y = Double.parseDouble(locStr[2]);
double z = Double.parseDouble(locStr[3]);
float yaw = Float.parseFloat(locStr[4]);
float pitch = Float.parseFloat(locStr[5]);
worldSpawn = new Location(world, x, y, z, yaw, pitch);
reducedDebugInfo = Boolean.parseBoolean(prop.getProperty("reduced-debug-info"));
allowFlight = Boolean.parseBoolean(prop.getProperty("allow-flight"));
motdJson = prop.getProperty("motd");
versionString = prop.getProperty("version");
File png = new File("server-icon.png");
if (png.exists()) {
try {
BufferedImage image = ImageIO.read(png);
if (image.getHeight() == 64 && image.getWidth() == 64) {
favicon = Optional.of(image);
} else {
System.out.println("Unable to load server-icon.png! The image is not 64 x 64 in size!");
}
} catch (Exception e) {
System.out.println("Unable to load server-icon.png! Is it a png image?");
}
} else {
System.out.println("No server-icon.png found");
favicon = Optional.empty();
}
System.out.println("Loaded server.properties");
}
public Optional<BufferedImage> getFavicon() {
return favicon;
}
public File getFile() {
return file;
}
public int getMaxPlayers() {
return maxPlayers;
}
public int getServerPort() {
return serverPort;
}
public String getServerIp() {
return serverIp;
}
public NamespacedKey getLevelName() {
return levelName;
}
public String getSchemFileName() {
return schemFileName;
}
public NamespacedKey getLevelDimension() {
return levelDimension;
}
public GameMode getDefaultGamemode() {
return defaultGamemode;
}
public Location getWorldSpawn() {
return worldSpawn;
}
public void setWorldSpawn(Location location) {
this.worldSpawn = location;
}
public boolean isReducedDebugInfo() {
return reducedDebugInfo;
}
public boolean isAllowFlight() {
return allowFlight;
}
public static String getJsonBaseResponse() {
return JSON_BASE_RESPONSE;
}
public String getMotdJson() {
return motdJson;
}
public String getVersionString() {
return versionString;
}
public int getProtocol() {
return protocol;
}
}

View File

@ -0,0 +1,190 @@
package com.loohp.limbo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.loohp.limbo.File.ServerProperties;
import com.loohp.limbo.Location.Location;
import com.loohp.limbo.Server.ServerConnection;
import com.loohp.limbo.Server.Packets.Packet;
import com.loohp.limbo.Server.Packets.PacketHandshakingIn;
import com.loohp.limbo.Server.Packets.PacketLoginInLoginStart;
import com.loohp.limbo.Server.Packets.PacketLoginOutLoginSuccess;
import com.loohp.limbo.Server.Packets.PacketPlayInChat;
import com.loohp.limbo.Server.Packets.PacketPlayInKeepAlive;
import com.loohp.limbo.Server.Packets.PacketPlayInPosition;
import com.loohp.limbo.Server.Packets.PacketPlayInPositionAndLook;
import com.loohp.limbo.Server.Packets.PacketPlayOutChat;
import com.loohp.limbo.Server.Packets.PacketPlayOutKeepAlive;
import com.loohp.limbo.Server.Packets.PacketPlayOutLogin;
import com.loohp.limbo.Server.Packets.PacketPlayOutMapChunk;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerAbilities;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo;
import com.loohp.limbo.Server.Packets.PacketPlayOutPositionAndLook;
import com.loohp.limbo.Server.Packets.PacketPlayOutShowPlayerSkins;
import com.loohp.limbo.Server.Packets.PacketPlayOutSpawnPosition;
import com.loohp.limbo.Server.Packets.PacketPlayOutUpdateViewPosition;
import com.loohp.limbo.Server.Packets.PacketStatusInPing;
import com.loohp.limbo.Server.Packets.PacketStatusInRequest;
import com.loohp.limbo.Server.Packets.PacketStatusOutPong;
import com.loohp.limbo.Server.Packets.PacketStatusOutResponse;
import com.loohp.limbo.Utils.ImageUtils;
import com.loohp.limbo.World.Schematic;
import com.loohp.limbo.World.World;
import net.querz.nbt.io.NBTUtil;
import net.querz.nbt.tag.CompoundTag;
public class Limbo {
private static Limbo instance;
public static void main(String args[]) throws IOException {
new Limbo();
}
public static Limbo getInstance() {
return instance;
}
//===========================
private ServerConnection server;
private Console console;
private List<World> worlds = new ArrayList<>();
private ServerProperties properties;
public Limbo() throws IOException {
instance = this;
console = new Console(System.in, System.out);
String spName = "server.properties";
File sp = new File(spName);
if (!sp.exists()) {
try (InputStream in = getClass().getClassLoader().getResourceAsStream(spName)) {
Files.copy(in, sp.toPath());
} catch (IOException e) {
e.printStackTrace();
}
}
properties = new ServerProperties(sp);
Map<Integer, Class<? extends Packet>> HandshakeIn = new HashMap<>();
HandshakeIn.put(0x00, PacketHandshakingIn.class);
Packet.setHandshakeIn(HandshakeIn);
Map<Integer, Class<? extends Packet>> StatusIn = new HashMap<>();
StatusIn.put(0x00, PacketStatusInRequest.class);
StatusIn.put(0x01, PacketStatusInPing.class);
Packet.setStatusIn(StatusIn);
Map<Class<? extends Packet>, Integer> StatusOut = new HashMap<>();
StatusOut.put(PacketStatusOutResponse.class, 0x00);
StatusOut.put(PacketStatusOutPong.class, 0x01);
Packet.setStatusOut(StatusOut);
Map<Integer, Class<? extends Packet>> LoginIn = new HashMap<>();
LoginIn.put(0x00, PacketLoginInLoginStart.class);
Packet.setLoginIn(LoginIn);
Map<Class<? extends Packet>, Integer> LoginOut = new HashMap<>();
LoginOut.put(PacketLoginOutLoginSuccess.class, 0x02);
Packet.setLoginOut(LoginOut);
Map<Integer, Class<? extends Packet>> PlayIn = new HashMap<>();
PlayIn.put(0x10, PacketPlayInKeepAlive.class);
PlayIn.put(0x12, PacketPlayInPosition.class);
PlayIn.put(0x13, PacketPlayInPositionAndLook.class);
PlayIn.put(0x03, PacketPlayInChat.class);
Packet.setPlayIn(PlayIn);
Map<Class<? extends Packet>, Integer> PlayOut = new HashMap<>();
PlayOut.put(PacketPlayOutLogin.class, 0x25);
PlayOut.put(PacketPlayOutSpawnPosition.class, 0x42);
PlayOut.put(PacketPlayOutPositionAndLook.class, 0x35);
PlayOut.put(PacketPlayOutMapChunk.class, 0x21);
PlayOut.put(PacketPlayOutKeepAlive.class, 0x20);
PlayOut.put(PacketPlayOutUpdateViewPosition.class, 0x40);
PlayOut.put(PacketPlayOutPlayerInfo.class, 0x33);
PlayOut.put(PacketPlayOutShowPlayerSkins.class, 0x44);
PlayOut.put(PacketPlayOutPlayerAbilities.class, 0x31);
PlayOut.put(PacketPlayOutChat.class, 0x0E);
Packet.setPlayOut(PlayOut);
worlds.add(loadDefaultWorld());
Location spawn = properties.getWorldSpawn();
properties.setWorldSpawn(new Location(getWorld(properties.getLevelName().getKey()), spawn.getX(), spawn.getY(), spawn.getZ(), spawn.getYaw(), spawn.getPitch()));
server = new ServerConnection(properties.getServerIp(), properties.getServerPort());
console.run();
}
private World loadDefaultWorld() throws IOException {
File schem = new File(properties.getSchemFileName());
if (!schem.exists()) {
System.out.println("Schemetic file " + properties.getSchemFileName() + " for world " + properties.getLevelName() + " not found!");
return null;
}
World world = Schematic.toWorld(properties.getLevelName().getKey(), (CompoundTag) NBTUtil.read(schem).getTag());
System.out.println("Loaded world " + properties.getLevelName() + " with the schematic file " + properties.getSchemFileName());
return world;
}
public ServerProperties getServerProperties() {
return properties;
}
public ServerConnection getServerConnection() {
return server;
}
public Console getConsole() {
return console;
}
public List<World> getWorlds() {
return new ArrayList<>(worlds);
}
public World getWorld(String name) {
for (World world : worlds) {
if (world.getName().equalsIgnoreCase(name)) {
return world;
}
}
return null;
}
public String getServerListResponseJson() throws IOException {
String base = ServerProperties.JSON_BASE_RESPONSE;
base = base.replace("%VERSION%", properties.getVersionString());
base = base.replace("%PROTOCOL%", String.valueOf(properties.getProtocol()));
base = base.replace("%MOTD%", properties.getMotdJson());
base = base.replace("%MAXPLAYERS%", String.valueOf(properties.getMaxPlayers()));
base = base.replace("%ONLINECLIENTS%", String.valueOf(server.getClients().size()));
if (properties.getFavicon().isPresent()) {
String icon = "\"favicon\":\"data:image/png;base64," + ImageUtils.imgToBase64String(properties.getFavicon().get(), "png") + "\",";
base = base.replace("%FAVICON%", icon);
} else {
base = base.replace("%FAVICON%", "");
}
return base;
}
}

View File

@ -0,0 +1,80 @@
package com.loohp.limbo.Location;
import com.loohp.limbo.World.World;
public class Location {
World world;
double x;
double y;
double z;
float yaw;
float pitch;
public Location(World world, double x, double y, double z, float yaw, float pitch) {
this.world = world;
this.x = x;
this.y = y;
this.z = z;
this.yaw = yaw;
this.pitch = pitch;
}
public Location(World world, double x, double y, double z) {
this(world, x, y, z, 0, 0);
}
@Override
public Location clone() {
return new Location(this.world, this.x, this.y, this.z, this.yaw, this.pitch);
}
public World getWorld() {
return world;
}
public void setWorld(World world) {
this.world = world;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public double getZ() {
return z;
}
public void setZ(double z) {
this.z = z;
}
public float getYaw() {
return yaw;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public float getPitch() {
return pitch;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
}

View File

@ -0,0 +1,331 @@
package com.loohp.limbo.Server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import com.loohp.limbo.Limbo;
import com.loohp.limbo.File.ServerProperties;
import com.loohp.limbo.Location.Location;
import com.loohp.limbo.Server.Packets.Packet;
import com.loohp.limbo.Server.Packets.PacketHandshakingIn;
import com.loohp.limbo.Server.Packets.PacketLoginInLoginStart;
import com.loohp.limbo.Server.Packets.PacketLoginOutLoginSuccess;
import com.loohp.limbo.Server.Packets.PacketPlayInChat;
import com.loohp.limbo.Server.Packets.PacketPlayInKeepAlive;
import com.loohp.limbo.Server.Packets.PacketPlayInPosition;
import com.loohp.limbo.Server.Packets.PacketPlayInPositionAndLook;
import com.loohp.limbo.Server.Packets.PacketPlayOutChat;
import com.loohp.limbo.Server.Packets.PacketPlayOutLogin;
import com.loohp.limbo.Server.Packets.PacketPlayOutMapChunk;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerAbilities;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerAbilities.PlayerAbilityFlags;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoAction;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoData;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer.PlayerSkinProperty;
import com.loohp.limbo.Server.Packets.PacketPlayOutPositionAndLook;
import com.loohp.limbo.Server.Packets.PacketPlayOutShowPlayerSkins;
import com.loohp.limbo.Server.Packets.PacketPlayOutSpawnPosition;
import com.loohp.limbo.Server.Packets.PacketPlayOutUpdateViewPosition;
import com.loohp.limbo.Server.Packets.PacketStatusInPing;
import com.loohp.limbo.Server.Packets.PacketStatusInRequest;
import com.loohp.limbo.Server.Packets.PacketStatusOutPong;
import com.loohp.limbo.Server.Packets.PacketStatusOutResponse;
import com.loohp.limbo.Utils.DataTypeIO;
import com.loohp.limbo.Utils.SkinUtils;
import com.loohp.limbo.Utils.SkinUtils.SkinResponse;
import com.loohp.limbo.World.BlockPosition;
import com.loohp.limbo.World.DimensionRegistry;
import com.loohp.limbo.World.World;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import net.querz.mca.Chunk;
public class ClientConnection extends Thread {
public enum ClientState {
HANDSHAKE,
STATUS,
LOGIN,
PLAY,
DISCONNECTED;
}
private Socket client_socket;
private boolean running;
private ClientState state;
private String username;
private UUID uuid;
private Location location;
private long lastKeepAlivePayLoad;
public long getLastKeepAlivePayLoad() {
return lastKeepAlivePayLoad;
}
public void setLastKeepAlivePayLoad(long payLoad) {
this.lastKeepAlivePayLoad = payLoad;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public String getUsername() {
return username;
}
public UUID getUuid() {
return uuid;
}
public ClientConnection(Socket client_socket) {
this.client_socket = client_socket;
}
public ClientState getClientState() {
return state;
}
public Socket getSocket() {
return client_socket;
}
public boolean isRunning() {
return running;
}
public int getEntityId() {
return Limbo.getInstance().getServerConnection().getClients().indexOf(this);
}
public void sendMessage(String message) {
sendMessage(TextComponent.fromLegacyText(message));
}
public void sendMessage(BaseComponent component) {
sendMessage(new BaseComponent[] {component});
}
public void sendMessage(BaseComponent[] component) {
try {
DataOutputStream output = new DataOutputStream(client_socket.getOutputStream());
PacketPlayOutChat chat = new PacketPlayOutChat(ComponentSerializer.toString(component), 0, new UUID(0, 0));
byte[] packetByte = chat.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
running = true;
state = ClientState.HANDSHAKE;
try {
client_socket.setKeepAlive(true);
DataInputStream input = new DataInputStream(client_socket.getInputStream());
DataOutputStream output = new DataOutputStream(client_socket.getOutputStream());
DataTypeIO.readVarInt(input);
int handShakeId = DataTypeIO.readVarInt(input);
PacketHandshakingIn handshake = (PacketHandshakingIn) Packet.getHandshakeIn().get(handShakeId).getConstructor(DataInputStream.class).newInstance(input);
switch (handshake.getHandshakeType()) {
case STATUS:
state = ClientState.STATUS;
while (client_socket.isConnected()) {
DataTypeIO.readVarInt(input);
int packetId = DataTypeIO.readVarInt(input);
Class<? extends Packet> packetType = Packet.getStatusIn().get(packetId);
if (packetType == null) {
//do nothing
} else if (packetType.equals(PacketStatusInRequest.class)) {
String str = client_socket.getInetAddress().getHostName() + ":" + client_socket.getPort();
System.out.println("[/" + str + "] <-> InitialHandler has pinged");
PacketStatusOutResponse packet = new PacketStatusOutResponse(Limbo.getInstance().getServerListResponseJson());
byte[] packetByte = packet.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
} else if (packetType.equals(PacketStatusInPing.class)) {
PacketStatusInPing ping = (PacketStatusInPing) packetType.getConstructor(DataInputStream.class).newInstance(input);
PacketStatusOutPong packet = new PacketStatusOutPong(ping.getPayload());
byte[] packetByte = packet.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
break;
}
}
break;
case LOGIN:
state = ClientState.LOGIN;
while (client_socket.isConnected()) {
int size = DataTypeIO.readVarInt(input);
int packetId = DataTypeIO.readVarInt(input);
Class<? extends Packet> packetType = Packet.getLoginIn().get(packetId);
if (packetType == null) {
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
} else if (packetType.equals(PacketLoginInLoginStart.class)) {
PacketLoginInLoginStart start = (PacketLoginInLoginStart) packetType.getConstructor(DataInputStream.class).newInstance(input);
username = start.getUsername();
uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(uuid, start.getUsername());
byte[] packetByte = success.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
state = ClientState.PLAY;
break;
}
}
break;
}
if (state == ClientState.PLAY) {
TimeUnit.MILLISECONDS.sleep(500);
ServerProperties p = Limbo.getInstance().getServerProperties();
PacketPlayOutLogin join = new PacketPlayOutLogin(getEntityId(), false, p.getDefaultGamemode(), new String[] {p.getLevelName().toString()}, DimensionRegistry.getCodec(), p.getLevelDimension().toString(), p.getLevelName().toString(), 0, (byte) p.getMaxPlayers(), 8, p.isReducedDebugInfo(), true, false, false);
byte[] packetByte = join.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
Location s = p.getWorldSpawn();
//PacketPlayOutKeepAlive alive = new PacketPlayOutKeepAlive((long) (Math.random() * Long.MAX_VALUE));
World world = s.getWorld();
for (int x = 0; x < world.getChunks().length; x++) {
for (int z = 0; z < world.getChunks()[x].length; z++) {
Chunk chunk = world.getChunks()[x][z];
if (chunk != null) {
PacketPlayOutMapChunk chunkdata = new PacketPlayOutMapChunk(x, z, chunk);
packetByte = chunkdata.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
//System.out.println(x + ", " + z);
}
}
}
PacketPlayOutSpawnPosition spawnPos = new PacketPlayOutSpawnPosition(BlockPosition.from(s));
packetByte = spawnPos.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
PacketPlayOutPositionAndLook positionLook = new PacketPlayOutPositionAndLook(s.getX(), s.getY(), s.getZ(), s.getYaw(), s.getPitch(), 1);
location = new Location(world, s.getX(), s.getY(), s.getZ(), s.getYaw(), s.getPitch());
packetByte = positionLook.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
SkinResponse skinresponce = SkinUtils.getSkinFromMojangServer(username);
PlayerSkinProperty skin = skinresponce != null ? new PlayerSkinProperty(skinresponce.getSkin(), skinresponce.getSignature()) : null;
PacketPlayOutPlayerInfo info = new PacketPlayOutPlayerInfo(PlayerInfoAction.ADD_PLAYER, uuid, new PlayerInfoData.PlayerInfoDataAddPlayer(username, Optional.ofNullable(skin), p.getDefaultGamemode(), 0, false, Optional.empty()));
packetByte = info.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
/*
for (ClientConnection client : Limbo.getInstance().getServerConnection().getClients()) {
DataOutputStream other = new DataOutputStream(client.getSocket().getOutputStream());
DataTypeIO.writeVarInt(other, packetByte.length);
other.write(packetByte);
}
*/
PacketPlayOutShowPlayerSkins show = new PacketPlayOutShowPlayerSkins(getEntityId());
packetByte = show.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
PacketPlayOutPlayerAbilities abilities;
if (p.isAllowFlight()) {
abilities = new PacketPlayOutPlayerAbilities(0.05F, 0.1F, PlayerAbilityFlags.ALLOW_FLYING);
} else {
abilities = new PacketPlayOutPlayerAbilities(0.05F, 0.1F);
}
packetByte = abilities.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
String str = client_socket.getInetAddress().getHostName() + ":" + client_socket.getPort() + "|" + username;
System.out.println("[/" + str + "] <-> Player had connected to the Limbo server!");
while (client_socket.isConnected()) {
try {
int size = DataTypeIO.readVarInt(input);
int packetId = DataTypeIO.readVarInt(input);
Class<? extends Packet> packetType = Packet.getPlayIn().get(packetId);
//System.out.println(packetId);
if (packetType == null) {
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
} else if (packetType.equals(PacketPlayInPositionAndLook.class)) {
PacketPlayInPositionAndLook pos = new PacketPlayInPositionAndLook(input);
location = new Location(location.getWorld(), pos.getX(), pos.getY(), pos.getZ(), pos.getYaw(), pos.getPitch());
PacketPlayOutUpdateViewPosition response = new PacketPlayOutUpdateViewPosition((int) location.getX() >> 4, (int) location.getZ() >> 4);
packetByte = response.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
} else if (packetType.equals(PacketPlayInPosition.class)) {
PacketPlayInPosition pos = new PacketPlayInPosition(input);
location = new Location(location.getWorld(), pos.getX(), pos.getY(), pos.getZ(), location.getYaw(), location.getPitch());
PacketPlayOutUpdateViewPosition response = new PacketPlayOutUpdateViewPosition((int) location.getX() >> 4, (int) location.getZ() >> 4);
packetByte = response.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
} else if (packetType.equals(PacketPlayInKeepAlive.class)) {
PacketPlayInKeepAlive alive = new PacketPlayInKeepAlive(input);
if (alive.getPayload() != lastKeepAlivePayLoad) {
System.out.println("Incorrect Payload recieved in KeepAlive packet for player " + username);
break;
}
} else if (packetType.equals(PacketPlayInChat.class)) {
PacketPlayInChat chat = new PacketPlayInChat(input);
if (chat.getMessage().startsWith("/")) {
//TO-DO COMMANDS
} else {
String message = "<" + username + "> " + chat.getMessage();
Limbo.getInstance().getConsole().sendMessage(message);
for (ClientConnection client : Limbo.getInstance().getServerConnection().getClients()) {
client.sendMessage(message);
}
}
}
} catch (Exception e) {
break;
}
}
str = client_socket.getInetAddress().getHostName() + ":" + client_socket.getPort() + "|" + username;
System.out.println("[/" + str + "] <-> Player had disconnected!");
}
} catch (Exception e) {}
try {
client_socket.close();
} catch (IOException e) {}
state = ClientState.DISCONNECTED;
Limbo.getInstance().getServerConnection().getClients().remove(this);
running = false;
}
}

View File

@ -0,0 +1,44 @@
package com.loohp.limbo.Server;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import com.loohp.limbo.Limbo;
import com.loohp.limbo.Server.ClientConnection.ClientState;
import com.loohp.limbo.Server.Packets.PacketPlayOutKeepAlive;
import com.loohp.limbo.Utils.DataTypeIO;
public class KeepAliveSender extends Thread {
private Random random = new Random();
public KeepAliveSender() {
start();
}
@Override
public void run() {
while (true) {
try {
for (ClientConnection client : Limbo.getInstance().getServerConnection().getClients()) {
if (client.getClientState().equals(ClientState.PLAY)) {
try {
DataOutputStream output = new DataOutputStream(client.getSocket().getOutputStream());
PacketPlayOutKeepAlive packet = new PacketPlayOutKeepAlive(random.nextLong());
byte[] packetByte = packet.getBytes();
DataTypeIO.writeVarInt(output, packetByte.length);
output.write(packetByte);
client.setLastKeepAlivePayLoad(packet.getPayload());
} catch (IOException ignore) {}
}
}
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,80 @@
package com.loohp.limbo.Server.Packets;
import java.util.Map;
public class Packet {
private static Map<Integer, Class<? extends Packet>> HandshakeIn;
private static Map<Integer, Class<? extends Packet>> StatusIn;
private static Map<Class<? extends Packet>, Integer> StatusOut;
private static Map<Integer, Class<? extends Packet>> LoginIn;
private static Map<Class<? extends Packet>, Integer> LoginOut;
private static Map<Integer, Class<? extends Packet>> PlayIn;
private static Map<Class<? extends Packet>, Integer> PlayOut;
public static Map<Integer, Class<? extends Packet>> getHandshakeIn() {
return HandshakeIn;
}
public static void setHandshakeIn(Map<Integer, Class<? extends Packet>> handshakeIn) {
HandshakeIn = handshakeIn;
}
public static Map<Integer, Class<? extends Packet>> getStatusIn() {
return StatusIn;
}
public static void setStatusIn(Map<Integer, Class<? extends Packet>> statusIn) {
StatusIn = statusIn;
}
public static Map<Class<? extends Packet>, Integer> getStatusOut() {
return StatusOut;
}
public static void setStatusOut(Map<Class<? extends Packet>, Integer> statusOut) {
StatusOut = statusOut;
}
public static Map<Integer, Class<? extends Packet>> getLoginIn() {
return LoginIn;
}
public static void setLoginIn(Map<Integer, Class<? extends Packet>> loginIn) {
LoginIn = loginIn;
}
public static Map<Class<? extends Packet>, Integer> getLoginOut() {
return LoginOut;
}
public static void setLoginOut(Map<Class<? extends Packet>, Integer> loginOut) {
LoginOut = loginOut;
}
public static Map<Integer, Class<? extends Packet>> getPlayIn() {
return PlayIn;
}
public static void setPlayIn(Map<Integer, Class<? extends Packet>> playIn) {
PlayIn = playIn;
}
public static Map<Class<? extends Packet>, Integer> getPlayOut() {
return PlayOut;
}
public static void setPlayOut(Map<Class<? extends Packet>, Integer> playOut) {
PlayOut = playOut;
}
//===========================================
public Packet() {
}
}

View File

@ -0,0 +1,69 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
import java.io.IOException;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketHandshakingIn extends Packet {
public static enum HandshakeType {
STATUS(1),
LOGIN(2);
int networkId;
HandshakeType(int networkId) {
this.networkId = networkId;
}
public int getNetworkId() {
return networkId;
}
public static HandshakeType fromNetworkId(int networkId) {
for (HandshakeType type : HandshakeType.values()) {
if (type.getNetworkId() == networkId) {
return type;
}
}
System.out.println("Invalid HandshakeType networkId, expected 0 or 1, but got " + networkId);
return null;
}
}
//==============================
private int protocolVersion;
private String serverAddress;
private int serverPort;
private HandshakeType handshakeType;
public PacketHandshakingIn(int protocolVersion, String serverAddress, int serverPort, HandshakeType handshakeType) {
this.protocolVersion = protocolVersion;
this.serverAddress = serverAddress;
this.serverPort = serverPort;
this.handshakeType = handshakeType;
}
public PacketHandshakingIn(DataInputStream in) throws IOException {
this(DataTypeIO.readVarInt(in), DataTypeIO.readString(in), in.readShort() & 0xFFFF, HandshakeType.fromNetworkId(DataTypeIO.readVarInt(in)));
}
public int getProtocolVersion() {
return protocolVersion;
}
public String getServerAddress() {
return serverAddress;
}
public int getServerPort() {
return serverPort;
}
public HandshakeType getHandshakeType() {
return handshakeType;
}
}

View File

@ -0,0 +1,24 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
import java.io.IOException;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketLoginInLoginStart extends Packet {
private String username;
public PacketLoginInLoginStart(String username) {
this.username = username;
}
public PacketLoginInLoginStart(DataInputStream in) throws IOException {
this(DataTypeIO.readString(in));
}
public String getUsername() {
return username;
}
}

View File

@ -0,0 +1,40 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketLoginOutLoginSuccess extends Packet {
private UUID uuid;
private String username;
public PacketLoginOutLoginSuccess(UUID uuid, String username) {
this.uuid = uuid;
this.username = username;
}
public UUID getUuid() {
return uuid;
}
public String getUsername() {
return username;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getLoginOut().get(getClass()));
DataTypeIO.writeUUID(output, uuid);
DataTypeIO.writeString(output, username, StandardCharsets.UTF_8);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,24 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
import java.io.IOException;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketPlayInChat extends Packet {
private String message;
public PacketPlayInChat(String message) {
this.message = message;
}
public PacketPlayInChat(DataInputStream in) throws IOException {
this(DataTypeIO.readString(in));
}
public String getMessage() {
return message;
}
}

View File

@ -0,0 +1,22 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInKeepAlive extends Packet {
long payload;
public PacketPlayInKeepAlive(long payload) {
this.payload = payload;
}
public PacketPlayInKeepAlive(DataInputStream in) throws IOException {
this(in.readLong());
}
public long getPayload() {
return payload;
}
}

View File

@ -0,0 +1,40 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInPosition extends Packet {
private double x;
private double y;
private double z;
private boolean onGround;
public PacketPlayInPosition(double x, double y, double z, boolean onGround) {
this.x = x;
this.y = y;
this.z = z;
this.onGround = onGround;
}
public PacketPlayInPosition(DataInputStream in) throws IOException {
this(in.readDouble(), in.readDouble(), in.readDouble(), in.readBoolean());
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
public boolean onGround() {
return onGround;
}
}

View File

@ -0,0 +1,52 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketPlayInPositionAndLook extends Packet {
private double x;
private double y;
private double z;
private float yaw;
private float pitch;
private boolean onGround;
public PacketPlayInPositionAndLook(double x, double y, double z, float yaw, float pitch, boolean onGround) {
this.x = x;
this.y = y;
this.z = z;
this.yaw = yaw;
this.pitch = pitch;
this.onGround = onGround;
}
public PacketPlayInPositionAndLook(DataInputStream in) throws IOException {
this(in.readDouble(), in.readDouble(), in.readDouble(), in.readFloat(), in.readFloat(), in.readBoolean());
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
public float getYaw() {
return yaw;
}
public float getPitch() {
return pitch;
}
public boolean onGround() {
return onGround;
}
}

View File

@ -0,0 +1,47 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketPlayOutChat extends Packet {
private String json;
private int position;
private UUID sender;
public PacketPlayOutChat(String json, int position, UUID sender) {
this.json = json;
this.position = position;
this.sender = sender;
}
public String getJson() {
return json;
}
public int getPosition() {
return position;
}
public UUID getSender() {
return sender;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, json, StandardCharsets.UTF_8);
output.writeByte(position);
DataTypeIO.writeUUID(output, sender);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,29 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class PacketPlayOutKeepAlive extends Packet {
long payload;
public PacketPlayOutKeepAlive(long payload) {
this.payload = payload;
}
public long getPayload() {
return payload;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeLong(payload);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,136 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.loohp.limbo.Utils.DataTypeIO;
import com.loohp.limbo.Utils.GameMode;
import net.querz.nbt.tag.CompoundTag;
public class PacketPlayOutLogin extends Packet {
private int entityId;
private boolean isHardcore;
private GameMode gamemode;
private String[] worldsNames;
private CompoundTag dimensionCodec;
private String dimension;
private String worldName;
private long hashedSeed;
private byte maxPlayers;
private int viewDistance;
private boolean reducedDebugInfo;
private boolean enableRespawnScreen;
private boolean isDebug;
private boolean isFlat;
public PacketPlayOutLogin(int entityId, boolean isHardcore, GameMode gamemode,
String[] worldsNames, CompoundTag dimensionCodec, String dimension, String worldName, long hashedSeed,
byte maxPlayers, int viewDistance, boolean reducedDebugInfo, boolean enableRespawnScreen, boolean isDebug,
boolean isFlat) {
this.entityId = entityId;
this.isHardcore = isHardcore;
this.gamemode = gamemode;
this.worldsNames = worldsNames;
this.dimensionCodec = dimensionCodec;
this.dimension = dimension;
this.worldName = worldName;
this.hashedSeed = hashedSeed;
this.maxPlayers = maxPlayers;
this.viewDistance = viewDistance;
this.reducedDebugInfo = reducedDebugInfo;
this.enableRespawnScreen = enableRespawnScreen;
this.isDebug = isDebug;
this.isFlat = isFlat;
}
public int getEntityId() {
return entityId;
}
public boolean isHardcore() {
return isHardcore;
}
public GameMode getGamemode() {
return gamemode;
}
public String[] getWorldsNames() {
return worldsNames;
}
public CompoundTag getDimensionCodec() {
return dimensionCodec;
}
public String getDimension() {
return dimension;
}
public String getWorldName() {
return worldName;
}
public long getHashedSeed() {
return hashedSeed;
}
public byte getMaxPlayers() {
return maxPlayers;
}
public int getViewDistance() {
return viewDistance;
}
public boolean isReducedDebugInfo() {
return reducedDebugInfo;
}
public boolean isEnableRespawnScreen() {
return enableRespawnScreen;
}
public boolean isDebug() {
return isDebug;
}
public boolean isFlat() {
return isFlat;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeInt(entityId);
int i = gamemode.getId();
if (isHardcore) {
i |= 8;
}
output.writeByte((byte) i);
output.writeByte((byte) gamemode.getId());
DataTypeIO.writeVarInt(output, worldsNames.length);
for (int u = 0; u < worldsNames.length; u++) {
DataTypeIO.writeString(output, worldsNames[u], StandardCharsets.UTF_8);
}
DataTypeIO.writeCompoundTag(output, dimensionCodec);
DataTypeIO.writeString(output, dimension, StandardCharsets.UTF_8);
DataTypeIO.writeString(output, worldName, StandardCharsets.UTF_8);
output.writeLong(hashedSeed);
output.writeByte((byte) maxPlayers);
DataTypeIO.writeVarInt(output, viewDistance);
output.writeBoolean(reducedDebugInfo);
output.writeBoolean(enableRespawnScreen);
output.writeBoolean(isDebug);
output.writeBoolean(isFlat);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,131 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Iterator;
import com.loohp.limbo.Utils.ChunkDataUtils;
import com.loohp.limbo.Utils.DataTypeIO;
import com.loohp.limbo.Utils.GeneratedDataUtils;
import net.querz.mca.Chunk;
import net.querz.mca.Section;
import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.ListTag;
public class PacketPlayOutMapChunk extends Packet {
private int chunkX;
private int chunkZ;
private Chunk chunk;
public PacketPlayOutMapChunk(int chunkX, int chunkZ, Chunk chunk) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
this.chunk = chunk;
}
public Chunk getChunk() {
return chunk;
}
public int getChunkX() {
return chunkX;
}
public int getChunkZ() {
return chunkZ;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeInt(chunkX);
output.writeInt(chunkZ);
output.writeBoolean(true);
output.writeBoolean(true);
int bitmask = 0;
for (int i = 0; i < 16; i++) {
Section section = chunk.getSection(i);
if (section != null) {
bitmask = bitmask | (int) Math.pow(2, i);
}
}
DataTypeIO.writeVarInt(output, bitmask);
DataTypeIO.writeCompoundTag(output, chunk.getHeightMaps());
//for (int i : chunk.getBiomes()) {
// output.writeInt(i);
//}
for (int i = 0; i < 1024; i++) {
output.writeInt(17);
}
ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(dataBuffer);
for (int i = 0; i < 16; i++) {
Section section = chunk.getSection(i);
if (section != null) {
int counter = 0;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
CompoundTag tag = section.getBlockStateAt(x, y, z);
if (tag != null && !tag.getString("Name").equals("minecraft:air")) {
counter++;
}
}
}
}
dataOut.writeShort(counter);
int newBits = 32 - Integer.numberOfLeadingZeros(section.getPalette().size() - 1);
newBits = Math.max(newBits, 4);
if (newBits <= 8) {
if (newBits == 4) {
dataOut.writeByte(4);
} else {
newBits = 8;
ChunkDataUtils.adjustBlockStateBits(newBits, section, chunk.getDataVersion());
dataOut.writeByte(8);
}
DataTypeIO.writeVarInt(dataOut, section.getPalette().size());
//System.out.println(section.getPalette().size());
Iterator<CompoundTag> itr1 = section.getPalette().iterator();
//System.out.println("Nonnull -> " + i + " " + newBits);
counter = 0;
while (itr1.hasNext()) {
CompoundTag tag = itr1.next();
DataTypeIO.writeVarInt(dataOut, GeneratedDataUtils.getGlobalPaletteIDFromState(tag));
//System.out.println(tag + " -> " + GeneratedDataUtils.getGlobalPaletteIDFromState(tag));
}
} else {
dataOut.writeByte(14);
}
DataTypeIO.writeVarInt(dataOut, section.getBlockStates().length);
for (int u = 0; u < section.getBlockStates().length; u++) {
dataOut.writeLong(section.getBlockStates()[u]);
//System.out.println(Arrays.toString(section.getBlockStates()));
}
}
}
byte[] data = dataBuffer.toByteArray();
DataTypeIO.writeVarInt(output, data.length);
output.write(data);
ListTag<CompoundTag> tileEntities = chunk.getTileEntities();
DataTypeIO.writeVarInt(output, tileEntities.size());
for (CompoundTag each : tileEntities) {
DataTypeIO.writeCompoundTag(output, each);
}
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,65 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class PacketPlayOutPlayerAbilities extends Packet {
public enum PlayerAbilityFlags {
INVULNERABLE(0x01),
FLY(0x02),
ALLOW_FLYING(0x04),
INSTANT_BREAK(0x08);
int bitvalue;
PlayerAbilityFlags(int bitvalue) {
this.bitvalue = bitvalue;
}
public int getValue() {
return bitvalue;
}
}
private PlayerAbilityFlags[] flags;
private float flySpeed;
private float fieldOfField;
public PacketPlayOutPlayerAbilities(float flySpeed, float fieldOfField, PlayerAbilityFlags... flags) {
this.flags = flags;
this.flySpeed = flySpeed;
this.fieldOfField = fieldOfField;
}
public PlayerAbilityFlags[] getFlags() {
return flags;
}
public float getFlySpeed() {
return flySpeed;
}
public float getFieldOfField() {
return fieldOfField;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
int value = 0;
for (PlayerAbilityFlags flag : flags) {
value = value | flag.getValue();
}
output.writeByte(value);
output.writeFloat(flySpeed);
output.writeFloat(fieldOfField);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,167 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.UUID;
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer;
import com.loohp.limbo.Utils.DataTypeIO;
import com.loohp.limbo.Utils.GameMode;
public class PacketPlayOutPlayerInfo extends Packet {
public enum PlayerInfoAction {
ADD_PLAYER(0), UPDATE_GAMEMODE(1), UPDATE_LATENCY(2), UPDATE_DISPLAY_NAME(3), REMOVE_PLAYER(4);
int id;
PlayerInfoAction(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
private PlayerInfoAction action;
private UUID uuid;
private PlayerInfoData data;
public PacketPlayOutPlayerInfo(PlayerInfoAction action, UUID uuid, PlayerInfoData data) {
this.action = action;
this.uuid = uuid;
this.data = data;
}
public PlayerInfoAction getAction() {
return action;
}
public UUID getUuid() {
return uuid;
}
public PlayerInfoData getData() {
return data;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeVarInt(output, action.getId());
DataTypeIO.writeVarInt(output, 1);
DataTypeIO.writeUUID(output, uuid);
switch (action) {
case ADD_PLAYER:
PlayerInfoDataAddPlayer data = (PlayerInfoDataAddPlayer) this.data;
DataTypeIO.writeString(output, data.getName(), StandardCharsets.UTF_8);
if (data.getProperty().isPresent()) {
DataTypeIO.writeVarInt(output, 1);
DataTypeIO.writeString(output, "textures", StandardCharsets.UTF_8);
DataTypeIO.writeString(output, data.getProperty().get().getSkin(), StandardCharsets.UTF_8);
output.writeBoolean(true);
DataTypeIO.writeString(output, data.getProperty().get().getSignature(), StandardCharsets.UTF_8);
} else {
DataTypeIO.writeVarInt(output, 0);
}
DataTypeIO.writeVarInt(output, data.getGamemode().getId());
DataTypeIO.writeVarInt(output, data.getPing());
if (data.getDisplayNameJson().isPresent()) {
output.writeBoolean(true);
DataTypeIO.writeString(output, data.getDisplayNameJson().get(), StandardCharsets.UTF_8);
} else {
output.writeBoolean(false);
}
break;
case REMOVE_PLAYER:
break;
case UPDATE_DISPLAY_NAME:
break;
case UPDATE_GAMEMODE:
break;
case UPDATE_LATENCY:
break;
}
return buffer.toByteArray();
}
// =========
public static class PlayerInfoData {
public static class PlayerInfoDataAddPlayer extends PlayerInfoData {
private String name;
private Optional<PlayerSkinProperty> skin;
private GameMode gamemode;
private int ping;
private boolean hasDisplayName;
private Optional<String> displayNameJson;
public PlayerInfoDataAddPlayer(String name, Optional<PlayerSkinProperty> skin, GameMode gamemode, int ping,
boolean hasDisplayName, Optional<String> displayNameJson) {
this.name = name;
this.skin = skin;
this.gamemode = gamemode;
this.ping = ping;
this.hasDisplayName = hasDisplayName;
this.displayNameJson = displayNameJson;
}
public String getName() {
return name;
}
public Optional<PlayerSkinProperty> getProperty() {
return skin;
}
public GameMode getGamemode() {
return gamemode;
}
public int getPing() {
return ping;
}
public boolean isHasDisplayName() {
return hasDisplayName;
}
public Optional<String> getDisplayNameJson() {
return displayNameJson;
}
public static class PlayerSkinProperty {
private String skin;
private String signature;
public PlayerSkinProperty(String skin, String signature) {
this.skin = skin;
this.signature = signature;
}
public String getSkin() {
return skin;
}
public String getSignature() {
return signature;
}
}
}
}
}

View File

@ -0,0 +1,100 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketPlayOutPositionAndLook extends Packet {
public enum PlayerTeleportFlags {
X((byte) 0x01),
Y((byte) 0x02),
Z((byte) 0x04),
Y_ROT((byte) 0x08),
X_ROT((byte) 0x10);
byte bit;
PlayerTeleportFlags(byte bit) {
this.bit = bit;
}
public byte getBit() {
return bit;
}
}
private double x;
private double y;
private double z;
private float yaw;
private float pitch;
private Set<PlayerTeleportFlags> flags;
private int teleportId;
public PacketPlayOutPositionAndLook(double x, double y, double z, float yaw, float pitch, int teleportId, PlayerTeleportFlags... flags) {
this.x = x;
this.y = y;
this.z = z;
this.yaw = yaw;
this.pitch = pitch;
this.teleportId = teleportId;
this.flags = Arrays.asList(flags).stream().collect(Collectors.toSet());
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
public float getYaw() {
return yaw;
}
public float getPitch() {
return pitch;
}
public Set<PlayerTeleportFlags> getFlags() {
return flags;
}
public int getTeleportId() {
return teleportId;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
output.writeDouble(x);
output.writeDouble(y);
output.writeDouble(z);
output.writeFloat(yaw);
output.writeFloat(pitch);
byte flag = 0;
for (PlayerTeleportFlags each : flags) {
flag = (byte) (flag | each.getBit());
}
output.writeByte(flag);
DataTypeIO.writeVarInt(output, teleportId);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,37 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketPlayOutShowPlayerSkins extends Packet {
private int entityId;
public PacketPlayOutShowPlayerSkins(int entityId) {
this.entityId = entityId;
}
public int getEntityId() {
return entityId;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeVarInt(output, entityId);
output.writeByte(16);
DataTypeIO.writeVarInt(output, 0);
int bitmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40;
output.writeByte(bitmask);
output.writeByte(0xff);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,34 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.loohp.limbo.World.BlockPosition;
public class PacketPlayOutSpawnPosition extends Packet {
private BlockPosition position;
public PacketPlayOutSpawnPosition(BlockPosition position) {
this.position = position;
}
public BlockPosition getPosition() {
return position;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
int x = position.getX();
int y = position.getY();
int z = position.getZ();
output.writeLong(((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF));
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,38 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketPlayOutUpdateViewPosition extends Packet {
private int chunkX;
private int chunkZ;
public PacketPlayOutUpdateViewPosition(int chunkX, int chunkZ) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
}
public int getChunkX() {
return chunkX;
}
public int getChunkZ() {
return chunkZ;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeVarInt(output, chunkX);
DataTypeIO.writeVarInt(output, chunkZ);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,22 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
import java.io.IOException;
public class PacketStatusInPing extends Packet {
private long payload;
public PacketStatusInPing(long payload) {
this.payload = payload;
}
public PacketStatusInPing(DataInputStream in) throws IOException {
this(in.readLong());
}
public long getPayload() {
return payload;
}
}

View File

@ -0,0 +1,15 @@
package com.loohp.limbo.Server.Packets;
import java.io.DataInputStream;
public class PacketStatusInRequest extends Packet {
public PacketStatusInRequest() {
}
public PacketStatusInRequest(DataInputStream in) {
this();
}
}

View File

@ -0,0 +1,29 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class PacketStatusOutPong extends Packet {
private long payload;
public PacketStatusOutPong(long payload) {
this.payload = payload;
}
public long getPayload() {
return payload;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getStatusOut().get(getClass()));
output.writeLong(payload);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,32 @@
package com.loohp.limbo.Server.Packets;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.loohp.limbo.Utils.DataTypeIO;
public class PacketStatusOutResponse extends Packet {
private String json;
public PacketStatusOutResponse(String json) {
this.json = json;
}
public String getJson() {
return json;
}
public byte[] getBytes() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getStatusOut().get(getClass()));
DataTypeIO.writeString(output, json, StandardCharsets.UTF_8);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,56 @@
package com.loohp.limbo.Server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ServerConnection extends Thread {
private ServerSocket serverSocket;
private List<ClientConnection> clients;
private String ip;
private int port;
private KeepAliveSender keepAliveSender;
public ServerConnection(String ip, int port) {
clients = new ArrayList<ClientConnection>();
this.ip = ip;
this.port = port;
start();
keepAliveSender = new KeepAliveSender();
}
@Override
public void run() {
try {
serverSocket = new ServerSocket(port, 50, InetAddress.getByName(ip));
System.out.println("Server listening on [" + serverSocket.getInetAddress().getHostName() + ":" + serverSocket.getLocalPort() + "]");
while (true) {
Socket connection = serverSocket.accept();
//String str = connection.getInetAddress().getHostName() + ":" + connection.getPort();
//System.out.println("[/127.0.0.1:57310] <-> InitialHandler has pinged);
ClientConnection sc = new ClientConnection(connection);
clients.add(sc);
sc.start();
}
} catch(IOException e) {
e.printStackTrace();
}
}
public KeepAliveSender getKeepAliveSender() {
return keepAliveSender;
}
public ServerSocket getServerSocket() {
return serverSocket;
}
public List<ClientConnection> getClients() {
return clients;
}
}

View File

@ -0,0 +1,26 @@
package com.loohp.limbo.Utils;
import net.querz.mca.Section;
public class ChunkDataUtils {
public static void adjustBlockStateBits(int newBits, Section section, int dataVersion) {
//increases or decreases the amount of bits used per BlockState
//based on the size of the palette.
long[] blockStates = section.getBlockStates();
long[] newBlockStates;
if (dataVersion < 2527) {
newBlockStates = newBits == blockStates.length / 64 ? blockStates : new long[newBits * 64];
} else {
int newLength = (int) Math.ceil(4096D / (64D / newBits));
newBlockStates = newBits == blockStates.length / 64 ? blockStates : new long[newLength];
}
for (int i = 0; i < 4096; i++) {
section.setPaletteIndex(i, section.getPaletteIndex(i), newBlockStates);
}
section.setBlockStates(newBlockStates);
}
}

View File

@ -0,0 +1,40 @@
package com.loohp.limbo.Utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CustomStringUtils {
public static boolean arrayContains(String compare, String[] args, boolean IgnoreCase) {
return IgnoreCase ? Arrays.asList(args).stream().anyMatch(each -> each.equalsIgnoreCase(compare)) : Arrays.asList(args).stream().anyMatch(each -> each.equals(compare));
}
public static boolean arrayContains(String compare, String[] args) {
return arrayContains(compare, args, true);
}
public static String[] splitStringToArgs(String str) {
List<String> tokens = new ArrayList<String>();
StringBuilder sb = new StringBuilder();
boolean insideQuote = false;
for (char c : str.toCharArray()) {
if (c == '"') {
insideQuote = !insideQuote;
} else if (c == ' ' && !insideQuote) {
if (sb.length() > 0) {
tokens.add(sb.toString());
}
sb.delete(0, sb.length());
} else {
sb.append(c);
}
}
tokens.add(sb.toString());
return tokens.toArray(new String[tokens.size()]);
}
}

View File

@ -0,0 +1,124 @@
package com.loohp.limbo.Utils;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import net.querz.nbt.io.NBTOutputStream;
import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.Tag;
public class DataTypeIO {
public static void writeUUID(DataOutputStream out, UUID uuid) throws IOException {
out.writeLong(uuid.getMostSignificantBits());
out.writeLong(uuid.getLeastSignificantBits());
}
public static void writeCompoundTag(DataOutputStream out, CompoundTag tag) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
new NBTOutputStream(output).writeTag(tag, Tag.DEFAULT_MAX_DEPTH);
byte[] b = buffer.toByteArray();
out.write(b);
}
public static String readString(DataInputStream in) throws IOException {
int length = readVarInt(in);
if (length == -1) {
throw new IOException("Premature end of stream.");
}
byte[] b = new byte[length];
in.readFully(b);
return new String(b);
}
public static void writeString(DataOutputStream out, String string, Charset charset) throws IOException {
byte[] bytes = string.getBytes(charset);
writeVarInt(out, bytes.length);
out.write(bytes);
}
public static int readVarInt(DataInputStream in) throws IOException {
int numRead = 0;
int result = 0;
byte read;
do {
read = in.readByte();
int value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
if (numRead > 5) {
throw new RuntimeException("VarInt is too big");
}
} while ((read & 0b10000000) != 0);
return result;
}
public static void writeVarInt(DataOutputStream out, int value) throws IOException {
do {
byte temp = (byte)(value & 0b01111111);
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
out.writeByte(temp);
} while (value != 0);
}
public static int getVarIntLength(int value) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(buffer);
do {
byte temp = (byte)(value & 0b01111111);
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
out.writeByte(temp);
} while (value != 0);
return buffer.toByteArray().length;
}
public static long readVarLong(DataInputStream in) throws IOException {
int numRead = 0;
long result = 0;
byte read;
do {
read = in.readByte();
long value = (read & 0b01111111);
result |= (value << (7 * numRead));
numRead++;
if (numRead > 10) {
throw new RuntimeException("VarLong is too big");
}
} while ((read & 0b10000000) != 0);
return result;
}
public static void writeVarLong(DataOutputStream out, long value) throws IOException {
do {
byte temp = (byte)(value & 0b01111111);
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
value >>>= 7;
if (value != 0) {
temp |= 0b10000000;
}
out.writeByte(temp);
} while (value != 0);
}
}

View File

@ -0,0 +1,44 @@
package com.loohp.limbo.Utils;
public enum GameMode {
SURVIVAL(0, "survival"),
CREATIVE(1, "creative"),
ADVENTURE(2, "adventure"),
SPECTATOR(3, "spectator");
int id;
String name;
GameMode(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public static GameMode fromId(int id) {
for (GameMode mode : GameMode.values()) {
if (mode.getId() == id) {
return mode;
}
}
return null;
}
public static GameMode fromName(String name) {
for (GameMode mode : GameMode.values()) {
if (mode.getName().equalsIgnoreCase(name)) {
return mode;
}
}
return null;
}
}

View File

@ -0,0 +1,77 @@
package com.loohp.limbo.Utils;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.loohp.limbo.Limbo;
import net.querz.nbt.tag.CompoundTag;
public class GeneratedDataUtils {
private static JSONObject globalPalette = new JSONObject();
static {
String block = "blocks.json";
File file = new File(block);
if (!file.exists()) {
try (InputStream in = Limbo.class.getClassLoader().getResourceAsStream(block)) {
Files.copy(in, file.toPath());
} catch (IOException e) {
e.printStackTrace();
}
}
try {
globalPalette = (JSONObject) new JSONParser().parse(new FileReader(file));
} catch (IOException | ParseException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
public static int getGlobalPaletteIDFromState(CompoundTag tag) {
String blockname = tag.getString("Name");
JSONObject data = (JSONObject) globalPalette.get(blockname);
Object obj = data.get("properties");
if (obj == null) {
return (int) (long) ((JSONObject) ((JSONArray) data.get("states")).get(0)).get("id");
}
//JSONObject properties = (JSONObject) obj;
if (tag.containsKey("Properties")) {
CompoundTag blockProp = tag.get("Properties", CompoundTag.class);
Map<String, String> blockstate = new HashMap<>();
for (String key : blockProp.keySet()) {
blockstate.put(key, blockProp.getString(key));
}
for (Object entry : (JSONArray) data.get("states")) {
JSONObject jsonobj = (JSONObject) entry;
if (((JSONObject) jsonobj.get("properties")).keySet().stream().allMatch(key -> blockstate.get(key).equals((String) (((JSONObject) jsonobj.get("properties")).get(key))))) {
return (int) (long) jsonobj.get("id");
}
}
}
for (Object entry : (JSONArray) data.get("states")) {
if (((JSONObject) entry).containsKey("default") && ((boolean) ((JSONObject) entry).get("default"))) {
return (int) (long) ((JSONObject) entry).get("id");
}
}
return 0;
}
}

View File

@ -0,0 +1,18 @@
package com.loohp.limbo.Utils;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import javax.imageio.ImageIO;
public class ImageUtils {
public static String imgToBase64String(final RenderedImage img, String formatName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(img, formatName, out);
return Base64.getEncoder().encodeToString(out.toByteArray());
}
}

View File

@ -0,0 +1,68 @@
package com.loohp.limbo.Utils;
public class NamespacedKey {
String namespace;
String key;
public NamespacedKey(String namespacedKey) {
int index = namespacedKey.indexOf(":");
if (index >= 0) {
this.namespace = namespacedKey.substring(0, index);
this.key = namespacedKey.substring(index + 1);
} else {
this.namespace = "minecraft";
this.key = namespacedKey;
}
}
public NamespacedKey(String namespace, String key) {
this.namespace = namespace;
this.key = key;
}
public String getNamespace() {
return namespace;
}
public String getKey() {
return key;
}
@Override
public String toString() {
return namespace + ":" + key;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NamespacedKey other = (NamespacedKey) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
if (namespace == null) {
if (other.namespace != null)
return false;
} else if (!namespace.equals(other.namespace))
return false;
return true;
}
}

View File

@ -0,0 +1,41 @@
package com.loohp.limbo.Utils;
import net.querz.nbt.tag.CompoundTag;
public class SchematicConvertionUtils {
public static CompoundTag toTileEntityTag(CompoundTag tag) {
int[] pos = tag.getIntArray("Pos");
tag.remove("Pos");
tag.remove("Id");
tag.putInt("x", pos[0]);
tag.putInt("y", pos[1]);
tag.putInt("z", pos[2]);
return tag;
}
public static CompoundTag toBlockTag(String input) {
int index = input.indexOf("[");
CompoundTag tag = new CompoundTag();
if (index < 0) {
tag.putString("Name", new NamespacedKey(input).toString());
return tag;
}
tag.putString("Name", new NamespacedKey(input.substring(0, index)).toString());
String[] states = input.substring(index + 1, input.lastIndexOf("]")).replace(" ", "").split(",");
CompoundTag properties = new CompoundTag();
for (String state : states) {
String key = state.substring(0, state.indexOf("="));
String value = state.substring(state.indexOf("=") + 1);
properties.putString(key, value);
}
tag.put("Properties", properties);
return tag;
}
}

View File

@ -0,0 +1,81 @@
package com.loohp.limbo.Utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.net.ssl.HttpsURLConnection;
public class SkinUtils {
public static class SkinResponse {
String skin;
String signature;
public SkinResponse(String skin, String signature) {
this.skin = skin;
this.signature = signature;
}
public String getSkin() {
return skin;
}
public String getSignature() {
return signature;
}
}
public static SkinResponse getSkinFromMojangServer(String username) {
try {
URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + username);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setUseCaches(false);
connection.setDefaultUseCaches(false);
connection.addRequestProperty("User-Agent", "Mozilla/5.0");
connection.addRequestProperty("Cache-Control", "no-cache, no-store, must-revalidate");
connection.addRequestProperty("Pragma", "no-cache");
if (connection.getResponseCode() == HttpsURLConnection.HTTP_OK) {
String reply = new BufferedReader(new InputStreamReader(connection.getInputStream())).readLine();
String uuid = reply.split("\"id\":\"")[1].split("\"")[0];
return getSkinFromMojangServer(UUID.fromString(uuid.replaceFirst( "([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5" )));
} else {
System.out.println("Connection could not be opened (Response code " + connection.getResponseCode() + ", " + connection.getResponseMessage() + ")");
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static SkinResponse getSkinFromMojangServer(UUID uuid) {
try {
URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid.toString() + "?unsigned=false");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setUseCaches(false);
connection.setDefaultUseCaches(false);
connection.addRequestProperty("User-Agent", "Mozilla/5.0");
connection.addRequestProperty("Cache-Control", "no-cache, no-store, must-revalidate");
connection.addRequestProperty("Pragma", "no-cache");
if (connection.getResponseCode() == HttpsURLConnection.HTTP_OK) {
String reply = String.join("", new BufferedReader(new InputStreamReader(connection.getInputStream())).lines().collect(Collectors.toList())).replace(" ", "");
String skin = reply.split("\"value\":\"")[1].split("\"")[0];
String signature = reply.split("\"signature\":\"")[1].split("\"")[0];
return new SkinResponse(skin, signature);
} else {
System.out.println("Connection could not be opened (Response code " + connection.getResponseCode() + ", " + connection.getResponseMessage() + ")");
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,31 @@
package com.loohp.limbo.World;
import com.loohp.limbo.Location.Location;
public class BlockPosition {
private int x;
private int y;
private int z;
public BlockPosition(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getZ() {
return this.z;
}
public static BlockPosition from(Location location) {
return new BlockPosition((int) Math.floor(location.getX()), (int) Math.floor(location.getY()), (int) Math.floor(location.getZ()));
}
}

View File

@ -0,0 +1,78 @@
package com.loohp.limbo.World;
import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.ListTag;
public class DimensionRegistry {
public static CompoundTag defaultTag;
static {
resetTag();
}
public static void resetTag() {
CompoundTag overworld = new CompoundTag();
overworld.putString("name", "minecraft:overworld");
overworld.putByte("natural", (byte) 1);
overworld.putFloat("ambient_light", 0.4F);
overworld.putByte("has_ceiling", (byte) 0);
overworld.putByte("has_skylight", (byte) 1);
overworld.putLong("fixed_time", 0);
overworld.putByte("shrunk", (byte) 0);
overworld.putByte("ultrawarm", (byte) 0);
overworld.putByte("has_raids", (byte) 1);
overworld.putByte("respawn_anchor_works", (byte) 0);
overworld.putByte("bed_works", (byte) 1);
overworld.putByte("piglin_safe", (byte) 0);
overworld.putInt("logical_height", 256);
overworld.putString("infiniburn", "");
CompoundTag nether = new CompoundTag();
nether.putString("name", "minecraft:nether");
nether.putByte("natural", (byte) 0);
nether.putFloat("ambient_light", 0.6F);
nether.putByte("has_ceiling", (byte) 1);
nether.putByte("has_skylight", (byte) 0);
nether.putLong("fixed_time", 0);
nether.putByte("shrunk", (byte) 1);
nether.putByte("ultrawarm", (byte) 1);
nether.putByte("has_raids", (byte) 0);
nether.putByte("respawn_anchor_works", (byte) 1);
nether.putByte("bed_works", (byte) 0);
nether.putByte("piglin_safe", (byte) 1);
nether.putInt("logical_height", 256);
nether.putString("infiniburn", "");
CompoundTag the_end = new CompoundTag();
the_end.putString("name", "minecraft:the_end");
the_end.putByte("natural", (byte) 0);
the_end.putFloat("ambient_light", 0.3F);
the_end.putByte("has_ceiling", (byte) 0);
the_end.putByte("has_skylight", (byte) 0);
the_end.putLong("fixed_time", 0);
the_end.putByte("shrunk", (byte) 0);
the_end.putByte("ultrawarm", (byte) 0);
the_end.putByte("has_raids", (byte) 0);
the_end.putByte("respawn_anchor_works", (byte) 0);
the_end.putByte("bed_works", (byte) 0);
the_end.putByte("piglin_safe", (byte) 1);
the_end.putInt("logical_height", 256);
the_end.putString("infiniburn", "");
ListTag<CompoundTag> listtag = new ListTag<CompoundTag>(CompoundTag.class);
listtag.add(overworld);
listtag.add(nether);
listtag.add(the_end);
CompoundTag dimensionTag = new CompoundTag();
dimensionTag.put("dimension", listtag);
defaultTag = dimensionTag;
}
public static CompoundTag getCodec() {
return defaultTag;
}
}

View File

@ -0,0 +1,67 @@
package com.loohp.limbo.World;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.loohp.limbo.Utils.SchematicConvertionUtils;
import net.querz.mca.Chunk;
import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.ListTag;
public class Schematic {
public static World toWorld(String name, CompoundTag nbt) {
short width = nbt.getShort("Width");
short length = nbt.getShort("Length");
short height = nbt.getShort("Height");
byte[] blocks = nbt.getByteArray("BlockData");
CompoundTag palette = nbt.getCompoundTag("Palette");
ListTag<CompoundTag> blockEntities = nbt.getListTag("BlockEntities").asTypedList(CompoundTag.class);
Map<Integer, String> mapping = new HashMap<>();
for (String key : palette.keySet()) {
mapping.put(palette.getInt(key), key);
}
World world = new World(name, width, length);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < length; z++) {
int blockIndex = x + z * width + y * width * length;
world.setBlock(x, y, z, mapping.get(blocks[blockIndex] < 0 ? blocks[blockIndex] + 256 : blocks[blockIndex]));
Chunk chunk = world.getChunkAtWorldPos(x, z);
Iterator<CompoundTag> itr = blockEntities.iterator();
while (itr.hasNext()) {
CompoundTag tag = itr.next();
int[] pos = tag.getIntArray("Pos");
if (pos[0] == x && pos[1] == y && pos[2] == z) {
ListTag<CompoundTag> newTag = chunk.getTileEntities();
newTag.add(SchematicConvertionUtils.toTileEntityTag(tag));
chunk.setTileEntities(newTag);
itr.remove();
break;
}
}
}
}
}
for (Chunk[] chunkarray : world.getChunks()) {
for (Chunk chunk : chunkarray) {
if (chunk != null) {
CompoundTag heightMap = new CompoundTag();
heightMap.putLongArray("MOTION_BLOCKING", new long[] {1371773531765642314L,1389823183635651148L,1371738278539598925L,1389823183635388492L,1353688558756731469L,1389823114781694027L,1317765589597723213L,1371773531899860042L,1389823183635651149L,1371773462911685197L,1389823183635650636L,1353688626805119565L,1371773531900123211L,1335639250618849869L,1371738278674077258L,1389823114781694028L,1353723811310638154L,1371738278674077259L,1335674228429068364L,1335674228429067338L,1335674228698027594L,1317624576693539402L,1335709481520370249L,1299610178184057417L,1335638906349064264L,1299574993811968586L,1299574924958011464L,1299610178184056904L,1299574924958011464L,1299610109330100296L,1299574924958011464L,1299574924823793736L,1299574924958011465L,1281525273222484040L,1299574924958011464L,1281525273222484040L,9548107335L});
chunk.setHeightMaps(heightMap);
chunk.setBiomes(new int[256]);
//chunk.cleanupPalettesAndBlockStates();
}
}
}
return world;
}
}

View File

@ -0,0 +1,53 @@
package com.loohp.limbo.World;
import com.loohp.limbo.Utils.SchematicConvertionUtils;
import net.querz.mca.Chunk;
import net.querz.nbt.tag.CompoundTag;
import net.querz.nbt.tag.ListTag;
public class World {
private String name;
private Chunk[][] chunks;
public World(String name, int width, int length) {
this.name = name;
this.chunks = new Chunk[(width >> 4) + 1][(length >> 4) + 1];
for (int x = 0; x < chunks.length; x++) {
for (int z = 0; z < chunks[x].length; z++) {
chunks[x][z] = Chunk.newChunk();
Chunk chunk = chunks[x][z];
chunk.cleanupPalettesAndBlockStates();
CompoundTag heightMap = new CompoundTag();
heightMap.putLongArray("MOTION_BLOCKING", new long[] {1371773531765642314L,1389823183635651148L,1371738278539598925L,1389823183635388492L,1353688558756731469L,1389823114781694027L,1317765589597723213L,1371773531899860042L,1389823183635651149L,1371773462911685197L,1389823183635650636L,1353688626805119565L,1371773531900123211L,1335639250618849869L,1371738278674077258L,1389823114781694028L,1353723811310638154L,1371738278674077259L,1335674228429068364L,1335674228429067338L,1335674228698027594L,1317624576693539402L,1335709481520370249L,1299610178184057417L,1335638906349064264L,1299574993811968586L,1299574924958011464L,1299610178184056904L,1299574924958011464L,1299610109330100296L,1299574924958011464L,1299574924823793736L,1299574924958011465L,1281525273222484040L,1299574924958011464L,1281525273222484040L,9548107335L});
chunk.setHeightMaps(heightMap);
chunk.setBiomes(new int[256]);
chunk.setTileEntities(new ListTag<CompoundTag>(CompoundTag.class));
}
}
}
public void setBlock(int x, int y, int z, String blockdata) {
Chunk chunk = this.chunks[(x >> 4)][(z >> 4)];
if (chunk == null) {
chunk = Chunk.newChunk();
this.chunks[(x >> 4)][(z >> 4)] = chunk;
}
CompoundTag block = SchematicConvertionUtils.toBlockTag(blockdata);
chunk.setBlockStateAt(x, y, z, block, false);
}
public Chunk[][] getChunks() {
return this.chunks;
}
public Chunk getChunkAtWorldPos(int x, int z) {
return this.chunks[(x >> 4)][(z >> 4)];
}
public String getName() {
return name;
}
}

32
src/server.properties Normal file
View File

@ -0,0 +1,32 @@
#Server max players, -1 for no limit
max-players=-1
#Server port
server-port=30000
#Server ip, localhost for local access only
server-ip=localhost
#World Name and the Schematic file containing map
level-name=world;spawn.schem
#Dimension, "minecraft:overworld", "minecraft:nether" or "minecraft:the_end"
level-dimension=minecraft:the_end
#Whether Flying is allowed
allow-flight=false
#GameMode, survival, creative, adventure, spectator
default-gamemode=creative
#Spawn location
world-spawn=world;20.5;17;22.5;-90;0
#Reduce debug info
reduced-debug-info=false
#Server list message in Json
motd={"text":"","extra":[{"text":"H ","color":"#E8162E"},{"text":"M ","color":"#F9D52B"},{"text":"G ","color":"#28B31F"},{"text":"! ","color":"blue"},{"text":"!","color":"dark_purple"},{"text":"\n"},{"text":"Be the first to join!","color":"yellow"}]}
#Server list version as string
version=Limbo!

171852
target/classes/blocks.json Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More