From f9f59d5377c5419be69a5e1f937b6d3e9b081979 Mon Sep 17 00:00:00 2001 From: TheClashFruit Date: Fri, 30 Aug 2024 18:24:55 +0200 Subject: [PATCH] feat: add api stuff --- build.gradle | 8 +- src/main/java/cc/crss/mod/CRSSMod.java | 14 +- src/main/java/cc/crss/mod/api/ApiServer.java | 127 ++++++++++++++++++ src/main/java/cc/crss/mod/api/Model.java | 13 ++ .../java/cc/crss/mod/api/data/ServerInfo.java | 39 ++++++ src/main/java/cc/crss/mod/util/JsonUtil.java | 11 ++ src/main/resources/crss.mixins.json | 2 +- 7 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 src/main/java/cc/crss/mod/api/ApiServer.java create mode 100644 src/main/java/cc/crss/mod/api/Model.java create mode 100644 src/main/java/cc/crss/mod/api/data/ServerInfo.java create mode 100644 src/main/java/cc/crss/mod/util/JsonUtil.java diff --git a/build.gradle b/build.gradle index 1889fa7..79a5edf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { id 'fabric-loom' version '1.7-SNAPSHOT' id 'maven-publish' + id 'com.github.johnrengelman.shadow' version '8.1.1' } version = project.mod_version @@ -26,7 +27,8 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - + + implementation 'com.google.code.gson:gson:2.10.1' } processResources { @@ -51,6 +53,10 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } +shadowJar { + relocate('com.google.gson', 'cc.crss.mod.deps.gson') +} + jar { from("LICENSE") { rename { "${it}_${project.base.archivesName.get()}"} diff --git a/src/main/java/cc/crss/mod/CRSSMod.java b/src/main/java/cc/crss/mod/CRSSMod.java index dd06906..fdfa709 100644 --- a/src/main/java/cc/crss/mod/CRSSMod.java +++ b/src/main/java/cc/crss/mod/CRSSMod.java @@ -1,16 +1,26 @@ package cc.crss.mod; +import cc.crss.mod.api.ApiServer; import net.fabricmc.api.DedicatedServerModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.minecraft.server.MinecraftServer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import cc.crss.mod.util.CommandRegister; public class CRSSMod implements DedicatedServerModInitializer { - public static final Logger LOGGER = LogManager.getLogger("crss"); + @Override public void onInitializeServer() { - LOGGER.info("CRSS initialising."); + + ServerLifecycleEvents.SERVER_STARTED.register(ApiServer::createInstance); + ServerLifecycleEvents.SERVER_STOPPING.register(server -> { + ApiServer srv = ApiServer.getInstance(); + + srv.stop(); + }); + CommandRegister.registerCommands(); } } \ No newline at end of file diff --git a/src/main/java/cc/crss/mod/api/ApiServer.java b/src/main/java/cc/crss/mod/api/ApiServer.java new file mode 100644 index 0000000..e8310af --- /dev/null +++ b/src/main/java/cc/crss/mod/api/ApiServer.java @@ -0,0 +1,127 @@ +package cc.crss.mod.api; + +import cc.crss.mod.api.data.ServerInfo; +import net.minecraft.MinecraftVersion; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +import net.minecraft.world.dimension.DimensionType; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; + +import static cc.crss.mod.CRSSMod.LOGGER; + +public class ApiServer { + private static ApiServer instance; + + public static MinecraftServer server; + + private Thread thread; + + public ApiServer(MinecraftServer server) { + if (instance != null) + throw new RuntimeException("API Server already started."); + + ApiServer.server = server; + + start(); + } + + public static ApiServer getInstance() { + return instance; + } + + public static void createInstance(MinecraftServer server) { + instance = new ApiServer(server); + } + + private void start() { + this.thread = new Thread( + new ServerRunnable(25580) + ); + + this.thread.start(); + } + + public void stop() { + this.thread.interrupt(); + + instance = null; + } + + static class ServerRunnable implements Runnable { + private int port; + + public ServerRunnable(int port) { + this.port = port; + } + + @Override + public void run() { + try (ServerSocket serverSocket = new ServerSocket(port)) { + System.out.println("Server is listening on port " + port); + + while (true) { + Socket clientSocket = serverSocket.accept(); + + clientSocket.setTcpNoDelay(true); + + DataInputStream dataInputStream = new DataInputStream( + clientSocket.getInputStream() + ); + + DataOutputStream dataOutputStream = new DataOutputStream( + clientSocket.getOutputStream() + ); + + byte packetId = dataInputStream.readByte(); + + switch (packetId) { + case 0x00: + MinecraftServer server = ApiServer.server; + + ArrayList worlds = new ArrayList<>(); + + server.getWorlds().forEach(world -> { + worlds.add(world.getDimension().getType().toString()); + }); + + String jsonData = new ServerInfo( + server.getVersion(), + server.getCurrentPlayerCount(), + worlds.toArray(new String[0]) + ).toJson(); + + int length = jsonData.length(); + + byte[] responseBytes = new byte[1 + 4 + length]; + + responseBytes[0] = 0x00; + + responseBytes[1] = (byte) ((length >> 24) & 0xFF); + responseBytes[2] = (byte) ((length >> 16) & 0xFF); + responseBytes[3] = (byte) ((length >> 8) & 0xFF); + responseBytes[4] = (byte) (length & 0xFF); + + System.arraycopy(jsonData.getBytes(), 0, responseBytes, 5, length); + + dataOutputStream.write(responseBytes); + dataOutputStream.flush(); + + break; + default: + LOGGER.warn("Received unknown packet: {}", packetId); + + break; + } + } + } catch (IOException e) { + LOGGER.error("Error starting API server", e); + } + } + } +} diff --git a/src/main/java/cc/crss/mod/api/Model.java b/src/main/java/cc/crss/mod/api/Model.java new file mode 100644 index 0000000..6f09e9e --- /dev/null +++ b/src/main/java/cc/crss/mod/api/Model.java @@ -0,0 +1,13 @@ +package cc.crss.mod.api; + +import cc.crss.mod.util.JsonUtil; + +public class Model { + public String toJson() { + return JsonUtil.gson.toJson(this); + } + + public static Object fromJson(String json, Class clazz) { + return JsonUtil.gson.fromJson(json, clazz); + } +} diff --git a/src/main/java/cc/crss/mod/api/data/ServerInfo.java b/src/main/java/cc/crss/mod/api/data/ServerInfo.java new file mode 100644 index 0000000..162048e --- /dev/null +++ b/src/main/java/cc/crss/mod/api/data/ServerInfo.java @@ -0,0 +1,39 @@ +package cc.crss.mod.api.data; + +import cc.crss.mod.api.Model; + +public class ServerInfo extends Model { + private String version; + private Integer online; + private String[] worlds; + + public ServerInfo(String version, Integer online, String[] worlds) { + this.version = version; + this.online = online; + this.worlds = worlds; + } + + public String getVersion() { + return version; + } + + public Integer getOnline() { + return online; + } + + public String[] getWorlds() { + return worlds; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setOnline(Integer online) { + this.online = online; + } + + public void setWorlds(String[] worlds) { + this.worlds = worlds; + } +} diff --git a/src/main/java/cc/crss/mod/util/JsonUtil.java b/src/main/java/cc/crss/mod/util/JsonUtil.java new file mode 100644 index 0000000..0b24c99 --- /dev/null +++ b/src/main/java/cc/crss/mod/util/JsonUtil.java @@ -0,0 +1,11 @@ +package cc.crss.mod.util; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class JsonUtil { + public static Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); +} diff --git a/src/main/resources/crss.mixins.json b/src/main/resources/crss.mixins.json index 526ce76..ac45d22 100644 --- a/src/main/resources/crss.mixins.json +++ b/src/main/resources/crss.mixins.json @@ -8,5 +8,5 @@ ], "injectors": { "defaultRequire": 1 - } + } } \ No newline at end of file