package ceratic.projectvollidioten.http; import ceratic.projectvollidioten.config.GithubResourcePackConfig; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import net.minecraft.server.MinecraftServer; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; public class ResourcePackHttpServer { private final HttpServer server; private final Path resourcePacksDir; private final String expectedFileName; private final int port; public ResourcePackHttpServer(MinecraftServer mcServer, String expectedFileName, int port) throws IOException { this.resourcePacksDir = mcServer.getRunDirectory().resolve("server-resource-packs"); this.expectedFileName = expectedFileName; this.port = port; Files.createDirectories(resourcePacksDir); this.server = HttpServer.create(new InetSocketAddress("0.0.0.0", port), 0); this.server.createContext("/resourcepacks", new ResourcePackHandler()); this.server.setExecutor(null); // Uses default executor } public void start() { server.start(); System.out.println("[GithubRP] 🌐 HTTP server started on http://0.0.0.0:" + port + "/resourcepacks/"); } public void stop() { server.stop(1); System.out.println("[GithubRP] 🌐 HTTP server stopped."); } // Returns URL clients should use — e.g. http://192.168.1.100:25585/resourcepacks/my-pack.zip public String getPackUrl() { // Try to get LAN IP (optional: replace with public IP if port-forwarded) String host = "127.0.0.1"; // fallback try { java.net.InetAddress addr = java.net.InetAddress.getLocalHost(); if (!addr.isLoopbackAddress() && addr.isSiteLocalAddress()) { host = addr.getHostAddress(); } } catch (Exception ignored) {} return String.format("http://%s:%d/resourcepacks/%s", host, port, expectedFileName); } private class ResourcePackHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { String path = exchange.getRequestURI().getPath(); String targetFile = path.substring("/resourcepacks/".length()); // Security: only allow configured pack filename if (!targetFile.equals(expectedFileName)) { sendResponse(exchange, 403, "Forbidden: unknown resource pack"); return; } Path packFile = resourcePacksDir.resolve(targetFile); if (!Files.exists(packFile) || !Files.isRegularFile(packFile)) { sendResponse(exchange, 404, "Resource pack not found"); return; } // Set headers exchange.getResponseHeaders().set("Content-Type", "application/zip"); exchange.getResponseHeaders().set("Content-Length", String.valueOf(Files.size(packFile))); exchange.getResponseHeaders().set("Cache-Control", "public, max-age=3600"); exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*"); // Send file exchange.sendResponseHeaders(200, 0); // chunked try (OutputStream os = exchange.getResponseBody(); var fis = Files.newInputStream(packFile)) { fis.transferTo(os); } catch (IOException e) { System.err.println("[GithubRP] Client disconnected during pack download."); } } private void sendResponse(HttpExchange exchange, int code, String msg) throws IOException { byte[] data = msg.getBytes(java.nio.charset.StandardCharsets.UTF_8); exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8"); exchange.sendResponseHeaders(code, data.length); exchange.getResponseBody().write(data); exchange.close(); } } }