diff --git a/BlueMapCommon/BlueMapVue b/BlueMapCommon/BlueMapVue index e2af667e..f68547b7 160000 --- a/BlueMapCommon/BlueMapVue +++ b/BlueMapCommon/BlueMapVue @@ -1 +1 @@ -Subproject commit e2af667e5e81ffdb3fec4e7278a87a5d746926fc +Subproject commit f68547b7ff4a63f77ba26e09a4f2b561358e7f77 diff --git a/BlueMapCommon/gradle/wrapper/gradle-wrapper.properties b/BlueMapCommon/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/BlueMapCommon/gradle/wrapper/gradle-wrapper.properties +++ b/BlueMapCommon/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java index 106ce6ea..32bdc4d1 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java @@ -52,7 +52,7 @@ public WebFilesManager(Path webRoot) { } public Path getSettingsFile() { - return webRoot.resolve("data").resolve("settings.json"); + return webRoot.resolve("settings.json"); } public void loadSettings() throws IOException { diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java index 9d755fca..f774e792 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java @@ -285,7 +285,7 @@ private synchronized Map loadStorageConfigs(Path defaultW Files.writeString( storageConfigFolder.resolve("file.conf"), configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/storages/file.conf") - .setVariable("root", formatPath(defaultWebroot.resolve("data"))) + .setVariable("root", formatPath(defaultWebroot.resolve("maps"))) .build(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING ); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/PluginConfig.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/PluginConfig.java index 0a70e49b..7ba5639c 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/PluginConfig.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/PluginConfig.java @@ -13,12 +13,13 @@ public class PluginConfig { private boolean livePlayerMarkers = true; - private boolean skinDownload = true; - private List hiddenGameModes = new ArrayList<>(); private boolean hideVanished = true; private boolean hideInvisible = true; - private boolean hideSneaking = true; + private boolean hideSneaking = false; + private boolean hideDifferentWorld = false; + + private boolean skinDownload = true; private int playerRenderLimit = -1; @@ -28,10 +29,6 @@ public boolean isLivePlayerMarkers() { return livePlayerMarkers; } - public boolean isSkinDownload() { - return skinDownload; - } - public List getHiddenGameModes() { return hiddenGameModes; } @@ -48,6 +45,14 @@ public boolean isHideSneaking() { return hideSneaking; } + public boolean isHideDifferentWorld() { + return hideDifferentWorld; + } + + public boolean isSkinDownload() { + return skinDownload; + } + public int getPlayerRenderLimit() { return playerRenderLimit; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/FileConfig.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/FileConfig.java index 9e07b30b..63159bb7 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/FileConfig.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/storage/FileConfig.java @@ -36,7 +36,7 @@ @ConfigSerializable public class FileConfig extends StorageConfig implements FileStorageSettings { - private Path root = Path.of("bluemap", "web", "data"); + private Path root = Path.of("bluemap", "web", "maps"); private Compression compression = Compression.GZIP; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java deleted file mode 100644 index 0e202643..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.common.live; - -import com.google.gson.stream.JsonWriter; -import de.bluecolored.bluemap.common.config.PluginConfig; -import de.bluecolored.bluemap.common.serverinterface.Player; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; -import de.bluecolored.bluemap.common.webserver.HttpRequest; -import de.bluecolored.bluemap.common.webserver.HttpRequestHandler; -import de.bluecolored.bluemap.common.webserver.HttpResponse; -import de.bluecolored.bluemap.common.webserver.HttpStatusCode; -import de.bluecolored.bluemap.core.BlueMap; - -import java.io.IOException; -import java.io.StringWriter; -import java.util.HashMap; -import java.util.Map; - -public class LiveAPIRequestHandler implements HttpRequestHandler { - - private HttpRequestHandler notFoundHandler; - private Map liveAPIRequests; - private ServerInterface server; - - private PluginConfig config; - - public LiveAPIRequestHandler(ServerInterface server, PluginConfig config, HttpRequestHandler notFoundHandler) { - this.server = server; - this.notFoundHandler = notFoundHandler; - - this.liveAPIRequests = new HashMap<>(); - - this.liveAPIRequests.put("live", this::handleLivePingRequest); - this.liveAPIRequests.put("live/players", this::handlePlayersRequest); - - this.config = config; - } - - @Override - public HttpResponse handle(HttpRequest request) { - if (!config.isLivePlayerMarkers()) return this.notFoundHandler.handle(request); - - String path = request.getPath(); - - //normalize path - if (path.startsWith("/")) path = path.substring(1); - if (path.endsWith("/")) path = path.substring(0, path.length() - 1); - - HttpRequestHandler handler = liveAPIRequests.get(path); - if (handler != null) { - HttpResponse response = handler.handle(request); - response.addHeader("Server", "BlueMap v" + BlueMap.VERSION); - response.addHeader("Cache-Control", "no-cache"); - response.addHeader("Content-Type", "application/json"); - - return response; - } - - return this.notFoundHandler.handle(request); - } - - public HttpResponse handleLivePingRequest(HttpRequest request) { - HttpResponse response = new HttpResponse(HttpStatusCode.OK); - response.setData("{\"status\":\"OK\"}"); - return response; - } - - public HttpResponse handlePlayersRequest(HttpRequest request) { - if (!request.getMethod().equalsIgnoreCase("GET")) return new HttpResponse(HttpStatusCode.BAD_REQUEST); - - try ( - StringWriter data = new StringWriter(); - JsonWriter json = new JsonWriter(data); - ){ - - json.beginObject(); - json.name("players").beginArray(); - for (Player player : server.getOnlinePlayers()) { - if (!player.isOnline()) continue; - - if (config.isHideInvisible() && player.isInvisible()) continue; - if (config.isHideVanished() && player.isVanished()) continue; - if (config.isHideSneaking() && player.isSneaking()) continue; - if (config.getHiddenGameModes().contains(player.getGamemode().getId())) continue; - - json.beginObject(); - json.name("uuid").value(player.getUuid().toString()); - json.name("name").value(player.getName().toPlainString()); - json.name("world").value(player.getWorld().toString()); - json.name("position").beginObject(); - json.name("x").value(player.getPosition().getX()); - json.name("y").value(player.getPosition().getY()); - json.name("z").value(player.getPosition().getZ()); - json.endObject(); - json.endObject(); - } - - json.endArray(); - json.endObject(); - - HttpResponse response = new HttpResponse(HttpStatusCode.OK); - response.setData(data.toString()); - - return response; - - } catch (IOException ex) { - return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR); - } - } - -} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LivePlayersDataSupplier.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LivePlayersDataSupplier.java new file mode 100644 index 00000000..0944b69e --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LivePlayersDataSupplier.java @@ -0,0 +1,77 @@ +package de.bluecolored.bluemap.common.live; + +import com.google.gson.stream.JsonWriter; +import de.bluecolored.bluemap.common.config.PluginConfig; +import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.core.logger.Logger; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.function.Supplier; + +public class LivePlayersDataSupplier implements Supplier { + + private final ServerInterface server; + private final PluginConfig config; + private final String worldId; + + public LivePlayersDataSupplier(ServerInterface server, PluginConfig config, String worldId) { + this.server = server; + this.config = config; + this.worldId = worldId; + } + + @Override + public String get() { + try (StringWriter jsonString = new StringWriter(); + JsonWriter json = new JsonWriter(jsonString)) { + + json.beginObject(); + json.name("players").beginArray(); + + if (config.isLivePlayerMarkers()) { + for (Player player : this.server.getOnlinePlayers()) { + if (!player.isOnline()) continue; + + boolean isCorrectWorld = player.getWorld().equals(this.worldId); + + if (config.isHideInvisible() && player.isInvisible()) continue; + if (config.isHideVanished() && player.isVanished()) continue; + if (config.isHideSneaking() && player.isSneaking()) continue; + if (config.getHiddenGameModes().contains(player.getGamemode().getId())) continue; + if (config.isHideDifferentWorld() && !isCorrectWorld) continue; + + json.beginObject(); + json.name("uuid").value(player.getUuid().toString()); + json.name("name").value(player.getName().toPlainString()); + json.name("foreign").value(!isCorrectWorld); + + json.name("position").beginObject(); + json.name("x").value(player.getPosition().getX()); + json.name("y").value(player.getPosition().getY()); + json.name("z").value(player.getPosition().getZ()); + json.endObject(); + + json.name("rotation").beginObject(); + json.name("pitch").value(player.getRotation().getX()); + json.name("yaw").value(player.getRotation().getY()); + json.name("roll").value(player.getRotation().getZ()); + json.endObject(); + + json.endObject(); + } + } + + json.endArray(); + json.endObject(); + + json.flush(); + return jsonString.toString(); + } catch (IOException ex) { + Logger.global.logError("Failed to write live/players json!", ex); + return "BlueMap - Exception handling this request"; + } + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java index e94fc0c7..d33ed77d 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java @@ -29,17 +29,15 @@ import de.bluecolored.bluemap.common.InterruptableReentrantLock; import de.bluecolored.bluemap.common.MissingResourcesException; import de.bluecolored.bluemap.common.config.*; -import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler; -import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater; import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask; import de.bluecolored.bluemap.common.rendermanager.RenderManager; +import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.web.FileRequestHandler; -import de.bluecolored.bluemap.common.web.MapStorageRequestHandler; -import de.bluecolored.bluemap.common.webserver.HttpRequestHandler; +import de.bluecolored.bluemap.common.web.MapRequestHandler; +import de.bluecolored.bluemap.common.web.RoutingRequestHandler; import de.bluecolored.bluemap.common.webserver.WebServer; -import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.debug.DebugDump; import de.bluecolored.bluemap.core.debug.StateDumper; import de.bluecolored.bluemap.core.logger.Logger; @@ -55,6 +53,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; @DebugDump public class Plugin implements ServerEventListener { @@ -152,17 +151,19 @@ public void load() throws IOException { if (webserverConfig.isEnabled()) { Path webroot = webserverConfig.getWebroot(); Files.createDirectories(webroot); - HttpRequestHandler requestHandler = new FileRequestHandler(webroot, - "BlueMap " + BlueMap.VERSION + " " + BlueMap.GIT_HASH + " " + BlueMap.GIT_CLEAN); - //use map-storage to provide map-tiles - requestHandler = new MapStorageRequestHandler( - id -> maps.get(id).getStorage(), - requestHandler); + RoutingRequestHandler routingRequestHandler = new RoutingRequestHandler(); - //inject live api if enabled - if (pluginConfig.isLivePlayerMarkers()) { - requestHandler = new LiveAPIRequestHandler(serverInterface, pluginConfig, requestHandler); + // default route + routingRequestHandler.register(".*", new FileRequestHandler(webroot)); + + // map route + for (BmMap map : maps.values()) { + routingRequestHandler.register( + "maps/" + Pattern.quote(map.getId()) + "/(.*)", + "$1", + new MapRequestHandler(map, serverInterface, pluginConfig) + ); } try { @@ -170,7 +171,7 @@ public void load() throws IOException { webserverConfig.resolveIp(), webserverConfig.getPort(), webserverConfig.getMaxConnectionCount(), - requestHandler, + routingRequestHandler, false ); } catch (UnknownHostException ex) { diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java index 607a8a73..94417b6e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java @@ -39,6 +39,11 @@ public interface Player { Vector3d getPosition(); + /** + * x -> pitch, y -> yaw, z -> roll + */ + Vector3d getRotation(); + boolean isOnline(); /** diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerInterface.java index 0a815689..f207f15d 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerInterface.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerInterface.java @@ -25,6 +25,7 @@ package de.bluecolored.bluemap.common.serverinterface; import de.bluecolored.bluemap.core.MinecraftVersion; +import de.bluecolored.bluemap.core.debug.DebugDump; import de.bluecolored.bluemap.core.util.Tristate; import java.nio.file.Path; @@ -34,6 +35,7 @@ public interface ServerInterface { + @DebugDump MinecraftVersion getMinecraftVersion(); /** @@ -53,21 +55,25 @@ default Optional getWorld(Path worldFolder) { .findAny(); } + @DebugDump Collection getLoadedWorlds(); /** * Returns the Folder containing the configurations for the plugin */ + @DebugDump Path getConfigFolder(); /** * Returns the folder that contains the mod-jars */ + @DebugDump Optional getModsFolder(); /** * Gives the possibility to override the metrics-setting in the config */ + @DebugDump default Tristate isMetricsEnabled() { return Tristate.UNDEFINED; } @@ -75,6 +81,7 @@ default Tristate isMetricsEnabled() { /** * Returns a collection of the states of players that are currently online */ + @DebugDump Collection getOnlinePlayers(); /** diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java index e1358820..f4f8fc8c 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java @@ -1,21 +1,27 @@ package de.bluecolored.bluemap.common.serverinterface; +import de.bluecolored.bluemap.core.debug.DebugDump; + import java.io.IOException; import java.nio.file.Path; import java.util.Optional; public interface ServerWorld { + @DebugDump default Optional getId() { return Optional.empty(); } + @DebugDump default Optional getName() { return Optional.empty(); } + @DebugDump Path getSaveFolder(); + @DebugDump default Dimension getDimension() { Path saveFolder = getSaveFolder(); String lastName = saveFolder.getFileName().toString(); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java new file mode 100644 index 00000000..2eed01af --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java @@ -0,0 +1,33 @@ +package de.bluecolored.bluemap.common.web; + +import de.bluecolored.bluemap.common.webserver.HttpRequest; +import de.bluecolored.bluemap.common.webserver.HttpRequestHandler; +import de.bluecolored.bluemap.common.webserver.HttpResponse; +import de.bluecolored.bluemap.common.webserver.HttpStatusCode; +import de.bluecolored.bluemap.core.BlueMap; + +public class BlueMapResponseModifier implements HttpRequestHandler { + + private final HttpRequestHandler delegate; + private final String serverName; + + public BlueMapResponseModifier(HttpRequestHandler delegate) { + this.delegate = delegate; + this.serverName = "BlueMap " + BlueMap.VERSION + " " + BlueMap.GIT_HASH + " " + BlueMap.GIT_CLEAN; + } + + @Override + public HttpResponse handle(HttpRequest request) { + HttpResponse response = delegate.handle(request); + + HttpStatusCode status = response.getStatusCode(); + if (status.getCode() >= 400 && !response.hasData()){ + response.setData(status.getCode() + " - " + status.getMessage() + "\n" + this.serverName); + } + + response.addHeader("Server", this.serverName); + + return response; + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/CachedRateLimitDataSupplier.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/CachedRateLimitDataSupplier.java new file mode 100644 index 00000000..f410ab6e --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/CachedRateLimitDataSupplier.java @@ -0,0 +1,31 @@ +package de.bluecolored.bluemap.common.web; + +import java.util.function.Supplier; + +public class CachedRateLimitDataSupplier implements Supplier { + + private final Supplier delegate; + private final long rateLimitMillis; + + private long updateTime = -1; + private String data = null; + + public CachedRateLimitDataSupplier(Supplier delegate, long rateLimitMillis) { + this.delegate = delegate; + this.rateLimitMillis = rateLimitMillis; + } + + @Override + public String get() { + update(); + return data; + } + + protected void update() { + long now = System.currentTimeMillis(); + if (data != null && now < updateTime + this.rateLimitMillis) return; + this.updateTime = now; + this.data = delegate.get(); + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java index d3350baa..a2d36fa2 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java @@ -41,14 +41,10 @@ public class FileRequestHandler implements HttpRequestHandler { private final Path webRoot; - private final String serverName; - private final File emptyTileFile; - public FileRequestHandler(Path webRoot, String serverName) { + public FileRequestHandler(Path webRoot) { this.webRoot = webRoot.normalize(); - this.serverName = serverName; - this.emptyTileFile = webRoot.resolve("assets").resolve("emptyTile.json").toFile(); } @@ -60,12 +56,7 @@ public HttpResponse handle(HttpRequest request) { ) return new HttpResponse(HttpStatusCode.NOT_IMPLEMENTED); HttpResponse response = generateResponse(request); - response.addHeader("Server", this.serverName); - HttpStatusCode status = response.getStatusCode(); - if (status.getCode() >= 400){ - response.setData(status.getCode() + " - " + status.getMessage() + "\n" + this.serverName); - } return response; } @@ -104,7 +95,7 @@ private HttpResponse generateResponse(HttpRequest request) { } // send empty tile-file if tile not exists - if (!file.exists() && file.toPath().startsWith(webRoot.resolve("data"))){ + if (!file.exists() && file.toPath().startsWith(webRoot.resolve("maps"))){ file = emptyTileFile; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/JsonDataRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/JsonDataRequestHandler.java new file mode 100644 index 00000000..c25e4cca --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/JsonDataRequestHandler.java @@ -0,0 +1,29 @@ +package de.bluecolored.bluemap.common.web; + +import de.bluecolored.bluemap.common.webserver.HttpRequest; +import de.bluecolored.bluemap.common.webserver.HttpRequestHandler; +import de.bluecolored.bluemap.common.webserver.HttpResponse; +import de.bluecolored.bluemap.common.webserver.HttpStatusCode; +import de.bluecolored.bluemap.core.BlueMap; + +import java.util.function.Supplier; + +public class JsonDataRequestHandler implements HttpRequestHandler { + + private final Supplier dataSupplier; + + public JsonDataRequestHandler(Supplier dataSupplier) { + this.dataSupplier = dataSupplier; + } + + @Override + public HttpResponse handle(HttpRequest request) { + HttpResponse response = new HttpResponse(HttpStatusCode.OK); + response.addHeader("Server", "BlueMap v" + BlueMap.VERSION); + response.addHeader("Cache-Control", "no-cache"); + response.addHeader("Content-Type", "application/json"); + response.setData(dataSupplier.get()); + return response; + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapRequestHandler.java new file mode 100644 index 00000000..e989a00d --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapRequestHandler.java @@ -0,0 +1,25 @@ +package de.bluecolored.bluemap.common.web; + +import de.bluecolored.bluemap.common.config.PluginConfig; +import de.bluecolored.bluemap.common.live.LivePlayersDataSupplier; +import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.core.map.BmMap; +import de.bluecolored.bluemap.core.storage.Storage; + +public class MapRequestHandler extends RoutingRequestHandler { + + public MapRequestHandler(BmMap map, ServerInterface serverInterface, PluginConfig pluginConfig) { + this(map.getId(), map.getWorldId(), map.getStorage(), serverInterface, pluginConfig); + } + + public MapRequestHandler(String mapId, String worldId, Storage mapStorage, ServerInterface serverInterface, PluginConfig pluginConfig) { + register(".*", new MapStorageRequestHandler(mapId, mapStorage)); + + register("live/players", "", new JsonDataRequestHandler( + new CachedRateLimitDataSupplier( + new LivePlayersDataSupplier(serverInterface, pluginConfig, worldId), + 1000) + )); + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java index 320e4ee0..e3b7bcd3 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java @@ -25,12 +25,13 @@ package de.bluecolored.bluemap.common.web; import com.flowpowered.math.vector.Vector2i; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.storage.*; import de.bluecolored.bluemap.common.webserver.HttpRequest; import de.bluecolored.bluemap.common.webserver.HttpRequestHandler; import de.bluecolored.bluemap.common.webserver.HttpResponse; import de.bluecolored.bluemap.common.webserver.HttpStatusCode; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.map.BmMap; +import de.bluecolored.bluemap.core.storage.*; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.time.DateFormatUtils; @@ -39,21 +40,25 @@ import java.io.IOException; import java.io.OutputStream; import java.util.*; -import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MapStorageRequestHandler implements HttpRequestHandler { - private static final Pattern TILE_PATTERN = Pattern.compile("data/([^/]+)/([^/]+)/x(-?[\\d/]+)z(-?[\\d/]+).*"); - private static final Pattern META_PATTERN = Pattern.compile("data/([^/]+)/(.*)"); + private static final Pattern TILE_PATTERN = Pattern.compile("([^/]+)/x(-?[\\d/]+)z(-?[\\d/]+).*"); - private final Function mapStorageProvider; - private final HttpRequestHandler notFoundHandler; + private final String mapId; + private final Storage mapStorage; - public MapStorageRequestHandler(Function mapStorageProvider, HttpRequestHandler notFoundHandler) { - this.mapStorageProvider = mapStorageProvider; - this.notFoundHandler = notFoundHandler; + + public MapStorageRequestHandler(BmMap map) { + this.mapId = map.getId(); + this.mapStorage = map.getStorage(); + } + + public MapStorageRequestHandler(String mapId, Storage mapStorage) { + this.mapId = mapId; + this.mapStorage = mapStorage; } @Override @@ -69,78 +74,64 @@ public HttpResponse handle(HttpRequest request) { // provide map-tiles Matcher tileMatcher = TILE_PATTERN.matcher(path); if (tileMatcher.matches()) { - String mapId = tileMatcher.group(1); - String tileTypeId = tileMatcher.group(2); - Storage storage = mapStorageProvider.apply(mapId); - if (storage != null) { - TileType tileType = TileType.forTypeId(tileTypeId); - int x = Integer.parseInt(tileMatcher.group(3).replace("/", "")); - int z = Integer.parseInt(tileMatcher.group(4).replace("/", "")); - Optional optTileData = storage.readMapTileData(mapId, tileType, new Vector2i(x, z)); + String tileTypeId = tileMatcher.group(1); + TileType tileType = TileType.forTypeId(tileTypeId); + int x = Integer.parseInt(tileMatcher.group(2).replace("/", "")); + int z = Integer.parseInt(tileMatcher.group(3).replace("/", "")); + Optional optTileData = mapStorage.readMapTileData(mapId, tileType, new Vector2i(x, z)); - if (optTileData.isPresent()) { - TileData tileData = optTileData.get(); + if (optTileData.isPresent()) { + TileData tileData = optTileData.get(); - // check e-tag - String eTag = calculateETag(path, tileData); - Set etagStringSet = request.getHeader("If-None-Match"); - if (!etagStringSet.isEmpty()){ - if(etagStringSet.iterator().next().equals(eTag)) { + // check e-tag + String eTag = calculateETag(path, tileData); + Set etagStringSet = request.getHeader("If-None-Match"); + if (!etagStringSet.isEmpty()){ + if(etagStringSet.iterator().next().equals(eTag)) { + return new HttpResponse(HttpStatusCode.NOT_MODIFIED); + } + } + + // check modified-since + long lastModified = tileData.getLastModified(); + Set modStringSet = request.getHeader("If-Modified-Since"); + if (!modStringSet.isEmpty()){ + try { + long since = stringToTimestamp(modStringSet.iterator().next()); + if (since + 1000 >= lastModified){ return new HttpResponse(HttpStatusCode.NOT_MODIFIED); } - } - - // check modified-since - long lastModified = tileData.getLastModified(); - Set modStringSet = request.getHeader("If-Modified-Since"); - if (!modStringSet.isEmpty()){ - try { - long since = stringToTimestamp(modStringSet.iterator().next()); - if (since + 1000 >= lastModified){ - return new HttpResponse(HttpStatusCode.NOT_MODIFIED); - } - } catch (IllegalArgumentException ignored){} - } - - CompressedInputStream compressedIn = tileData.readMapTile(); - HttpResponse response = new HttpResponse(HttpStatusCode.OK); - response.addHeader("ETag", eTag); - if (lastModified > 0) - response.addHeader("Last-Modified", timestampToString(lastModified)); - response.addHeader("Content-Type", "application/json"); - writeToResponse(compressedIn, response, request); - return response; + } catch (IllegalArgumentException ignored){} } + + CompressedInputStream compressedIn = tileData.readMapTile(); + HttpResponse response = new HttpResponse(HttpStatusCode.OK); + response.addHeader("ETag", eTag); + if (lastModified > 0) + response.addHeader("Last-Modified", timestampToString(lastModified)); + response.addHeader("Content-Type", "application/json"); + writeToResponse(compressedIn, response, request); + return response; } } // provide meta-data - Matcher metaMatcher = META_PATTERN.matcher(path); - if (metaMatcher.matches()) { - String mapId = metaMatcher.group(1); - String metaFilePath = metaMatcher.group(2); + MetaType metaType = null; + for (MetaType mt : MetaType.values()) { + if (mt.getFilePath().equals(path)) { + metaType = mt; + break; + } + } - Storage storage = mapStorageProvider.apply(mapId); - if (storage != null) { - - MetaType metaType = null; - for (MetaType mt : MetaType.values()) { - if (mt.getFilePath().equals(metaFilePath)) { - metaType = mt; - break; - } - } - - if (metaType != null) { - Optional optIn = storage.readMeta(mapId, metaType); - if (optIn.isPresent()) { - CompressedInputStream compressedIn = optIn.get(); - HttpResponse response = new HttpResponse(HttpStatusCode.OK); - response.addHeader("Content-Type", metaType.getContentType()); - writeToResponse(compressedIn, response, request); - return response; - } - } + if (metaType != null) { + Optional optIn = mapStorage.readMeta(mapId, metaType); + if (optIn.isPresent()) { + CompressedInputStream compressedIn = optIn.get(); + HttpResponse response = new HttpResponse(HttpStatusCode.OK); + response.addHeader("Content-Type", metaType.getContentType()); + writeToResponse(compressedIn, response, request); + return response; } } @@ -150,7 +141,9 @@ public HttpResponse handle(HttpRequest request) { return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR); } - return this.notFoundHandler.handle(request); + HttpResponse response = new HttpResponse(HttpStatusCode.OK); + response.setData("{}"); + return response; } private String calculateETag(String path, TileData tileData) { diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java new file mode 100644 index 00000000..29f3ed64 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java @@ -0,0 +1,86 @@ +package de.bluecolored.bluemap.common.web; + +import de.bluecolored.bluemap.common.webserver.*; +import org.intellij.lang.annotations.Language; + +import java.util.LinkedList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class RoutingRequestHandler implements HttpRequestHandler { + + public LinkedList routes; + + public RoutingRequestHandler() { + this.routes = new LinkedList<>(); + } + + public void register(@Language("RegExp") String pattern, HttpRequestHandler handler) { + register(Pattern.compile(pattern), handler); + } + + public void register(@Language("RegExp") String pattern, String replacementRoute, HttpRequestHandler handler) { + register(Pattern.compile(pattern), replacementRoute, handler); + } + + public void register(Pattern pattern, HttpRequestHandler handler) { + this.routes.addFirst(new Route(pattern, handler)); + } + + public void register(Pattern pattern, String replacementRoute, HttpRequestHandler handler) { + this.routes.addFirst(new Route(pattern, replacementRoute, handler)); + } + + @Override + public HttpResponse handle(HttpRequest request) { + String path = request.getPath(); + + // normalize path + if (path.startsWith("/")) path = path.substring(1); + if (path.endsWith("/")) path = path.substring(0, path.length() - 1); + + for (Route route : routes) { + Matcher matcher = route.getRoutePattern().matcher(path); + if (matcher.matches()) { + RewrittenHttpRequest rewrittenRequest = new RewrittenHttpRequest(request); + rewrittenRequest.setPath(matcher.replaceFirst(route.getReplacementRoute())); + return route.handler.handle(rewrittenRequest); + } + } + + return new HttpResponse(HttpStatusCode.BAD_REQUEST); + } + + private static class Route { + + private final Pattern routePattern; + private final HttpRequestHandler handler; + private final String replacementRoute; + + public Route(Pattern routePattern, HttpRequestHandler handler) { + this.routePattern = routePattern; + this.replacementRoute = "$0"; + this.handler = handler; + } + + public Route(Pattern routePattern, String replacementRoute, HttpRequestHandler handler) { + this.routePattern = routePattern; + this.replacementRoute = replacementRoute; + this.handler = handler; + } + + public Pattern getRoutePattern() { + return routePattern; + } + + public HttpRequestHandler getHandler() { + return handler; + } + + public String getReplacementRoute() { + return replacementRoute; + } + + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpRequest.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpRequest.java index 004d10d4..d42c08b5 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpRequest.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpRequest.java @@ -1,126 +1,53 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ package de.bluecolored.bluemap.common.webserver; -import de.bluecolored.bluemap.common.webserver.HttpConnection.ConnectionClosedException; -import de.bluecolored.bluemap.common.webserver.HttpConnection.InvalidRequestException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class HttpRequest { - - private static final Pattern REQUEST_PATTERN = Pattern.compile("^(\\w+) (\\S+) (.+)$"); - - private String method; - private String address; - private String version; - private Map> header; - private Map> headerLC; - private byte[] data; +public abstract class HttpRequest { private String path = null; private Map getParams = null; private String getParamString = null; - public HttpRequest(String method, String address, String version, Map> header) { - this.method = method; - this.address = address; - this.version = version; - this.header = header; - this.headerLC = new HashMap<>(); + public abstract String getMethod(); - for (Entry> e : header.entrySet()){ - Set values = new HashSet<>(); - for (String v : e.getValue()){ - values.add(v.toLowerCase()); - } + public abstract String getAddress(); - headerLC.put(e.getKey().toLowerCase(), values); - } + public abstract String getVersion(); - this.data = new byte[0]; - } + public abstract Map> getHeader(); - public String getMethod() { - return method; - } + public abstract Map> getLowercaseHeader(); - public String getAddress(){ - return address; - } + public abstract Set getHeader(String key); - public String getVersion() { - return version; - } - - public Map> getHeader() { - return header; - } - - public Map> getLowercaseHeader() { - return headerLC; - } - - public Set getHeader(String key){ - Set headerValues = header.get(key); - if (headerValues == null) return Collections.emptySet(); - return headerValues; - } - - public Set getLowercaseHeader(String key){ - Set headerValues = headerLC.get(key.toLowerCase()); - if (headerValues == null) return Collections.emptySet(); - return headerValues; - } + public abstract Set getLowercaseHeader(String key); public String getPath() { - if (path == null) parseAdress(); + if (path == null) parseAddress(); return path; } public Map getGETParams() { - if (getParams == null) parseAdress(); + if (getParams == null) parseAddress(); return Collections.unmodifiableMap(getParams); } public String getGETParamString() { - if (getParamString == null) parseAdress(); + if (getParamString == null) parseAddress(); return getParamString; } - private void parseAdress() { - String adress = this.address; - if (adress.isEmpty()) adress = "/"; - String[] adressParts = adress.split("\\?", 2); - String path = adressParts[0]; - this.getParamString = adressParts.length > 1 ? adressParts[1] : ""; + protected void parseAddress() { + String address = this.getAddress(); + if (address.isEmpty()) address = "/"; + String[] addressParts = address.split("\\?", 2); + String path = addressParts[0]; + this.getParamString = addressParts.length > 1 ? addressParts[1] : ""; Map getParams = new HashMap<>(); for (String getParam : this.getParamString.split("&")){ @@ -135,100 +62,10 @@ private void parseAdress() { this.getParams = getParams; } - public InputStream getData(){ - return new ByteArrayInputStream(data); - } + public abstract InputStream getData(); - public static HttpRequest read(InputStream in) throws IOException, InvalidRequestException { - BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); - List header = new ArrayList<>(20); - while(header.size() < 1000){ - String headerLine = readLine(reader); - if (headerLine.isEmpty()) break; - header.add(headerLine); - } - - if (header.isEmpty()) throw new InvalidRequestException(); - - Matcher m = REQUEST_PATTERN.matcher(header.remove(0)); - if (!m.find()) throw new InvalidRequestException(); - - String method = m.group(1); - if (method == null) throw new InvalidRequestException(); - - String address = m.group(2); - if (address == null) throw new InvalidRequestException(); - - String version = m.group(3); - if (version == null) throw new InvalidRequestException(); - - Map> headerMap = new HashMap>(); - for (String line : header){ - if (line.trim().isEmpty()) continue; - - String[] kv = line.split(":", 2); - if (kv.length < 2) continue; - - Set values = new HashSet<>(); - if (kv[0].trim().equalsIgnoreCase("If-Modified-Since")){ - values.add(kv[1].trim()); - } else { - for(String v : kv[1].split(",")){ - values.add(v.trim()); - } - } - - headerMap.put(kv[0].trim(), values); - } - - HttpRequest request = new HttpRequest(method, address, version, headerMap); - - if (request.getLowercaseHeader("Transfer-Encoding").contains("chunked")){ - try { - ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - while (dataStream.size() < 1000000){ - String hexSize = reader.readLine(); - int chunkSize = Integer.parseInt(hexSize, 16); - if (chunkSize <= 0) break; - byte[] data = new byte[chunkSize]; - in.read(data); - dataStream.write(data); - } - - if (dataStream.size() >= 1000000) { - throw new InvalidRequestException(); - } - - request.data = dataStream.toByteArray(); - - return request; - } catch (NumberFormatException ex){ - return request; - } - } else { - Set clSet = request.getLowercaseHeader("Content-Length"); - if (clSet.isEmpty()){ - return request; - } else { - try { - int cl = Integer.parseInt(clSet.iterator().next()); - byte[] data = new byte[cl]; - in.read(data); - request.data = data; - return request; - } catch (NumberFormatException ex){ - return request; - } - } - } - } - - private static String readLine(BufferedReader in) throws ConnectionClosedException, IOException { - String line = in.readLine(); - if (line == null){ - throw new ConnectionClosedException(); - } - return line; + static HttpRequest read(InputStream in) throws IOException { + return OriginalHttpRequest.read(in); } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpResponse.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpResponse.java index 3217eb18..25390f07 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpResponse.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/HttpResponse.java @@ -65,6 +65,10 @@ public void setData(String data){ setData(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))); } + public boolean hasData() { + return this.data != null; + } + /** * Writes this Response to an Output-Stream.
*
@@ -73,7 +77,7 @@ public void setData(String data){ public void write(OutputStream out) throws IOException { OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); - if (data != null){ + if (hasData()){ addHeader("Transfer-Encoding", "chunked"); } else { addHeader("Content-Length", "0"); @@ -88,7 +92,7 @@ public void write(OutputStream out) throws IOException { writeLine(writer, ""); writer.flush(); - if(data != null){ + if(hasData()){ chunkedPipe(data, out); out.flush(); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/OriginalHttpRequest.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/OriginalHttpRequest.java new file mode 100644 index 00000000..2a0bd728 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/OriginalHttpRequest.java @@ -0,0 +1,202 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.common.webserver; + +import de.bluecolored.bluemap.common.webserver.HttpConnection.ConnectionClosedException; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OriginalHttpRequest extends HttpRequest { + + private static final Pattern REQUEST_PATTERN = Pattern.compile("^(\\w+) (\\S+) (.+)$"); + + private final String method; + private final String address; + private final String version; + private final Map> header; + private final Map> headerLC; + private byte[] data; + + public OriginalHttpRequest(String method, String address, String version, Map> header) { + this.method = method; + this.address = address; + this.version = version; + this.header = header; + this.headerLC = new HashMap<>(); + + for (Entry> e : header.entrySet()){ + Set values = new HashSet<>(); + for (String v : e.getValue()){ + values.add(v.toLowerCase()); + } + + headerLC.put(e.getKey().toLowerCase(), values); + } + + this.data = new byte[0]; + } + + @Override + public String getMethod() { + return method; + } + + @Override + public String getAddress(){ + return address; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public Map> getHeader() { + return header; + } + + @Override + public Map> getLowercaseHeader() { + return headerLC; + } + + @Override + public Set getHeader(String key){ + Set headerValues = header.get(key); + if (headerValues == null) return Collections.emptySet(); + return headerValues; + } + + @Override + public Set getLowercaseHeader(String key){ + Set headerValues = headerLC.get(key.toLowerCase()); + if (headerValues == null) return Collections.emptySet(); + return headerValues; + } + + @Override + public InputStream getData(){ + return new ByteArrayInputStream(data); + } + + private static String readLine(BufferedReader in) throws IOException { + String line = in.readLine(); + if (line == null){ + throw new ConnectionClosedException(); + } + return line; + } + + public static HttpRequest read(InputStream in) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + List header = new ArrayList<>(20); + while (header.size() < 1000) { + String headerLine = OriginalHttpRequest.readLine(reader); + if (headerLine.isEmpty()) break; + header.add(headerLine); + } + + if (header.isEmpty()) throw new HttpConnection.InvalidRequestException(); + + Matcher m = OriginalHttpRequest.REQUEST_PATTERN.matcher(header.remove(0)); + if (!m.find()) throw new HttpConnection.InvalidRequestException(); + + String method = m.group(1); + if (method == null) throw new HttpConnection.InvalidRequestException(); + + String address = m.group(2); + if (address == null) throw new HttpConnection.InvalidRequestException(); + + String version = m.group(3); + if (version == null) throw new HttpConnection.InvalidRequestException(); + + Map> headerMap = new HashMap<>(); + for (String line : header) { + if (line.trim().isEmpty()) continue; + + String[] kv = line.split(":", 2); + if (kv.length < 2) continue; + + Set values = new HashSet<>(); + if (kv[0].trim().equalsIgnoreCase("If-Modified-Since")) { + values.add(kv[1].trim()); + } else { + for (String v : kv[1].split(",")) { + values.add(v.trim()); + } + } + + headerMap.put(kv[0].trim(), values); + } + + OriginalHttpRequest request = new OriginalHttpRequest(method, address, version, headerMap); + + if (request.getLowercaseHeader("Transfer-Encoding").contains("chunked")) { + try { + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + while (dataStream.size() < 1000000) { + String hexSize = reader.readLine(); + int chunkSize = Integer.parseInt(hexSize, 16); + if (chunkSize <= 0) break; + byte[] data = new byte[chunkSize]; + in.read(data); + dataStream.write(data); + } + + if (dataStream.size() >= 1000000) { + throw new HttpConnection.InvalidRequestException(); + } + + request.data = dataStream.toByteArray(); + + return request; + } catch (NumberFormatException ex) { + return request; + } + } else { + Set clSet = request.getLowercaseHeader("Content-Length"); + if (clSet.isEmpty()) { + return request; + } else { + try { + int cl = Integer.parseInt(clSet.iterator().next()); + byte[] data = new byte[cl]; + in.read(data); + request.data = data; + return request; + } catch (NumberFormatException ex) { + return request; + } + } + } + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/RewrittenHttpRequest.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/RewrittenHttpRequest.java new file mode 100644 index 00000000..6b2f6db6 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/webserver/RewrittenHttpRequest.java @@ -0,0 +1,173 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.common.webserver; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class RewrittenHttpRequest extends HttpRequest { + + private final HttpRequest originalRequest; + + private String method = null; + private String address = null; + private String version = null; + private Map> header = null; + private Map> headerLC = null; + private byte[] data = null; + + public RewrittenHttpRequest(HttpRequest originalRequest) { + this.originalRequest = originalRequest; + + } + + public void setMethod(String method) { + this.method = method; + } + + @Override + public String getMethod() { + return method != null ? method : originalRequest.getMethod(); + } + + public void setAddress(String address) { + this.address = address; + parseAddress(); + } + + @Override + public String getAddress() { + return address != null ? address : originalRequest.getAddress(); + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public String getVersion() { + return version != null ? version : originalRequest.getVersion(); + } + + public void setHeader(Map> header) { + this.header = header; + this.headerLC = new HashMap<>(); + for (Map.Entry> e : header.entrySet()){ + Set values = new HashSet<>(); + for (String v : e.getValue()){ + values.add(v.toLowerCase()); + } + + headerLC.put(e.getKey().toLowerCase(), values); + } + } + + @Override + public Map> getHeader() { + return header != null ? header : originalRequest.getHeader(); + } + + @Override + public Map> getLowercaseHeader() { + return headerLC != null ? headerLC : originalRequest.getLowercaseHeader(); + } + + public void setHeader(String key, String value) { + if (header == null || headerLC == null) { + header = new HashMap<>(originalRequest.getHeader()); + headerLC = new HashMap<>(originalRequest.getLowercaseHeader()); + } + + header.computeIfAbsent(key, k -> new HashSet<>()).add(value); + headerLC.computeIfAbsent(key.toLowerCase(), k -> new HashSet<>()).add(value.toLowerCase()); + } + + public void removeHeader(String key) { + if (header == null || headerLC == null) { + header = new HashMap<>(originalRequest.getHeader()); + headerLC = new HashMap<>(originalRequest.getLowercaseHeader()); + } + + header.remove(key); + headerLC.remove(key.toLowerCase()); + } + + @Override + public Set getHeader(String key) { + return header != null ? header.get(key) : originalRequest.getHeader(key); + } + + @Override + public Set getLowercaseHeader(String key) { + return headerLC != null ? headerLC.get(key.toLowerCase()) : originalRequest.getLowercaseHeader(key); + } + + public void setPath(String path) { + if (getGETParamString().isEmpty()) this.address = path; + else this.address = path + "?" + getGETParamString(); + parseAddress(); + } + + @Override + public String getPath() { + if (address == null) return originalRequest.getPath(); + return super.getPath(); + } + + @Override + public Map getGETParams() { + if (address == null) return originalRequest.getGETParams(); + return super.getGETParams(); + } + + public void setGETParamString(String paramString) { + this.address = getAddress() + "?" + paramString; + parseAddress(); + } + + @Override + public String getGETParamString() { + if (address == null) return originalRequest.getGETParamString(); + return super.getGETParamString(); + } + + public void setData(byte[] data) { + this.data = data; + } + + @Override + public InputStream getData() { + return data != null ? new ByteArrayInputStream(data) : originalRequest.getData(); + } + + public HttpRequest getOriginalRequest() { + return originalRequest; + } + +} diff --git a/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/plugin.conf b/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/plugin.conf index 0516cbe5..74ef37b6 100644 --- a/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/plugin.conf +++ b/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/plugin.conf @@ -27,6 +27,10 @@ hide-invisible: true # Default is false hide-sneaking: false +# If this is true, players that are on a different world than the viewed map will not appear on the player-list. +# Default is false +hide-different-world: false + # Download the skin from mojang-serves when a player joins your server, so it can be used for the player-markers. # Default is true skin-download: true diff --git a/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages/file.conf b/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages/file.conf index 8c1cd6a8..e54f6514 100644 --- a/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages/file.conf +++ b/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages/file.conf @@ -9,7 +9,7 @@ storage-type: FILE # The path to the folder on your file-system where bluemap will save the rendered map -# The default is: "bluemap/web/data" +# The default is: "bluemap/web/maps" root: "${root}" # The compression-type that bluemap will use to compress generated map-data. diff --git a/BlueMapCore/gradle/wrapper/gradle-wrapper.properties b/BlueMapCore/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/BlueMapCore/gradle/wrapper/gradle-wrapper.properties +++ b/BlueMapCore/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/debug/StateDumper.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/debug/StateDumper.java index 17c6a6b6..a0a093c3 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/debug/StateDumper.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/debug/StateDumper.java @@ -161,58 +161,70 @@ private void dumpInstance(Object instance, ConfigurationOptions options, Configu return; } - boolean foundSomething = false; - do { - boolean allFields = type.isAnnotationPresent(DebugDump.class); - - for (Field field : type.getDeclaredFields()) { - DebugDump dd = field.getAnnotation(DebugDump.class); - if (dd == null) { - if (!allFields) continue; - if (Modifier.isStatic(field.getModifiers())) continue; - if (Modifier.isTransient(field.getModifiers())) continue; - } - foundSomething = true; - - String key = ""; - if (dd != null) key = dd.value(); - if (key.isEmpty()) key = field.getName(); - - if (options.acceptsType(field.getType())) { - field.setAccessible(true); - node.node(key).set(field.get(instance)); - } else { - field.setAccessible(true); - dumpInstance(field.get(instance), options, node.node(key), alreadyDumped); - } - } - - for (Method method : type.getDeclaredMethods()) { - DebugDump dd = method.getAnnotation(DebugDump.class); - if (dd == null) continue; - foundSomething = true; - - String key = dd.value(); - if (key.isEmpty()) key = method.toGenericString().replace(' ', '_'); - - if (options.acceptsType(method.getReturnType())) { - method.setAccessible(true); - node.node(key).set(method.invoke(instance)); - } else { - method.setAccessible(true); - dumpInstance(method.invoke(instance), options, node.node(key), alreadyDumped); - } - } - } while ((type = type.getSuperclass()) != null); - + boolean foundSomething = dumpAnnotatedInstance(type, instance, options, node, alreadyDumped); if (!foundSomething) { node.set(instance.toString()); } + } catch (Exception ex) { node.set("Error: " + ex); } } + private boolean dumpAnnotatedInstance(Class type, Object instance, ConfigurationOptions options, ConfigurationNode node, Set alreadyDumped) throws Exception { + boolean foundSomething = false; + boolean allFields = type.isAnnotationPresent(DebugDump.class); + + for (Field field : type.getDeclaredFields()) { + DebugDump dd = field.getAnnotation(DebugDump.class); + if (dd == null) { + if (!allFields) continue; + if (Modifier.isStatic(field.getModifiers())) continue; + if (Modifier.isTransient(field.getModifiers())) continue; + } + foundSomething = true; + + String key = ""; + if (dd != null) key = dd.value(); + if (key.isEmpty()) key = field.getName(); + + field.setAccessible(true); + if (options.acceptsType(field.getType())) { + node.node(key).set(field.get(instance)); + } else { + dumpInstance(field.get(instance), options, node.node(key), alreadyDumped); + } + } + + for (Method method : type.getDeclaredMethods()) { + DebugDump dd = method.getAnnotation(DebugDump.class); + if (dd == null) continue; + foundSomething = true; + + String key = dd.value(); + if (key.isEmpty()) key = method.toGenericString().replace(' ', '_'); + + if (options.acceptsType(method.getReturnType())) { + method.setAccessible(true); + node.node(key).set(method.invoke(instance)); + } else { + method.setAccessible(true); + dumpInstance(method.invoke(instance), options, node.node(key), alreadyDumped); + } + } + + for (Class iface : type.getInterfaces()) { + foundSomething |= dumpAnnotatedInstance(iface, instance, options, node, alreadyDumped); + } + + Class typeSuperclass = type.getSuperclass(); + if (typeSuperclass != null) { + foundSomething |= dumpAnnotatedInstance(typeSuperclass, instance, options, node, alreadyDumped); + } + + return foundSomething; + } + private void collectSystemInfo(ConfigurationNode node) throws SerializationException { node.node("bluemap-version").set(BlueMap.VERSION); node.node("git-hash").set(BlueMap.GIT_HASH); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/metrics/Metrics.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/metrics/Metrics.java index 8a17696b..094df86a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/metrics/Metrics.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/metrics/Metrics.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; -import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -57,7 +56,7 @@ public static void sendReport(String implementation) { } } - private static String sendData(String data) throws MalformedURLException, IOException { + private static String sendData(String data) throws IOException { byte[] bytes = data.getBytes(StandardCharsets.UTF_8); HttpsURLConnection connection = (HttpsURLConnection) new URL(METRICS_REPORT_URL).openConnection(); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/ConfigUtils.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/ConfigUtils.java index 06d7c52f..0d63de6e 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/ConfigUtils.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/ConfigUtils.java @@ -157,9 +157,11 @@ public static int readColorInt(ConfigurationNode node) throws NumberFormatExcept public static int parseColorFromString(String val) { if (val.charAt(0) == '#') { val = val.substring(1); - if (val.length() == 3) val = "f" + val; + if (val.length() == 3) val = val + "f"; if (val.length() == 4) val = "" + val.charAt(0) + val.charAt(0) + val.charAt(1) + val.charAt(1) + val.charAt(2) + val.charAt(2) + val.charAt(3) + val.charAt(3); - if (val.length() == 6) val = "ff" + val; + if (val.length() == 6) val = val + "ff"; + if (val.length() != 8) throw new NumberFormatException("Invalid color format!"); + val = val.substring(6, 8) + val.substring(0, 6); // move alpha to front return Integer.parseUnsignedInt(val, 16); } diff --git a/BlueMapCore/src/main/resourceExtensions/mc1_13/assets/minecraft/biomes.json b/BlueMapCore/src/main/resourceExtensions/mc1_13/assets/minecraft/biomes.json index 8b37a599..b14a8d34 100644 --- a/BlueMapCore/src/main/resourceExtensions/mc1_13/assets/minecraft/biomes.json +++ b/BlueMapCore/src/main/resourceExtensions/mc1_13/assets/minecraft/biomes.json @@ -149,8 +149,8 @@ "minecraft:dark_forest": { "humidity": 0.8, "temperature": 0.7, - "foliagecolor": "#5528340a", - "grasscolor": "#8828340a", + "foliagecolor": "#28340a55", + "grasscolor": "#28340a88", "watercolor": 4159204 }, "minecraft:snowy_taiga": { @@ -329,8 +329,8 @@ "minecraft:dark_forest_hills": { "humidity": 0.8, "temperature": 0.7, - "foliagecolor": "#5528340a", - "grasscolor": "#8828340a", + "foliagecolor": "#28340a55", + "grasscolor": "#28340a88", "watercolor": 4159204 }, "minecraft:snowy_taiga_mountains": { diff --git a/BlueMapCore/src/main/resourceExtensions/mc1_15/assets/minecraft/biomes.json b/BlueMapCore/src/main/resourceExtensions/mc1_15/assets/minecraft/biomes.json index 8b37a599..b14a8d34 100644 --- a/BlueMapCore/src/main/resourceExtensions/mc1_15/assets/minecraft/biomes.json +++ b/BlueMapCore/src/main/resourceExtensions/mc1_15/assets/minecraft/biomes.json @@ -149,8 +149,8 @@ "minecraft:dark_forest": { "humidity": 0.8, "temperature": 0.7, - "foliagecolor": "#5528340a", - "grasscolor": "#8828340a", + "foliagecolor": "#28340a55", + "grasscolor": "#28340a88", "watercolor": 4159204 }, "minecraft:snowy_taiga": { @@ -329,8 +329,8 @@ "minecraft:dark_forest_hills": { "humidity": 0.8, "temperature": 0.7, - "foliagecolor": "#5528340a", - "grasscolor": "#8828340a", + "foliagecolor": "#28340a55", + "grasscolor": "#28340a88", "watercolor": 4159204 }, "minecraft:snowy_taiga_mountains": { diff --git a/BlueMapCore/src/main/resourceExtensions/mc1_16/assets/minecraft/biomes.json b/BlueMapCore/src/main/resourceExtensions/mc1_16/assets/minecraft/biomes.json index 8b37a599..b14a8d34 100644 --- a/BlueMapCore/src/main/resourceExtensions/mc1_16/assets/minecraft/biomes.json +++ b/BlueMapCore/src/main/resourceExtensions/mc1_16/assets/minecraft/biomes.json @@ -149,8 +149,8 @@ "minecraft:dark_forest": { "humidity": 0.8, "temperature": 0.7, - "foliagecolor": "#5528340a", - "grasscolor": "#8828340a", + "foliagecolor": "#28340a55", + "grasscolor": "#28340a88", "watercolor": 4159204 }, "minecraft:snowy_taiga": { @@ -329,8 +329,8 @@ "minecraft:dark_forest_hills": { "humidity": 0.8, "temperature": 0.7, - "foliagecolor": "#5528340a", - "grasscolor": "#8828340a", + "foliagecolor": "#28340a55", + "grasscolor": "#28340a88", "watercolor": 4159204 }, "minecraft:snowy_taiga_mountains": { diff --git a/BlueMapCore/src/main/resourceExtensions/mc1_18/assets/minecraft/biomes.json b/BlueMapCore/src/main/resourceExtensions/mc1_18/assets/minecraft/biomes.json index 7e8e6118..f56c732d 100644 --- a/BlueMapCore/src/main/resourceExtensions/mc1_18/assets/minecraft/biomes.json +++ b/BlueMapCore/src/main/resourceExtensions/mc1_18/assets/minecraft/biomes.json @@ -62,8 +62,8 @@ "humidity": 0.8, "temperature": 0.7, "watercolor": 4159204, - "foliagecolor": "#5528340a", - "grasscolor": "#8828340a" + "foliagecolor": "#28340a55", + "grasscolor": "#28340a88" }, "minecraft:old_growth_birch_forest": { "humidity": 0.6, diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e..41dfb879 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/cli/gradle/wrapper/gradle-wrapper.properties b/implementations/cli/gradle/wrapper/gradle-wrapper.properties index 69a97150..41dfb879 100644 --- a/implementations/cli/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/cli/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java b/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java index a49bd9ef..b109a837 100644 --- a/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java +++ b/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java @@ -32,24 +32,22 @@ import de.bluecolored.bluemap.common.config.MapConfig; import de.bluecolored.bluemap.common.config.WebserverConfig; import de.bluecolored.bluemap.common.plugin.RegionFileWatchService; +import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask; +import de.bluecolored.bluemap.common.rendermanager.RenderManager; +import de.bluecolored.bluemap.common.rendermanager.RenderTask; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; -import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask; -import de.bluecolored.bluemap.common.rendermanager.RenderManager; -import de.bluecolored.bluemap.common.rendermanager.RenderTask; import de.bluecolored.bluemap.common.web.FileRequestHandler; -import de.bluecolored.bluemap.common.web.MapStorageRequestHandler; -import de.bluecolored.bluemap.common.webserver.HttpRequestHandler; +import de.bluecolored.bluemap.common.web.MapRequestHandler; +import de.bluecolored.bluemap.common.web.RoutingRequestHandler; import de.bluecolored.bluemap.common.webserver.WebServer; -import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.LoggerLogger; import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.metrics.Metrics; -import de.bluecolored.bluemap.core.storage.Storage; import org.apache.commons.cli.*; import org.apache.commons.lang3.time.DurationFormatUtils; @@ -59,6 +57,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; public class BlueMapCLI implements ServerInterface { @@ -183,32 +182,30 @@ public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOExc WebserverConfig config = blueMap.getConfigs().getWebserverConfig(); Files.createDirectories(config.getWebroot()); - HttpRequestHandler requestHandler = new FileRequestHandler(config.getWebroot(), "BlueMap v" + BlueMap.VERSION); - try { - //use map-storage to provide map-tiles - Map mapConfigs = blueMap.getConfigs().getMapConfigs(); - Map mapStorages = new HashMap<>(); - for (var entry : mapConfigs.entrySet()) { - mapStorages.put( - entry.getKey(), - blueMap.getStorage(entry.getValue().getStorage()) - ); - } + RoutingRequestHandler routingRequestHandler = new RoutingRequestHandler(); - requestHandler = new MapStorageRequestHandler(mapStorages::get, requestHandler); - } catch (ConfigurationException ex) { - Logger.global.logWarning(ex.getFormattedExplanation()); - Logger.global.logError("Here is the full error:", ex); + // default route + routingRequestHandler.register(".*", new FileRequestHandler(config.getWebroot())); - Logger.global.logWarning("The webserver will still be started, but it will not be able to serve the map-tiles correctly!"); + // map route + for (var entry : blueMap.getConfigs().getMapConfigs().entrySet()) { + String mapId = entry.getKey(); + MapConfig mapConfig = entry.getValue(); + String worldId = blueMap.getWorldId(mapConfig.getWorld()); + + routingRequestHandler.register( + "maps/" + Pattern.quote(mapId) + "/(.*)", + "$1", + new MapRequestHandler(mapId, worldId, blueMap.getStorage(mapConfig.getStorage()), this, blueMap.getConfigs().getPluginConfig()) + ); } WebServer webServer = new WebServer( config.resolveIp(), config.getPort(), config.getMaxConnectionCount(), - requestHandler, + routingRequestHandler, verbose ); webServer.start(); diff --git a/implementations/fabric-1.15.2/build.gradle.kts b/implementations/fabric-1.15.2/build.gradle.kts index 11137a88..b22bddad 100644 --- a/implementations/fabric-1.15.2/build.gradle.kts +++ b/implementations/fabric-1.15.2/build.gradle.kts @@ -8,7 +8,7 @@ plugins { id ("com.palantir.git-version") version "0.12.3" id ("com.github.node-gradle.node") version "3.0.1" id ("com.github.johnrengelman.shadow") version "7.1.2" - id ("fabric-loom") version "0.8-SNAPSHOT" + id ("fabric-loom") version "0.12-SNAPSHOT" } val versionDetails: groovy.lang.Closure by extra @@ -129,10 +129,10 @@ tasks.register("remappedShadowJar", type = RemapJarTask::class) { destinationDirectory.set(file("../../build/release")) archiveFileName.set("BlueMap-${archiveVersion.get()}-${project.name}.jar") dependsOn (tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile.get()) + inputFile.set(tasks.shadowJar.get().archiveFile) addNestedDependencies.set(true) } tasks.register("release") { - dependsOn(tasks["remappedShadowJar"]) + dependsOn("remappedShadowJar") } diff --git a/implementations/fabric-1.15.2/gradle/wrapper/gradle-wrapper.properties b/implementations/fabric-1.15.2/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/fabric-1.15.2/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/fabric-1.15.2/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index c12369ff..1677c34e 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -56,6 +56,7 @@ public class FabricPlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -92,6 +93,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -139,6 +145,7 @@ public void update() { Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.pitch, player.headYaw, 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/fabric-1.16.1/build.gradle.kts b/implementations/fabric-1.16.1/build.gradle.kts index daa378db..99d76827 100644 --- a/implementations/fabric-1.16.1/build.gradle.kts +++ b/implementations/fabric-1.16.1/build.gradle.kts @@ -8,7 +8,7 @@ plugins { id ("com.palantir.git-version") version "0.12.3" id ("com.github.node-gradle.node") version "3.0.1" id ("com.github.johnrengelman.shadow") version "7.1.2" - id ("fabric-loom") version "0.8-SNAPSHOT" + id ("fabric-loom") version "0.12-SNAPSHOT" } val versionDetails: groovy.lang.Closure by extra @@ -129,10 +129,10 @@ tasks.register("remappedShadowJar", type = RemapJarTask::class) { destinationDirectory.set(file("../../build/release")) archiveFileName.set("BlueMap-${archiveVersion.get()}-${project.name}.jar") dependsOn (tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile.get()) + inputFile.set(tasks.shadowJar.get().archiveFile) addNestedDependencies.set(true) } tasks.register("release") { - dependsOn(tasks["remappedShadowJar"]) + dependsOn("remappedShadowJar") } diff --git a/implementations/fabric-1.16.1/gradle/wrapper/gradle-wrapper.properties b/implementations/fabric-1.16.1/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/fabric-1.16.1/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/fabric-1.16.1/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index c12369ff..1677c34e 100644 --- a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -56,6 +56,7 @@ public class FabricPlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -92,6 +93,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -139,6 +145,7 @@ public void update() { Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.pitch, player.headYaw, 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/fabric-1.16.2/build.gradle.kts b/implementations/fabric-1.16.2/build.gradle.kts index e00f5f9d..aa52e747 100644 --- a/implementations/fabric-1.16.2/build.gradle.kts +++ b/implementations/fabric-1.16.2/build.gradle.kts @@ -8,7 +8,7 @@ plugins { id ("com.palantir.git-version") version "0.12.3" id ("com.github.node-gradle.node") version "3.0.1" id ("com.github.johnrengelman.shadow") version "7.1.2" - id ("fabric-loom") version "0.8-SNAPSHOT" + id ("fabric-loom") version "0.12-SNAPSHOT" } val versionDetails: groovy.lang.Closure by extra @@ -129,10 +129,10 @@ tasks.register("remappedShadowJar", type = RemapJarTask::class) { destinationDirectory.set(file("../../build/release")) archiveFileName.set("BlueMap-${archiveVersion.get()}-${project.name}.jar") dependsOn (tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile.get()) + inputFile.set(tasks.shadowJar.get().archiveFile) addNestedDependencies.set(true) } tasks.register("release") { - dependsOn(tasks["remappedShadowJar"]) + dependsOn("remappedShadowJar") } diff --git a/implementations/fabric-1.16.2/gradle/wrapper/gradle-wrapper.properties b/implementations/fabric-1.16.2/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/fabric-1.16.2/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/fabric-1.16.2/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index c12369ff..1677c34e 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -56,6 +56,7 @@ public class FabricPlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -92,6 +93,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -139,6 +145,7 @@ public void update() { Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.pitch, player.headYaw, 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/fabric-1.17/build.gradle.kts b/implementations/fabric-1.17/build.gradle.kts index cd44e0e4..00c3efe2 100644 --- a/implementations/fabric-1.17/build.gradle.kts +++ b/implementations/fabric-1.17/build.gradle.kts @@ -8,7 +8,7 @@ plugins { id ("com.palantir.git-version") version "0.12.3" id ("com.github.node-gradle.node") version "3.0.1" id ("com.github.johnrengelman.shadow") version "7.1.2" - id ("fabric-loom") version "0.8-SNAPSHOT" + id ("fabric-loom") version "0.12-SNAPSHOT" } val versionDetails: groovy.lang.Closure by extra @@ -129,10 +129,10 @@ tasks.register("remappedShadowJar", type = RemapJarTask::class) { destinationDirectory.set(file("../../build/release")) archiveFileName.set("BlueMap-${archiveVersion.get()}-${project.name}.jar") dependsOn (tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile.get()) + inputFile.set(tasks.shadowJar.get().archiveFile) addNestedDependencies.set(true) } tasks.register("release") { - dependsOn(tasks["remappedShadowJar"]) + dependsOn("remappedShadowJar") } diff --git a/implementations/fabric-1.17/gradle/wrapper/gradle-wrapper.properties b/implementations/fabric-1.17/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/fabric-1.17/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/fabric-1.17/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index 2ea8be7b..1c75bab3 100644 --- a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -55,6 +55,7 @@ public class FabricPlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -91,6 +92,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -138,6 +144,7 @@ public void update() { Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.getPitch(), player.getHeadYaw(), 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/fabric-1.18/build.gradle.kts b/implementations/fabric-1.18/build.gradle.kts index 20e89a8f..a1793b05 100644 --- a/implementations/fabric-1.18/build.gradle.kts +++ b/implementations/fabric-1.18/build.gradle.kts @@ -8,7 +8,7 @@ plugins { id ("com.palantir.git-version") version "0.12.3" id ("com.github.node-gradle.node") version "3.0.1" id ("com.github.johnrengelman.shadow") version "7.1.2" - id ("fabric-loom") version "0.8-SNAPSHOT" + id ("fabric-loom") version "0.12-SNAPSHOT" } val versionDetails: groovy.lang.Closure by extra @@ -129,10 +129,10 @@ tasks.register("remappedShadowJar", type = RemapJarTask::class) { destinationDirectory.set(file("../../build/release")) archiveFileName.set("BlueMap-${archiveVersion.get()}-${project.name}.jar") dependsOn (tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile.get()) + inputFile.set(tasks.shadowJar.get().archiveFile) addNestedDependencies.set(true) } tasks.register("release") { - dependsOn(tasks["remappedShadowJar"]) + dependsOn("remappedShadowJar") } diff --git a/implementations/fabric-1.18/gradle/wrapper/gradle-wrapper.properties b/implementations/fabric-1.18/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/fabric-1.18/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/fabric-1.18/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index 63b1a52b..638323b4 100644 --- a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -55,6 +55,7 @@ public class FabricPlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -91,6 +92,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return this.rotation; + } + @Override public boolean isOnline() { return this.online; @@ -138,6 +144,7 @@ public void update() { Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.getPitch(), player.getHeadYaw(), 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/fabric-1.19/build.gradle.kts b/implementations/fabric-1.19/build.gradle.kts index dc873acd..af394b3f 100644 --- a/implementations/fabric-1.19/build.gradle.kts +++ b/implementations/fabric-1.19/build.gradle.kts @@ -129,10 +129,10 @@ tasks.register("remappedShadowJar", type = RemapJarTask::class) { destinationDirectory.set(file("../../build/release")) archiveFileName.set("BlueMap-${archiveVersion.get()}-${project.name}.jar") dependsOn (tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile.get()) + inputFile.set(tasks.shadowJar.get().archiveFile) addNestedDependencies.set(true) } tasks.register("release") { - dependsOn(tasks["remappedShadowJar"]) + dependsOn("remappedShadowJar") } diff --git a/implementations/fabric-1.19/gradle/wrapper/gradle-wrapper.properties b/implementations/fabric-1.19/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/fabric-1.19/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/fabric-1.19/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/fabric-1.19/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.19/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index 63b1a52b..66b0a0f8 100644 --- a/implementations/fabric-1.19/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.19/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -55,6 +55,7 @@ public class FabricPlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -91,6 +92,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -138,6 +144,7 @@ public void update() { Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.getPitch(), player.getHeadYaw(), 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/forge-1.14.4/gradle/wrapper/gradle-wrapper.properties b/implementations/forge-1.14.4/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/forge-1.14.4/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/forge-1.14.4/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index 11dd9c44..0daa48f9 100644 --- a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -56,6 +56,7 @@ public class ForgePlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -92,6 +93,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -139,6 +145,7 @@ public void update() { Vec3d pos = player.getPositionVec(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.rotationPitch, player.rotationYawHead, 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/forge-1.15.2/gradle/wrapper/gradle-wrapper.properties b/implementations/forge-1.15.2/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/forge-1.15.2/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/forge-1.15.2/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index f29446d6..ce9d691d 100644 --- a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -56,6 +56,7 @@ public class ForgePlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -92,6 +93,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -139,6 +145,7 @@ public void update() { Vec3d pos = player.getPositionVec(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.rotationPitch, player.rotationYawHead, 0); this.sneaking = player.isCrouching(); try { diff --git a/implementations/forge-1.16.2/gradle/wrapper/gradle-wrapper.properties b/implementations/forge-1.16.2/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/forge-1.16.2/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/forge-1.16.2/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index 47d7230f..ec789bfb 100644 --- a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -55,6 +55,7 @@ public class ForgePlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -91,6 +92,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -138,6 +144,7 @@ public void update() { var pos = player.getPositionVec(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.rotation = new Vector3d(player.rotationPitch, player.rotationYawHead, 0); this.sneaking = player.isCrouching(); try { diff --git a/implementations/forge-1.17.1/gradle/wrapper/gradle-wrapper.properties b/implementations/forge-1.17.1/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/forge-1.17.1/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/forge-1.17.1/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index fc6ea264..01394d9b 100644 --- a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -55,6 +55,7 @@ public class ForgePlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -91,6 +92,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -138,6 +144,7 @@ public void update() { Vec3 pos = player.getPosition(1f); this.position = new Vector3d(pos.x(), pos.y(), pos.z()); + this.rotation = new Vector3d(player.getXRot(), player.getYHeadRot(), 0); this.sneaking = player.isCrouching(); try { diff --git a/implementations/forge-1.18.1/gradle/wrapper/gradle-wrapper.properties b/implementations/forge-1.18.1/gradle/wrapper/gradle-wrapper.properties index 2e6e5897..41dfb879 100644 --- a/implementations/forge-1.18.1/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/forge-1.18.1/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index ccb99524..8df96795 100644 --- a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -76,7 +76,7 @@ public ForgeMod() { this.onlinePlayerMap = new ConcurrentHashMap<>(); this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>()); - this.pluginInstance = new Plugin("forge-1.17.1", this); + this.pluginInstance = new Plugin("forge-1.18.1", this); this.eventForwarder = new ForgeEventForwarder(); this.worlds = Caffeine.newBuilder() diff --git a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index fc6ea264..01394d9b 100644 --- a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -55,6 +55,7 @@ public class ForgePlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -91,6 +92,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -138,6 +144,7 @@ public void update() { Vec3 pos = player.getPosition(1f); this.position = new Vector3d(pos.x(), pos.y(), pos.z()); + this.rotation = new Vector3d(player.getXRot(), player.getYHeadRot(), 0); this.sneaking = player.isCrouching(); try { diff --git a/implementations/forge-1.19/build.gradle b/implementations/forge-1.19/build.gradle new file mode 100644 index 00000000..46dd2fed --- /dev/null +++ b/implementations/forge-1.19/build.gradle @@ -0,0 +1,172 @@ +buildscript { + repositories { + // These repositories are only for Gradle plugins, put any other repositories in the repository block further below + maven { url = 'https://maven.minecraftforge.net' } + mavenCentral() + } + dependencies { + classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true + } +} + +plugins { + id "java" + id "java-library" + id "com.diffplug.spotless" version "6.1.2" + id "com.palantir.git-version" version "0.12.3" + id "com.github.node-gradle.node" version "3.0.1" + id "com.github.johnrengelman.shadow" version "7.1.2" +} + +apply plugin: "net.minecraftforge.gradle" + +def git = versionDetails() + +def releaseProperties = new Properties() +releaseProperties.load(new FileInputStream(file("../../release.properties"))) + +group = "de.bluecolored.bluemap.forge" +version = releaseProperties.version.toString() +archivesBaseName = 'bluemap' + +def javaTarget = 17 +java { + sourceCompatibility = JavaVersion.toVersion(javaTarget) + targetCompatibility = JavaVersion.toVersion(javaTarget) +} + +minecraft { + mappings channel: 'official', version: '1.19' + + runs { + server { + workingDirectory project.file('run') + property 'forge.logging.markers', 'REGISTRIES' + property 'forge.logging.console.level', 'debug' + + mods { + bluemap { + source sourceSets.main + } + } + } + } +} + +sourceSets.main.resources { srcDir 'src/generated/resources' } + +repositories { + mavenCentral() + maven { + setUrl("https://libraries.minecraft.net") + } + maven { + setUrl("https://jitpack.io") + } +} + +configurations { + implementation.extendsFrom(shadowInclude) +} + +dependencies { + minecraft 'net.minecraftforge:forge:1.19-41.0.16' + + shadowInclude ("de.bluecolored.bluemap.common:BlueMapCommon") { + //exclude dependencies provided by forge + exclude (group: "com.google.guava", module: "guava") + exclude (group: "com.google.code.gson", module: "gson") + exclude (group: "org.apache.commons", module: "commons-lang3") + exclude (group: "commons-io", module: "commons-io") + exclude (group: "com.mojang", module: "brigadier") + } + + testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2") + testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2") +} + +spotless { + java { + target ("src/*/java/**/*.java") + + licenseHeaderFile("../../HEADER") + indentWithSpaces() + trimTrailingWhitespace() + } +} + +jar { + manifest { + attributes([ + "Specification-Title" : "bluemap", + "Specification-Vendor" : "bluemap", + "Specification-Version" : "1", // We are version 1 of ourselves + "Implementation-Title" : project.name, + "Implementation-Version" : project.jar.archiveVersion, + "Implementation-Vendor" : "bluemap", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + ]) + } +} + +jar.finalizedBy('reobfJar') + +tasks.withType(JavaCompile).configureEach { + options.encoding = "utf-8" +} + +tasks.withType(AbstractArchiveTask).configureEach { + setReproducibleFileOrder(true) + setPreserveFileTimestamps(false) +} + +test { + useJUnitPlatform() +} + +shadowJar { + destinationDirectory.set(file("../../build/release")) + archiveFileName.set("BlueMap-${archiveVersion.get()}-${project.name}.jar") + configurations = [project.configurations.shadowInclude] + + //relocate ("com.flowpowered.math", "de.bluecolored.shadow.flowpowered.math") //DON"T relocate this, because the API depends on it + relocate ("com.typesafe.config", "de.bluecolored.shadow.typesafe.config") + relocate ("net.querz.nbt", "de.bluecolored.shadow.querz.nbt") + relocate ("org.spongepowered.configurate", "de.bluecolored.shadow.configurate") + relocate ("com.github.benmanes.caffeine", "de.bluecolored.shadow.benmanes.caffeine") + relocate ("org.aopalliance", "de.bluecolored.shadow.aopalliance") + relocate ("javax.inject", "de.bluecolored.shadow.javax.inject") + relocate ("org.checkerframework", "de.bluecolored.shadow.checkerframework") + relocate ("org.codehaus", "de.bluecolored.shadow.codehaus") + relocate ("io.leangen.geantyref", "de.bluecolored.shadow.geantyref") + + relocate ("com.google.errorprone", "de.bluecolored.shadow.google.errorprone") + relocate ("com.google.inject", "de.bluecolored.shadow.google.inject") + + relocate ("org.apache.commons.dbcp2", "de.bluecolored.shadow.apache.commons.dbcp2") + relocate ("org.apache.commons.logging", "de.bluecolored.shadow.apache.commons.logging") + relocate ("org.apache.commons.pool2", "de.bluecolored.shadow.apache.commons.pool2") +} + +processResources { + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info','META-INF/mods.toml' + duplicatesStrategy = DuplicatesStrategy.WARN + + expand ( + version: project.version + ) + } +} + +afterEvaluate { + reobf { + shadowJar { + mappings = createMcpToSrg.output + } + } +} + +task release { + dependsOn(build) +} diff --git a/implementations/forge-1.19/gradle/wrapper/gradle-wrapper.jar b/implementations/forge-1.19/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..7454180f Binary files /dev/null and b/implementations/forge-1.19/gradle/wrapper/gradle-wrapper.jar differ diff --git a/implementations/forge-1.19/gradle/wrapper/gradle-wrapper.properties b/implementations/forge-1.19/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..41dfb879 --- /dev/null +++ b/implementations/forge-1.19/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/implementations/forge-1.19/gradlew b/implementations/forge-1.19/gradlew new file mode 100644 index 00000000..c53aefaa --- /dev/null +++ b/implementations/forge-1.19/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/implementations/forge-1.19/gradlew.bat b/implementations/forge-1.19/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/implementations/forge-1.19/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/implementations/forge-1.19/settings.gradle.kts b/implementations/forge-1.19/settings.gradle.kts new file mode 100644 index 00000000..3af000a6 --- /dev/null +++ b/implementations/forge-1.19/settings.gradle.kts @@ -0,0 +1,3 @@ +rootProject.name = "forge-1.19" + +includeBuild("../../BlueMapCommon") \ No newline at end of file diff --git a/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java new file mode 100644 index 00000000..5bc17ce9 --- /dev/null +++ b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java @@ -0,0 +1,79 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.forge; + +import com.flowpowered.math.vector.Vector3d; +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.core.world.World; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; + +import java.io.IOException; +import java.util.Optional; + +public class ForgeCommandSource implements CommandSource { + + private final ForgeMod mod; + private final Plugin plugin; + private final CommandSourceStack delegate; + + public ForgeCommandSource(ForgeMod mod, Plugin plugin, CommandSourceStack delegate) { + this.mod = mod; + this.plugin = plugin; + this.delegate = delegate; + } + + @Override + public void sendMessage(Text text) { + var component = Component.Serializer.fromJsonLenient(text.toJSONString()); + if (component != null) + delegate.sendSuccess(component, false); + } + + @Override + public boolean hasPermission(String permission) { + return delegate.hasPermission(1); + } + + @Override + public Optional getPosition() { + var pos = delegate.getPosition(); + return Optional.of(new Vector3d(pos.x, pos.y, pos.z)); + } + + @Override + public Optional getWorld() { + try { + var serverWorld = mod.getWorld(delegate.getLevel()); + String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); + return Optional.ofNullable(plugin.getWorlds().get(worldId)); + } catch (IOException ignore) {} + + return Optional.empty(); + } + +} diff --git a/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java new file mode 100644 index 00000000..ff54d23a --- /dev/null +++ b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -0,0 +1,67 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.forge; + +import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + +public class ForgeEventForwarder { + + private final Collection eventListeners; + + public ForgeEventForwarder() { + this.eventListeners = new ArrayList<>(1); + + MinecraftForge.EVENT_BUS.register(this); + } + + public synchronized void addEventListener(ServerEventListener listener) { + this.eventListeners.add(listener); + } + + public synchronized void removeAllListeners() { + this.eventListeners.clear(); + } + + @SubscribeEvent + public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) { + UUID uuid = evt.getPlayer().getUUID(); + for (ServerEventListener listener : eventListeners) listener.onPlayerJoin(uuid); + } + + @SubscribeEvent + public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { + UUID uuid = evt.getPlayer().getUUID(); + for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); + } + +} diff --git a/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java new file mode 100644 index 00000000..dd6734a3 --- /dev/null +++ b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -0,0 +1,232 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.forge; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.commands.Commands; +import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.BlueMap; +import de.bluecolored.bluemap.core.MinecraftVersion; +import de.bluecolored.bluemap.core.logger.Logger; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.TickEvent.ServerTickEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; +import net.minecraftforge.event.server.ServerStartedEvent; +import net.minecraftforge.event.server.ServerStartingEvent; +import net.minecraftforge.event.server.ServerStoppingEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.IExtensionPoint; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.network.NetworkConstants; +import org.apache.logging.log4j.LogManager; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Mod(Plugin.PLUGIN_ID) +public class ForgeMod implements ServerInterface { + + private final Plugin pluginInstance; + private MinecraftServer serverInstance = null; + + private final ForgeEventForwarder eventForwarder; + private final LoadingCache worlds; + + private int playerUpdateIndex = 0; + private final Map onlinePlayerMap; + private final List onlinePlayerList; + + public ForgeMod() { + Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME)); + + this.onlinePlayerMap = new ConcurrentHashMap<>(); + this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>()); + + this.pluginInstance = new Plugin("forge-1.19", this); + + this.eventForwarder = new ForgeEventForwarder(); + this.worlds = Caffeine.newBuilder() + .executor(BlueMap.THREAD_POOL) + .weakKeys() + .maximumSize(1000) + .build(ForgeWorld::new); + + MinecraftForge.EVENT_BUS.register(this); + + //Make sure the mod being absent on the other network side does not cause the client to display the server as incompatible + ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true)); + } + + @SubscribeEvent + public void onServerStarting(ServerStartingEvent event) { + this.serverInstance = event.getServer(); + + //register commands + new Commands<>(pluginInstance, event.getServer().getCommands().getDispatcher(), forgeSource -> + new ForgeCommandSource(this, pluginInstance, forgeSource) + ); + } + + @SubscribeEvent + public void onServerStarted(ServerStartedEvent event) { + //save worlds to generate level.dat files + serverInstance.saveAllChunks(false, true, true); + + new Thread(() -> { + Logger.global.logInfo("Loading..."); + + try { + pluginInstance.load(); + if (pluginInstance.isLoaded()) Logger.global.logInfo("Loaded!"); + } catch (IOException e) { + Logger.global.logError("Failed to load bluemap!", e); + pluginInstance.unload(); + } + }).start(); + } + + @SubscribeEvent + public void onServerStopping(ServerStoppingEvent event) { + pluginInstance.unload(); + Logger.global.logInfo("BlueMap unloaded!"); + } + + @SubscribeEvent + public void onTick(ServerTickEvent evt) { + updateSomePlayers(); + } + + @Override + public MinecraftVersion getMinecraftVersion() { + return new MinecraftVersion(1, 17, 1); + } + + @Override + public void registerListener(ServerEventListener listener) { + eventForwarder.addEventListener(listener); + } + + @Override + public void unregisterAllListeners() { + eventForwarder.removeAllListeners(); + } + + @Override + public Collection getLoadedWorlds() { + Collection loadedWorlds = new ArrayList<>(3); + for (ServerLevel serverWorld : serverInstance.getAllLevels()) { + loadedWorlds.add(worlds.get(serverWorld)); + } + return loadedWorlds; + } + + public ServerWorld getWorld(ServerLevel world) { + return worlds.get(world); + } + + @Override + public Path getConfigFolder() { + return Path.of("config", "bluemap"); + } + + @Override + public Optional getModsFolder() { + return Optional.of(Path.of("mods")); + } + + @SubscribeEvent + public void onPlayerJoin(PlayerLoggedInEvent evt) { + var playerInstance = evt.getPlayer(); + if (!(playerInstance instanceof ServerPlayer)) return; + + ForgePlayer player = new ForgePlayer(playerInstance.getUUID(), this, getPlugin().getBlueMap()); + onlinePlayerMap.put(player.getUuid(), player); + onlinePlayerList.add(player); + } + + @SubscribeEvent + public void onPlayerLeave(PlayerLoggedOutEvent evt) { + var player = evt.getPlayer(); + if (!(player instanceof ServerPlayer)) return; + + UUID playerUUID = player.getUUID(); + onlinePlayerMap.remove(playerUUID); + synchronized (onlinePlayerList) { + onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); + } + } + + public MinecraftServer getServer() { + return this.serverInstance; + } + + public Plugin getPlugin() { + return this.pluginInstance; + } + + @Override + public Collection getOnlinePlayers() { + return onlinePlayerMap.values(); + } + + @Override + public Optional getPlayer(UUID uuid) { + return Optional.ofNullable(onlinePlayerMap.get(uuid)); + } + + /** + * Only update some of the online players each tick to minimize performance impact on the server-thread. + * Only call this method on the server-thread. + */ + private void updateSomePlayers() { + int onlinePlayerCount = onlinePlayerList.size(); + if (onlinePlayerCount == 0) return; + + int playersToBeUpdated = onlinePlayerCount / 20; //with 20 tps, each player is updated once a second + if (playersToBeUpdated == 0) playersToBeUpdated = 1; + + for (int i = 0; i < playersToBeUpdated; i++) { + playerUpdateIndex++; + if (playerUpdateIndex >= 20 && playerUpdateIndex >= onlinePlayerCount) playerUpdateIndex = 0; + + if (playerUpdateIndex < onlinePlayerCount) { + onlinePlayerList.get(playerUpdateIndex).update(); + } + } + } + +} diff --git a/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java new file mode 100644 index 00000000..01394d9b --- /dev/null +++ b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -0,0 +1,158 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.forge; + +import com.flowpowered.math.vector.Vector3d; +import de.bluecolored.bluemap.common.BlueMapService; +import de.bluecolored.bluemap.common.serverinterface.Gamemode; +import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.plugin.text.Text; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.level.GameType; +import net.minecraft.world.phys.Vec3; + +import java.io.IOException; +import java.util.EnumMap; +import java.util.Map; +import java.util.UUID; + +public class ForgePlayer implements Player { + + private static final Map GAMEMODE_MAP = new EnumMap<>(GameType.class); + static { + GAMEMODE_MAP.put(GameType.ADVENTURE, Gamemode.ADVENTURE); + GAMEMODE_MAP.put(GameType.SURVIVAL, Gamemode.SURVIVAL); + GAMEMODE_MAP.put(GameType.CREATIVE, Gamemode.CREATIVE); + GAMEMODE_MAP.put(GameType.SPECTATOR, Gamemode.SPECTATOR); + } + + private final UUID uuid; + private Text name; + private String world; + private Vector3d position; + private Vector3d rotation; + private boolean online; + private boolean sneaking; + private boolean invisible; + private Gamemode gamemode; + + private final ForgeMod mod; + private final BlueMapService blueMap; + + public ForgePlayer(UUID playerUuid, ForgeMod mod, BlueMapService blueMap) { + this.uuid = playerUuid; + this.mod = mod; + this.blueMap = blueMap; + + update(); + } + + @Override + public UUID getUuid() { + return this.uuid; + } + + @Override + public Text getName() { + return this.name; + } + + @Override + public String getWorld() { + return this.world; + } + + @Override + public Vector3d getPosition() { + return this.position; + } + + @Override + public Vector3d getRotation() { + return rotation; + } + + @Override + public boolean isOnline() { + return this.online; + } + + @Override + public boolean isSneaking() { + return this.sneaking; + } + + @Override + public boolean isInvisible() { + return this.invisible; + } + + @Override + public Gamemode getGamemode() { + return this.gamemode; + } + + /** + * Only call on server thread! + */ + public void update() { + MinecraftServer server = mod.getServer(); + if (server == null) { + this.online = false; + return; + } + + ServerPlayer player = server.getPlayerList().getPlayer(uuid); + if (player == null) { + this.online = false; + return; + } + + this.gamemode = GAMEMODE_MAP.getOrDefault(player.gameMode.getGameModeForPlayer(), Gamemode.SURVIVAL); + if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; + + MobEffectInstance invis = player.getEffect(MobEffects.INVISIBILITY); + this.invisible = invis != null && invis.getDuration() > 0; + + this.name = Text.of(player.getName().getString()); + this.online = true; + + Vec3 pos = player.getPosition(1f); + this.position = new Vector3d(pos.x(), pos.y(), pos.z()); + this.rotation = new Vector3d(player.getXRot(), player.getYHeadRot(), 0); + this.sneaking = player.isCrouching(); + + try { + var world = mod.getWorld(player.getLevel()); + this.world = blueMap.getWorldId(world.getSaveFolder()); + } catch (IOException e) { + this.world = "unknown"; + } + } + +} diff --git a/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java new file mode 100644 index 00000000..ce92b260 --- /dev/null +++ b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java @@ -0,0 +1,100 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.forge; + +import de.bluecolored.bluemap.common.serverinterface.Dimension; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.LevelResource; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; + +public class ForgeWorld implements ServerWorld { + + private final WeakReference delegate; + private final Path saveFolder; + + public ForgeWorld(ServerLevel delegate) { + this.delegate = new WeakReference<>(delegate); + + MinecraftServer server = delegate.getServer(); + Path worldFolder = delegate.getServer().getServerDirectory().toPath().resolve(server.getWorldPath(LevelResource.ROOT)); + this.saveFolder = DimensionType.getStorageFolder(delegate.dimension(), worldFolder) + .toAbsolutePath().normalize(); + } + + @Override + public Dimension getDimension() { + ServerLevel world = delegate.get(); + if (world != null) { + if (world.dimension().equals(Level.NETHER)) return Dimension.NETHER; + if (world.dimension().equals(Level.END)) return Dimension.END; + if (world.dimension().equals(Level.OVERWORLD)) return Dimension.OVERWORLD; + } + + return ServerWorld.super.getDimension(); + } + + @Override + public boolean persistWorldChanges() throws IOException { + ServerLevel world = delegate.get(); + if (world == null) return false; + + var taskResult = CompletableFuture.supplyAsync(() -> { + try { + world.save(null, true, false); + return true; + } catch (Exception e) { + throw new CompletionException(e); + } + }, world.getServer()); + + try { + return taskResult.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) throw (IOException) t; + if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t; + throw new IOException(t); + } + } + + @Override + public Path getSaveFolder() { + return this.saveFolder; + } + +} diff --git a/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java new file mode 100644 index 00000000..fa6664ec --- /dev/null +++ b/implementations/forge-1.19/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java @@ -0,0 +1,69 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.forge; + +import org.apache.logging.log4j.Logger; + +import de.bluecolored.bluemap.core.logger.AbstractLogger; + +public class Log4jLogger extends AbstractLogger { + + private Logger out; + + public Log4jLogger(Logger out) { + this.out = out; + } + + @Override + public void logError(String message, Throwable throwable) { + out.error(message, throwable); + } + + @Override + public void logWarning(String message) { + out.warn(message); + } + + @Override + public void logInfo(String message) { + out.info(message); + } + + @Override + public void logDebug(String message) { + if (out.isDebugEnabled()) out.debug(message); + } + + @Override + public void noFloodDebug(String message) { + if (out.isDebugEnabled()) super.noFloodDebug(message); + } + + @Override + public void noFloodDebug(String key, String message) { + if (out.isDebugEnabled()) super.noFloodDebug(key, message); + } + +} diff --git a/implementations/forge-1.19/src/main/resources/META-INF/mods.toml b/implementations/forge-1.19/src/main/resources/META-INF/mods.toml new file mode 100644 index 00000000..be1b4710 --- /dev/null +++ b/implementations/forge-1.19/src/main/resources/META-INF/mods.toml @@ -0,0 +1,26 @@ +modLoader="javafml" +loaderVersion="[41,)" +license="MIT" +issueTrackerURL="https://github.com/BlueMap-Minecraft/BlueMap/issues" +[[mods]] +modId="bluemap" +version="${version}" +displayName="BlueMap" +displayURL="https://github.com/BlueMap-Minecraft/BlueMap" +authors="Blue (TBlueF, Lukas Rieger)" +description=''' +A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL) +''' + +[[dependencies.bluemap]] + modId="forge" + mandatory=true + versionRange="[41,)" + ordering="NONE" + side="SERVER" +[[dependencies.bluemap]] + modId="minecraft" + mandatory=true + versionRange="[1.19,)" + ordering="NONE" + side="SERVER" diff --git a/implementations/forge-1.19/src/main/resources/pack.mcmeta b/implementations/forge-1.19/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..0f95ce1c --- /dev/null +++ b/implementations/forge-1.19/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "BlueMap - A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)", + "pack_format": 8 + } +} diff --git a/implementations/spigot/gradle/wrapper/gradle-wrapper.properties b/implementations/spigot/gradle/wrapper/gradle-wrapper.properties index 69a97150..41dfb879 100644 --- a/implementations/spigot/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/spigot/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java index 22428e11..9e9e6f8c 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java @@ -53,6 +53,7 @@ public class BukkitPlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -84,6 +85,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -136,6 +142,7 @@ public void update() { Location location = player.getLocation(); this.position = new Vector3d(location.getX(), location.getY(), location.getZ()); + this.rotation = new Vector3d(location.getPitch(), location.getYaw(), 0); this.sneaking = player.isSneaking(); try { diff --git a/implementations/sponge-8.0.0/gradle/wrapper/gradle-wrapper.properties b/implementations/sponge-8.0.0/gradle/wrapper/gradle-wrapper.properties index 69a97150..41dfb879 100644 --- a/implementations/sponge-8.0.0/gradle/wrapper/gradle-wrapper.properties +++ b/implementations/sponge-8.0.0/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/implementations/sponge-8.0.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java b/implementations/sponge-8.0.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java index d29234c6..81861ddd 100644 --- a/implementations/sponge-8.0.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java +++ b/implementations/sponge-8.0.0/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java @@ -55,6 +55,7 @@ public class SpongePlayer implements Player { private Text name; private String world; private Vector3d position; + private Vector3d rotation; private boolean online; private boolean sneaking; private boolean invisible; @@ -86,6 +87,11 @@ public Vector3d getPosition() { return this.position; } + @Override + public Vector3d getRotation() { + return rotation; + } + @Override public boolean isOnline() { return this.online; @@ -138,6 +144,7 @@ public void update() { this.name = Text.of(player.name()); this.online = player.isOnline(); this.position = SpongePlugin.fromSpongePoweredVector(player.position()); + this.rotation = SpongePlugin.fromSpongePoweredVector(player.rotation()); this.sneaking = player.get(Keys.IS_SNEAKING).orElse(false); try { diff --git a/settings.gradle.kts b/settings.gradle.kts index a80fd27f..c3770835 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ includeBuild("implementations/forge-1.15.2") includeBuild("implementations/forge-1.16.2") includeBuild("implementations/forge-1.17.1") includeBuild("implementations/forge-1.18.1") +includeBuild("implementations/forge-1.19") includeBuild("implementations/spigot")