diff --git a/src/main/java/io/gitlab/jfronny/dynres/Cfg.java b/src/main/java/io/gitlab/jfronny/dynres/Cfg.java index 0a21f1e..bf04b14 100644 --- a/src/main/java/io/gitlab/jfronny/dynres/Cfg.java +++ b/src/main/java/io/gitlab/jfronny/dynres/Cfg.java @@ -6,7 +6,10 @@ import me.sargunvohra.mcmods.autoconfig1u.shadowed.blue.endless.jankson.Comment; @Config(name = "dynres") public class Cfg implements ConfigData { - @Comment("The port to use for hosting the file. Use 0 for a random one") + @Comment("The base of the link to send to client. This is equal to the address for clients to enter (requires a restart)") + public String baseLink = "http://127.0.0.1"; + + @Comment("The port to use for hosting the file. Use 0 for a random one (requires a restart)") public int port = 0; @Comment("The number of allowed concurrent downloads. Higher values prevent errors on clients but can increase load") diff --git a/src/main/java/io/gitlab/jfronny/dynres/DynRes.java b/src/main/java/io/gitlab/jfronny/dynres/DynRes.java index 5c6c687..a06f1ab 100644 --- a/src/main/java/io/gitlab/jfronny/dynres/DynRes.java +++ b/src/main/java/io/gitlab/jfronny/dynres/DynRes.java @@ -14,6 +14,8 @@ import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.gui.FabricGuiEntry; import net.minecraft.server.command.CommandManager; import net.minecraft.text.LiteralText; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.File; import java.io.FileNotFoundException; @@ -24,7 +26,7 @@ public class DynRes implements DedicatedServerModInitializer { static WebServer server; public static File resFile; public static Cfg cfg; - public static final String baseLink = "http://127.0.0.1/"; + public static Logger logger = LogManager.getLogger("DynRes"); static { AutoConfig.register(Cfg.class, JanksonConfigSerializer::new); @@ -37,15 +39,12 @@ public class DynRes implements DedicatedServerModInitializer { @Override public void onInitializeServer() { - server = new WebServer(cfg.port, cfg.maxConnections, null, new RequestHandler("resources.zip")); - server.start(); ServerLifecycleEvents.SERVER_STOPPED.register(t -> { try { server.close(); server.join(); } catch (Throwable e) { - System.err.println("Failed to stop web server"); - e.printStackTrace(); + logger.error("Failed to stop web server", e); } }); @@ -61,24 +60,37 @@ public class DynRes implements DedicatedServerModInitializer { try { context.getSource().sendFeedback(new LiteralText("Restarting DynRes"), true); cfg = AutoConfig.getConfigHolder(Cfg.class).getConfig(); - server.setPort(cfg.port); - server.setMaxConnections(cfg.maxConnections); resFile = new File(FabricLoader.getInstance().getGameDir().toFile(), cfg.resourcesFile); - server.close(); - server.start(); - context.getSource().sendFeedback(new LiteralText("DynRes restarted"), true); + restartServer(); } catch (Exception e) { - e.printStackTrace(); + logger.error("Failed to run restart command", e); context.getSource().sendError(new LiteralText(e.getMessage())); } return Command.SINGLE_SUCCESS; }))); } else { - System.err.println("DYNRES SHOULD NOT BE RUN ON INTERNAL SERVERS!"); + DynRes.logger.error("DYNRES SHOULD NOT BE RUN ON INTERNAL SERVERS!"); } }); + + restartServer(); + } + + public static void restartServer() { + int port = cfg.port; + if (server != null) { + port = server.getPort(); + server.close(); + try { + server.join(); + } catch (InterruptedException e) { + //It is most likely already dead + } + } + server = new WebServer(port, cfg.maxConnections, null, new RequestHandler("resources.zip")); + server.start(); } public static String getPort() { diff --git a/src/main/java/io/gitlab/jfronny/dynres/ServerPropertiesHandlerExt.java b/src/main/java/io/gitlab/jfronny/dynres/ServerPropertiesHandlerExt.java new file mode 100644 index 0000000..a751cb8 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/dynres/ServerPropertiesHandlerExt.java @@ -0,0 +1,5 @@ +package io.gitlab.jfronny.dynres; + +public interface ServerPropertiesHandlerExt { + void applyChanges(boolean print); +} diff --git a/src/main/java/io/gitlab/jfronny/dynres/mixin/ServerPropertiesHandlerMixin.java b/src/main/java/io/gitlab/jfronny/dynres/mixin/ServerPropertiesHandlerMixin.java index e64cf0e..ee86888 100644 --- a/src/main/java/io/gitlab/jfronny/dynres/mixin/ServerPropertiesHandlerMixin.java +++ b/src/main/java/io/gitlab/jfronny/dynres/mixin/ServerPropertiesHandlerMixin.java @@ -1,6 +1,7 @@ package io.gitlab.jfronny.dynres.mixin; import io.gitlab.jfronny.dynres.DynRes; +import io.gitlab.jfronny.dynres.ServerPropertiesHandlerExt; import net.minecraft.server.dedicated.ServerPropertiesHandler; import net.minecraft.util.registry.DynamicRegistryManager; import org.apache.commons.codec.digest.DigestUtils; @@ -12,31 +13,48 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.io.FileInputStream; import java.io.IOException; +import java.util.Objects; import java.util.Properties; @Mixin(ServerPropertiesHandler.class) -public class ServerPropertiesHandlerMixin { +public class ServerPropertiesHandlerMixin implements ServerPropertiesHandlerExt { @Shadow public String resourcePack; @Shadow public String resourcePackHash; @Shadow public String resourcePackSha1; + private String resourcePackSha1Temp; + private String resourcePackTemp; + @Inject(at = @At("RETURN"), method = "(Ljava/util/Properties;Lnet/minecraft/util/registry/DynamicRegistryManager;)V") public void init(Properties properties, DynamicRegistryManager dynamicRegistryManager, CallbackInfo info) { - resourcePack = DynRes.removePort(DynRes.simplifyElement(DynRes.baseLink)) + ":" + DynRes.getPort() + "/resources.zip"; - System.out.println("Pack link: " + resourcePack); - resourcePackSha1 = ""; - if (DynRes.cfg.hashResources) { - try { - FileInputStream fs = new FileInputStream(DynRes.resFile); - resourcePackSha1 = DigestUtils.sha1Hex(fs); - System.out.println("Set hash to " + resourcePackSha1); - fs.close(); - } catch (IOException e) { - System.err.println("Failed to get hash, continuing with empty"); - e.printStackTrace(); + applyChanges(true); + } + + @Override + public void applyChanges(boolean print) { + resourcePack = DynRes.removePort(DynRes.simplifyElement(DynRes.cfg.baseLink)) + ":" + DynRes.getPort() + "/resources.zip"; + if (print) + DynRes.logger.info("Pack link: " + resourcePack); + if (!Objects.equals(resourcePackTemp, resourcePack)) { + resourcePackTemp = resourcePack; + resourcePackSha1 = ""; + if (DynRes.cfg.hashResources) { + try { + FileInputStream fs = new FileInputStream(DynRes.resFile); + resourcePackSha1 = DigestUtils.sha1Hex(fs); + if (print) + DynRes.logger.info("Set hash to " + resourcePackSha1); + fs.close(); + } catch (IOException e) { + DynRes.logger.error("Failed to get hash, continuing with empty", e); + } } + resourcePackSha1Temp = resourcePackSha1; + } + else { + resourcePackSha1 = resourcePackSha1Temp; } resourcePackHash = resourcePackSha1; } diff --git a/src/main/java/io/gitlab/jfronny/dynres/mixin/ServerPropertiesLoaderMixin.java b/src/main/java/io/gitlab/jfronny/dynres/mixin/ServerPropertiesLoaderMixin.java new file mode 100644 index 0000000..bdb226b --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/dynres/mixin/ServerPropertiesLoaderMixin.java @@ -0,0 +1,38 @@ +package io.gitlab.jfronny.dynres.mixin; + +import io.gitlab.jfronny.dynres.ServerPropertiesHandlerExt; +import net.minecraft.server.dedicated.ServerPropertiesHandler; +import net.minecraft.server.dedicated.ServerPropertiesLoader; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.function.UnaryOperator; + +@Mixin(ServerPropertiesLoader.class) +public class ServerPropertiesLoaderMixin { + @Shadow + private ServerPropertiesHandler propertiesHandler; + @Inject(at = @At("RETURN"), method = "apply(Ljava/util/function/UnaryOperator;)Lnet/minecraft/server/dedicated/ServerPropertiesLoader;") + public void apply(UnaryOperator unaryOperator, CallbackInfoReturnable info) { + applyChanges(); + } + + @Inject(at = @At("HEAD"), method = "store()V") + public void store(CallbackInfo info) { + applyChanges(); + } + + //This caused a stackoverflow + //@Inject(at = @At("HEAD"), method = "getPropertiesHandler()Lnet/minecraft/server/dedicated/ServerPropertiesHandler;") + //public void getPropertiesHandler(CallbackInfoReturnable info) { + // applyChanges(); + //} + + public void applyChanges() { + ((ServerPropertiesHandlerExt)propertiesHandler).applyChanges(false); + } +} diff --git a/src/main/java/io/gitlab/jfronny/dynres/web/RequestHandler.java b/src/main/java/io/gitlab/jfronny/dynres/web/RequestHandler.java index fa856b6..c3d93ef 100644 --- a/src/main/java/io/gitlab/jfronny/dynres/web/RequestHandler.java +++ b/src/main/java/io/gitlab/jfronny/dynres/web/RequestHandler.java @@ -15,31 +15,35 @@ public class RequestHandler implements HttpRequestHandler { String r; @Override public HttpResponse handle(HttpRequest request) { - String method = request.getMethod().toUpperCase(); - if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) { - HttpResponse resp = new HttpResponse(HttpStatusCode.BAD_REQUEST); - resp.setData(method + " method not supported"); - return resp; - } + try { + //TODO remove debug statement + DynRes.logger.info("Got connection from " + request.getGETParamString()); + String method = request.getMethod().toUpperCase(); + if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) { + DynRes.logger.error("Invalid method: " + method); + HttpResponse resp = new HttpResponse(HttpStatusCode.BAD_REQUEST); + resp.setData(method + " method not supported"); + return resp; + } - String path = DynRes.simplifyElement(request.getPath()); + String path = DynRes.simplifyElement(request.getPath()); - if (Objects.equals(path, r)) { - HttpResponse resp = new HttpResponse(HttpStatusCode.OK); - resp.addHeader("Server", "DynRes using BlueMap"); - resp.addHeader("Cache-Control", "no-cache"); - resp.addHeader("Content-Type", "application/zip"); + if (Objects.equals(path, r)) { + HttpResponse resp = new HttpResponse(HttpStatusCode.OK); + resp.addHeader("Server", "DynRes using BlueMap"); + resp.addHeader("Cache-Control", "no-cache"); + resp.addHeader("Content-Type", "application/zip"); - try { FileInputStream fs = new FileInputStream(DynRes.resFile); resp.setData(fs); return resp; - } catch (FileNotFoundException e) { - return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR); } + DynRes.logger.error("An invalid file was requested: " + path); + return new HttpResponse(HttpStatusCode.NOT_FOUND); + } catch (Throwable e) { + DynRes.logger.error("Cough error while sending", e); + return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR); } - - return new HttpResponse(HttpStatusCode.NOT_FOUND); } public RequestHandler(String relativePath) { diff --git a/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/HttpConnection.java b/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/HttpConnection.java index 3c0be1e..1a92c37 100644 --- a/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/HttpConnection.java +++ b/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/HttpConnection.java @@ -24,6 +24,8 @@ */ package io.gitlab.jfronny.dynres.web.bluemapcore; +import io.gitlab.jfronny.dynres.DynRes; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -76,8 +78,7 @@ public class HttpConnection implements Runnable { } catch (ConnectionClosedException e){ break; } catch (IOException e) { - System.err.println("Unexpected error while processing a HttpRequest!"); - e.printStackTrace(); + DynRes.logger.error("Unexpected error while processing a HttpRequest!", e); break; } } @@ -85,8 +86,7 @@ public class HttpConnection implements Runnable { try { close(); } catch (IOException e){ - System.err.println("Error while closing HttpConnection!"); - e.printStackTrace(); + DynRes.logger.error("Error while closing HttpConnection!", e); } } diff --git a/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/WebServer.java b/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/WebServer.java index 0a86002..2190378 100644 --- a/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/WebServer.java +++ b/src/main/java/io/gitlab/jfronny/dynres/web/bluemapcore/WebServer.java @@ -24,6 +24,8 @@ */ package io.gitlab.jfronny.dynres.web.bluemapcore; +import io.gitlab.jfronny.dynres.DynRes; + import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; @@ -67,12 +69,11 @@ public class WebServer extends Thread { server = new ServerSocket(port, maxConnections, bindAdress); server.setSoTimeout(0); } catch (IOException e){ - System.err.println("Error while starting the WebServer!"); - e.printStackTrace(); + DynRes.logger.error("Error while starting the WebServer!", e); return; } - - System.out.println("WebServer started."); + + DynRes.logger.info("WebServer started."); while (!server.isClosed() && server.isBound()){ @@ -83,19 +84,18 @@ public class WebServer extends Thread { connectionThreads.execute(new HttpConnection(server, connection, handler, 10, TimeUnit.SECONDS)); } catch (RejectedExecutionException e){ connection.close(); - System.err.println("Dropped an incoming HttpConnection! (Too many connections?)"); + DynRes.logger.error("Dropped an incoming HttpConnection! (Too many connections?)"); } } catch (SocketException e){ // this mainly occurs if the socket got closed, so we ignore this error } catch (IOException e){ - System.err.println("Error while creating a new HttpConnection!"); - e.printStackTrace(); + DynRes.logger.error("Error while creating a new HttpConnection!", e); } } - System.out.println("WebServer closed."); + DynRes.logger.info("WebServer closed."); } public int getPort() { @@ -118,8 +118,7 @@ public class WebServer extends Thread { server.close(); } } catch (IOException e) { - System.err.println("Error while closing WebServer!"); - e.printStackTrace(); + DynRes.logger.error("Error while closing WebServer!", e); } } diff --git a/src/main/resources/dynres.mixins.json b/src/main/resources/dynres.mixins.json index 3479b77..7752adf 100644 --- a/src/main/resources/dynres.mixins.json +++ b/src/main/resources/dynres.mixins.json @@ -6,7 +6,8 @@ "mixins": [ ], "server": [ - "ServerPropertiesHandlerMixin" + "ServerPropertiesHandlerMixin", + "ServerPropertiesLoaderMixin" ], "injectors": { "defaultRequire": 1