forked from BLOCKFANTASY/LOOHP-Limbo
Upload
This commit is contained in:
+171852
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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!
|
||||
Reference in New Issue
Block a user