From a33721446f9fc11d8d8aba29f5f7ca7349f7b994 Mon Sep 17 00:00:00 2001 From: XiaoMoMi Date: Wed, 16 Apr 2025 20:25:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=85=E6=B5=85=E6=94=BE=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/BukkitItemManager.java | 2 +- .../core/item/AbstractItemManager.java | 2 +- .../craftengine/core/item/ItemManager.java | 2 +- .../core/pack/AbstractPackManager.java | 2 +- .../core/pack/host/ResourcePackHost.java | 4 +- .../core/pack/host/impl/SelfHost.java | 20 +++ .../pack/host/impl/SelfHostHttpServer.java | 139 ++++++++++++++++++ .../pack/host/impl/SimpleExternalHost.java | 25 ++++ .../{ => model}/LegacyOverridesModel.java | 2 +- .../core/plugin/network/NetWorkUser.java | 2 + 10 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SimpleExternalHost.java rename core/src/main/java/net/momirealms/craftengine/core/pack/{ => model}/LegacyOverridesModel.java (98%) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index 0cafce423..1eeefc72a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -20,7 +20,7 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviors; import net.momirealms.craftengine.core.item.modifier.CustomModelDataModifier; import net.momirealms.craftengine.core.item.modifier.IdModifier; import net.momirealms.craftengine.core.item.modifier.ItemModelModifier; -import net.momirealms.craftengine.core.pack.LegacyOverridesModel; +import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.LoadingSequence; import net.momirealms.craftengine.core.pack.Pack; import net.momirealms.craftengine.core.pack.ResourceLocation; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java index bcc2ffa4a..6e286da07 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.modifier.*; -import net.momirealms.craftengine.core.pack.LegacyOverridesModel; +import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration; import net.momirealms.craftengine.core.pack.model.ItemModel; import net.momirealms.craftengine.core.pack.model.generation.AbstractModelGenerator; diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java index 7b6ed87a1..2a9e464e4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemManager.java @@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; -import net.momirealms.craftengine.core.pack.LegacyOverridesModel; +import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration; import net.momirealms.craftengine.core.pack.model.ItemModel; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 08e816388..1d5b4f01b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.item.EquipmentData; import net.momirealms.craftengine.core.pack.conflict.resolution.ConditionalResolution; import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration; import net.momirealms.craftengine.core.pack.model.ItemModel; +import net.momirealms.craftengine.core.pack.model.LegacyOverridesModel; import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration; import net.momirealms.craftengine.core.pack.model.generation.ModelGenerator; import net.momirealms.craftengine.core.pack.obfuscation.ObfA; @@ -35,7 +36,6 @@ import java.nio.file.attribute.BasicFileAttributes; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; -import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHost.java index 0490d77c7..13beaf412 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHost.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/ResourcePackHost.java @@ -5,7 +5,7 @@ import java.util.concurrent.CompletableFuture; public interface ResourcePackHost { - CompletableFuture requestOneTimeUrl(UUID player); + CompletableFuture requestResourcePackDownloadLink(UUID player); - ResourcePackDownloadData getResourcePackUrl(UUID player); + ResourcePackDownloadData getResourcePackDownloadLink(UUID player); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java new file mode 100644 index 000000000..aabe7a017 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHost.java @@ -0,0 +1,20 @@ +package net.momirealms.craftengine.core.pack.host.impl; + +import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; +import net.momirealms.craftengine.core.pack.host.ResourcePackHost; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class SelfHost implements ResourcePackHost { + + @Override + public CompletableFuture requestResourcePackDownloadLink(UUID player) { + return null; + } + + @Override + public ResourcePackDownloadData getResourcePackDownloadLink(UUID player) { + return null; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java new file mode 100644 index 000000000..4722a0d2a --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SelfHostHttpServer.java @@ -0,0 +1,139 @@ +package net.momirealms.craftengine.core.pack.host.impl; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.config.Config; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class SelfHostHttpServer { + private static SelfHostHttpServer instance; + private HttpServer server; + private String ip; + private int port; + private Path resourcePackPath; + private final ConcurrentHashMap ipAccessMap = new ConcurrentHashMap<>(); + private int rateLimit = 1; + private long rateLimitInterval = 1000; + + public String url() { + return Config.hostProtocol() + "://" + ip + ":" + port + "/"; + } + + public void enable(String ip, int port, Path resourcePackPath) { + if (isAlive() && ip.equals(this.ip) && port == this.port && resourcePackPath.equals(this.resourcePackPath)) { + return; + } + if (server != null) { + disable(); + } + this.ip = ip; + this.port = port; + this.resourcePackPath = resourcePackPath; + + try { + server = HttpServer.create(new InetSocketAddress("::", port), 0); + server.createContext("/", new ResourcePackHandler()); + server.setExecutor(Executors.newCachedThreadPool()); + server.start(); + CraftEngine.instance().logger().info("HTTP resource pack server running on " + ip + ":" + port); + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to start HTTP server", e); + } + } + + public void disable() { + if (server != null) { + server.stop(0); + server = null; + } + } + + public boolean isAlive() { + return server != null; + } + + public static SelfHostHttpServer instance() { + if (instance == null) { + instance = new SelfHostHttpServer(); + } + return instance; + } + + public void setRateLimit(int rateLimit, long rateLimitInterval, TimeUnit timeUnit) { + this.rateLimit = rateLimit; + this.rateLimitInterval = timeUnit.toMillis(rateLimitInterval); + } + + private class ResourcePackHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + if (Config.denyNonMinecraftRequest()) { + String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); + if (userAgent == null || !userAgent.startsWith("Minecraft Java/")) { + CraftEngine.instance().debug(() -> "Blocked non-Minecraft Java client. User-Agent: " + userAgent); + sendError(exchange, 403); + return; + } + } + + String clientIp = exchange.getRemoteAddress().getAddress().getHostAddress(); + + IpAccessRecord record = ipAccessMap.compute(clientIp, (k, v) -> { + long currentTime = System.currentTimeMillis(); + if (v == null || currentTime - v.lastAccessTime > rateLimitInterval) { + return new IpAccessRecord(currentTime, 1); + } else { + v.accessCount++; + return v; + } + }); + + if (record.accessCount > rateLimit) { + CraftEngine.instance().debug(() -> "Rate limit exceeded for IP: " + clientIp); + sendError(exchange, 429); + return; + } + + if (!Files.exists(resourcePackPath)) { + CraftEngine.instance().logger().warn("ResourcePack not found: " + resourcePackPath); + sendError(exchange, 404); + return; + } + + exchange.getResponseHeaders().set("Content-Type", "application/zip"); + exchange.getResponseHeaders().set("Content-Length", String.valueOf(Files.size(resourcePackPath))); + exchange.sendResponseHeaders(200, Files.size(resourcePackPath)); + + try (OutputStream os = exchange.getResponseBody()) { + Files.copy(resourcePackPath, os); + } catch (IOException e) { + CraftEngine.instance().logger().warn("Failed to send pack", e); + } + } + + private void sendError(HttpExchange exchange, int code) throws IOException { + exchange.sendResponseHeaders(code, 0); + exchange.getResponseBody().close(); + } + } + + private static class IpAccessRecord { + long lastAccessTime; + int accessCount; + + IpAccessRecord(long lastAccessTime, int accessCount) { + this.lastAccessTime = lastAccessTime; + this.accessCount = accessCount; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SimpleExternalHost.java b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SimpleExternalHost.java new file mode 100644 index 000000000..a021836bd --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/host/impl/SimpleExternalHost.java @@ -0,0 +1,25 @@ +package net.momirealms.craftengine.core.pack.host.impl; + +import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; +import net.momirealms.craftengine.core.pack.host.ResourcePackHost; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public class SimpleExternalHost implements ResourcePackHost { + private final ResourcePackDownloadData downloadData; + + public SimpleExternalHost(ResourcePackDownloadData downloadData) { + this.downloadData = downloadData; + } + + @Override + public CompletableFuture requestResourcePackDownloadLink(UUID player) { + return CompletableFuture.completedFuture(this.downloadData); + } + + @Override + public ResourcePackDownloadData getResourcePackDownloadLink(UUID player) { + return this.downloadData; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/LegacyOverridesModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/LegacyOverridesModel.java similarity index 98% rename from core/src/main/java/net/momirealms/craftengine/core/pack/LegacyOverridesModel.java rename to core/src/main/java/net/momirealms/craftengine/core/pack/model/LegacyOverridesModel.java index 4d39a7d3e..07d6d1e7f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/LegacyOverridesModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/LegacyOverridesModel.java @@ -1,4 +1,4 @@ -package net.momirealms.craftengine.core.pack; +package net.momirealms.craftengine.core.pack.model; import com.google.gson.JsonObject; import org.jetbrains.annotations.NotNull; diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index e79b0e842..2bf8b725c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -49,4 +49,6 @@ public interface NetWorkUser { boolean clientModEnabled(); void setClientModState(boolean enable); + + }