From 484e4d0779c37b9bd0cce63b30e825d531bc8f34 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Thu, 2 Jul 2020 13:08:47 +0200 Subject: [PATCH 01/10] First changes towards live updates --- .../bluemap/bukkit/BukkitPlugin.java | 16 ++++ .../bluecolored/bluemap/cli/BlueMapCLI.java | 2 +- .../bluemap/common}/BlueMapWebServer.java | 7 +- .../bluemap/common/live/Event.java | 73 +++++++++++++++++++ .../common/live/LiveAPIRequestHandler.java | 62 ++++++++++++++++ .../common/plugin/MapUpdateHandler.java | 22 ++++++ .../bluemap/common/plugin/Plugin.java | 2 +- .../plugin/serverinterface/Gamemode.java | 34 +++++++++ .../serverinterface/PlayerInterface.java | 58 +++++++++++++++ .../serverinterface/ServerEventListener.java | 10 +++ .../serverinterface/ServerInterface.java | 13 ++++ ...stHandler.java => FileRequestHandler.java} | 28 ++----- .../bluemap/core/webserver/HttpRequest.java | 49 +++++++++++-- .../core/webserver/HttpRequestHandler.java | 1 + .../bluecolored/bluemap/forge/ForgeMod.java | 15 ++++ .../bluemap/sponge/SpongePlugin.java | 15 ++++ 16 files changed, 376 insertions(+), 31 deletions(-) rename {BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web => BlueMapCommon/src/main/java/de/bluecolored/bluemap/common}/BlueMapWebServer.java (87%) create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerInterface.java rename BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/{BlueMapWebRequestHandler.java => FileRequestHandler.java} (91%) diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index 3c3637b2..30e8f4a0 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -27,6 +27,9 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -40,6 +43,7 @@ import org.bukkit.plugin.java.JavaPlugin; import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -178,5 +182,17 @@ public Plugin getBlueMap() { public static BukkitPlugin getInstance() { return instance; } + + @Override + public Collection getOnlinePlayers() { + // TODO Implement + return Collections.emptyList(); + } + + @Override + public Optional getPlayer(UUID uuid) { + // TODO Implement + return Optional.empty(); + } } diff --git a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java index 93506a45..d3d9f5a4 100644 --- a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java +++ b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java @@ -57,6 +57,7 @@ import com.flowpowered.math.vector.Vector2i; import com.google.common.base.Preconditions; +import de.bluecolored.bluemap.common.BlueMapWebServer; import de.bluecolored.bluemap.common.MapType; import de.bluecolored.bluemap.common.RenderManager; import de.bluecolored.bluemap.common.RenderTask; @@ -72,7 +73,6 @@ import de.bluecolored.bluemap.core.render.lowres.LowresModelManager; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.resourcepack.ResourcePack; -import de.bluecolored.bluemap.core.web.BlueMapWebServer; import de.bluecolored.bluemap.core.web.WebFilesManager; import de.bluecolored.bluemap.core.web.WebSettings; import de.bluecolored.bluemap.core.world.SlicedWorld; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/BlueMapWebServer.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java similarity index 87% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/BlueMapWebServer.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java index 79db02cc..015b88ac 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/BlueMapWebServer.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java @@ -22,11 +22,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.web; +package de.bluecolored.bluemap.common; import java.io.IOException; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.web.FileRequestHandler; +import de.bluecolored.bluemap.core.web.WebFilesManager; +import de.bluecolored.bluemap.core.web.WebServerConfig; import de.bluecolored.bluemap.core.webserver.WebServer; public class BlueMapWebServer extends WebServer { @@ -38,7 +41,7 @@ public BlueMapWebServer(WebServerConfig config) { config.getWebserverPort(), config.getWebserverMaxConnections(), config.getWebserverBindAdress(), - new BlueMapWebRequestHandler(config.getWebRoot()) + new FileRequestHandler(config.getWebRoot(), "BlueMap/Webserver") ); this.webFilesManager = new WebFilesManager(config.getWebRoot()); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java new file mode 100644 index 00000000..3e509014 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java @@ -0,0 +1,73 @@ +/* + * 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 java.io.IOException; + +import com.google.gson.stream.JsonWriter; + +public class Event { + + private String type; + private long time; + private long expires; + private String jsonData; + + public Event(String type, long time, long expires, String jsonData) { + this.type = type; + this.time = time; + this.expires = expires; + this.jsonData = jsonData; + } + + public void serialize(JsonWriter writer) throws IOException { + writer.beginObject(); + + writer.name("type").value(type); + writer.name("time").value(time); + writer.name("jsonData").jsonValue(jsonData); + + writer.endObject(); + } + + public String getType() { + return type; + } + + public long getTime() { + return time; + } + + public long getExpireTime() { + return expires; + } + + public String getJsonData() { + return jsonData; + } + + + +} 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 new file mode 100644 index 00000000..755f4e35 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java @@ -0,0 +1,62 @@ +/* + * 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 java.util.HashMap; +import java.util.Map; + +import de.bluecolored.bluemap.core.webserver.HttpRequest; +import de.bluecolored.bluemap.core.webserver.HttpRequestHandler; +import de.bluecolored.bluemap.core.webserver.HttpResponse; +import de.bluecolored.bluemap.core.webserver.HttpStatusCode; + +public class LiveAPIRequestHandler implements HttpRequestHandler { + + private HttpRequestHandler notFoundHandler; + private Map liveAPIRequests; + + public LiveAPIRequestHandler(HttpRequestHandler notFoundHandler) { + this.notFoundHandler = notFoundHandler; + + this.liveAPIRequests = new HashMap<>(); + + this.liveAPIRequests.put("live/events", this::handleEventsRequest); + } + + @Override + public HttpResponse handle(HttpRequest request) { + HttpRequestHandler handler = liveAPIRequests.get(request.getPath()); + if (handler != null) return handler.handle(request); + + return this.notFoundHandler.handle(request); + } + + public HttpResponse handleEventsRequest(HttpRequest request) { + if (!request.getMethod().equalsIgnoreCase("GET")) return new HttpResponse(HttpStatusCode.BAD_REQUEST); + + return new HttpResponse(HttpStatusCode.NOT_IMPLEMENTED); + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java index 8f8b9925..f315298a 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java @@ -34,7 +34,9 @@ import de.bluecolored.bluemap.common.MapType; import de.bluecolored.bluemap.common.RenderManager; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.plugin.text.Text; public class MapUpdateHandler implements ServerEventListener { @@ -131,5 +133,25 @@ public void flushTileBuffer() { updateBuffer.clear(); } } + + @Override + public void onPlayerJoin(UUID playerUuid) { + + } + + @Override + public void onPlayerLeave(UUID playerUuid) { + + } + + @Override + public void onPlayerMove(UUID playerUuid) { + + } + + @Override + public void onChatMessage(Text message) { + + } } 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 7c0ef346..160b1efa 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 @@ -46,6 +46,7 @@ import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.common.BlueMapWebServer; import de.bluecolored.bluemap.common.MapType; import de.bluecolored.bluemap.common.RenderManager; import de.bluecolored.bluemap.common.api.BlueMapAPIImpl; @@ -62,7 +63,6 @@ import de.bluecolored.bluemap.core.render.lowres.LowresModelManager; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.resourcepack.ResourcePack; -import de.bluecolored.bluemap.core.web.BlueMapWebServer; import de.bluecolored.bluemap.core.web.WebFilesManager; import de.bluecolored.bluemap.core.web.WebSettings; import de.bluecolored.bluemap.core.world.SlicedWorld; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java new file mode 100644 index 00000000..9012e4b9 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java @@ -0,0 +1,34 @@ +/* + * 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.plugin.serverinterface; + +public enum Gamemode { + + SURVIVAL, + CREATIVE, + ADVENTURE, + SPECTATOR + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerInterface.java new file mode 100644 index 00000000..a7f1b8ee --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerInterface.java @@ -0,0 +1,58 @@ +/* + * 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.plugin.serverinterface; + +import java.util.UUID; + +import de.bluecolored.bluemap.common.plugin.text.Text; + +public interface PlayerInterface extends CommandSource { + + UUID getUuid(); + + Text getName(); + + boolean isOnline(); + + /** + * Return true if the player is sneaking. + *

If the player is offline the value of this method is undetermined.

+ * @return + */ + boolean isSneaking(); + + /** + * Returns true if the player has an invisibillity effect + *

If the player is offline the value of this method is undetermined.

+ */ + boolean isInvisible(); + + /** + * Returns the {@link Gamemode} this player is in + *

If the player is offline the value of this method is undetermined.

+ */ + Gamemode getGamemode(); + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java index 85c3edad..eee4f028 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java @@ -29,6 +29,8 @@ import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; +import de.bluecolored.bluemap.common.plugin.text.Text; + public interface ServerEventListener { void onWorldSaveToDisk(UUID world); @@ -37,4 +39,12 @@ public interface ServerEventListener { void onChunkFinishedGeneration(UUID world, Vector2i chunkPos); + void onPlayerJoin(UUID playerUuid); + + void onPlayerLeave(UUID playerUuid); + + void onPlayerMove(UUID playerUuid); + + void onChatMessage(Text message); + } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java index 73327761..7a06fbcd 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java @@ -26,6 +26,8 @@ import java.io.File; import java.io.IOException; +import java.util.Collection; +import java.util.Optional; import java.util.UUID; public interface ServerInterface { @@ -73,5 +75,16 @@ default boolean isMetricsEnabled(boolean configValue) { return configValue; } + /** + * Returns a collection of players that are currently online + */ + Collection getOnlinePlayers(); + + /** + * Returns the player with that UUID if present
+ * this method is only guaranteed to return a player if the player is currently online. + */ + Optional getPlayer(UUID uuid); + } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/BlueMapWebRequestHandler.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java similarity index 91% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/BlueMapWebRequestHandler.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java index 28116e61..ca439667 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/BlueMapWebRequestHandler.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java @@ -50,16 +50,18 @@ import de.bluecolored.bluemap.core.webserver.HttpResponse; import de.bluecolored.bluemap.core.webserver.HttpStatusCode; -public class BlueMapWebRequestHandler implements HttpRequestHandler { +public class FileRequestHandler implements HttpRequestHandler { private static final long DEFLATE_MIN_SIZE = 10L * 1024L; private static final long DEFLATE_MAX_SIZE = 10L * 1024L * 1024L; private static final long INFLATE_MAX_SIZE = 10L * 1024L * 1024L; private Path webRoot; + private String serverName; - public BlueMapWebRequestHandler(Path webRoot) { + public FileRequestHandler(Path webRoot, String serverName) { this.webRoot = webRoot; + this.serverName = serverName; } @Override @@ -70,11 +72,11 @@ public HttpResponse handle(HttpRequest request) { ) return new HttpResponse(HttpStatusCode.NOT_IMPLEMENTED); HttpResponse response = generateResponse(request); - response.addHeader("Server", "BlueMap/WebServer"); + response.addHeader("Server", this.serverName); HttpStatusCode status = response.getStatusCode(); if (status.getCode() >= 400){ - response.setData(status.getCode() + " - " + status.getMessage() + "\nBlueMap/Webserver"); + response.setData(status.getCode() + " - " + status.getMessage() + "\n" + this.serverName); } return response; @@ -82,23 +84,7 @@ public HttpResponse handle(HttpRequest request) { @SuppressWarnings ("resource") private HttpResponse generateResponse(HttpRequest request) { - String adress = request.getPath(); - if (adress.isEmpty()) adress = "/"; - String[] adressParts = adress.split("\\?", 2); - String path = adressParts[0]; - String getParamString = adressParts.length > 1 ? adressParts[1] : ""; - - Map getParams = new HashMap<>(); - for (String getParam : getParamString.split("&")){ - if (getParam.isEmpty()) continue; - String[] kv = getParam.split("=", 2); - String key = kv[0]; - String value = kv.length > 1 ? kv[1] : ""; - getParams.put(key, value); - } - - if (path.startsWith("/")) path = path.substring(1); - if (path.endsWith("/")) path = path.substring(0, path.length() - 1); + String path = request.getPath(); Path filePath = webRoot; try { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequest.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequest.java index a8597c51..9142546b 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequest.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequest.java @@ -50,15 +50,18 @@ public class HttpRequest { private static final Pattern REQUEST_PATTERN = Pattern.compile("^(\\w+) (\\S+) (.+)$"); private String method; - private String path; + private String adress; private String version; private Map> header; private Map> headerLC; private byte[] data; - public HttpRequest(String method, String path, String version, Map> header) { + private String path = null; + private Map getParams = null; + + public HttpRequest(String method, String adress, String version, Map> header) { this.method = method; - this.path = path; + this.adress = adress; this.version = version; this.header = header; this.headerLC = new HashMap<>(); @@ -79,8 +82,8 @@ public String getMethod() { return method; } - public String getPath(){ - return path; + public String getAdress(){ + return adress; } public String getVersion() { @@ -92,7 +95,7 @@ public Map> getHeader() { } public Map> getLowercaseHeader() { - return header; + return headerLC; } public Set getHeader(String key){ @@ -107,6 +110,40 @@ public Set getLowercaseHeader(String key){ return headerValues; } + public String getPath() { + if (path == null) parseAdress(); + return path; + } + + public Map getGETParams() { + if (getParams == null) parseAdress(); + return Collections.unmodifiableMap(getParams); + } + + private void parseAdress() { + String adress = this.adress; + if (adress.isEmpty()) adress = "/"; + String[] adressParts = adress.split("\\?", 2); + String path = adressParts[0]; + String getParamString = adressParts.length > 1 ? adressParts[1] : ""; + + Map getParams = new HashMap<>(); + for (String getParam : getParamString.split("&")){ + if (getParam.isEmpty()) continue; + String[] kv = getParam.split("=", 2); + String key = kv[0]; + String value = kv.length > 1 ? kv[1] : ""; + getParams.put(key, value); + } + + //normalize path + if (path.startsWith("/")) path = path.substring(1); + if (path.endsWith("/")) path = path.substring(0, path.length() - 1); + + this.path = path; + this.getParams = getParams; + } + public InputStream getData(){ return new ByteArrayInputStream(data); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequestHandler.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequestHandler.java index 23f887ef..e5ce2f31 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequestHandler.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpRequestHandler.java @@ -24,6 +24,7 @@ */ package de.bluecolored.bluemap.core.webserver; +@FunctionalInterface public interface HttpRequestHandler { HttpResponse handle(HttpRequest request); diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 66cb7a9c..dd9223e1 100644 --- a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -28,8 +28,10 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.UUID; import org.apache.logging.log4j.LogManager; @@ -38,6 +40,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -207,5 +210,17 @@ public File getConfigFolder() { public Commands getCommands() { return commands; } + + @Override + public Collection getOnlinePlayers() { + // TODO Implement + return Collections.emptyList(); + } + + @Override + public Optional getPlayer(UUID uuid) { + // TODO Implement + return Optional.empty(); + } } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index a6fc6ee3..b771e6d2 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -27,6 +27,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.Collection; +import java.util.Collections; import java.util.Optional; import java.util.UUID; @@ -44,6 +46,7 @@ import org.spongepowered.api.world.storage.WorldProperties; import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -160,5 +163,17 @@ public String getWorldName(UUID worldUUID) { public File getConfigFolder() { return configurationDir.toFile(); } + + @Override + public Collection getOnlinePlayers() { + // TODO Implement + return Collections.emptyList(); + } + + @Override + public Optional getPlayer(UUID uuid) { + // TODO Implement + return Optional.empty(); + } } From 187649a613552336c5983f25c586cac2ccc63821 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Fri, 31 Jul 2020 00:42:10 +0200 Subject: [PATCH 02/10] Implement player live-api on sponge --- .../bluemap/bukkit/BukkitPlugin.java | 6 +- .../bluemap/common/BlueMapWebServer.java | 14 ++++ .../bluemap/common/live/Event.java | 73 ------------------ .../common/live/LiveAPIRequestHandler.java | 51 ++++++++++++- .../common/plugin/MapUpdateHandler.java | 6 -- .../bluemap/common/plugin/Plugin.java | 2 +- ...{PlayerInterface.java => PlayerState.java} | 67 ++++++++++++++-- .../serverinterface/ServerEventListener.java | 2 - .../serverinterface/ServerInterface.java | 4 +- .../bluecolored/bluemap/forge/ForgeMod.java | 6 +- .../bluemap/sponge/EventForwarder.java | 18 +++++ .../bluemap/sponge/SpongePlayerState.java | 76 +++++++++++++++++++ .../bluemap/sponge/SpongePlugin.java | 64 +++++++++++++--- 13 files changed, 279 insertions(+), 110 deletions(-) delete mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java rename BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/{PlayerInterface.java => PlayerState.java} (59%) create mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index 30e8f4a0..7794fd44 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -43,7 +43,7 @@ import org.bukkit.plugin.java.JavaPlugin; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -184,13 +184,13 @@ public static BukkitPlugin getInstance() { } @Override - public Collection getOnlinePlayers() { + public Collection getOnlinePlayers() { // TODO Implement return Collections.emptyList(); } @Override - public Optional getPlayer(UUID uuid) { + public Optional getPlayer(UUID uuid) { // TODO Implement return Optional.empty(); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java index 015b88ac..e51dd030 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java @@ -26,6 +26,8 @@ import java.io.IOException; +import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.web.FileRequestHandler; import de.bluecolored.bluemap.core.web.WebFilesManager; @@ -36,6 +38,7 @@ public class BlueMapWebServer extends WebServer { private WebFilesManager webFilesManager; + public BlueMapWebServer(WebServerConfig config) { super( config.getWebserverPort(), @@ -47,6 +50,17 @@ public BlueMapWebServer(WebServerConfig config) { this.webFilesManager = new WebFilesManager(config.getWebRoot()); } + public BlueMapWebServer(WebServerConfig config, ServerInterface server) { + super( + config.getWebserverPort(), + config.getWebserverMaxConnections(), + config.getWebserverBindAdress(), + new LiveAPIRequestHandler(server, new FileRequestHandler(config.getWebRoot(), "BlueMap/Webserver")) + ); + + this.webFilesManager = new WebFilesManager(config.getWebRoot()); + } + public void updateWebfiles() throws IOException { if (webFilesManager.needsUpdate()) { Logger.global.logInfo("Webfiles are missing or outdated, updating..."); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java deleted file mode 100644 index 3e509014..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/Event.java +++ /dev/null @@ -1,73 +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 java.io.IOException; - -import com.google.gson.stream.JsonWriter; - -public class Event { - - private String type; - private long time; - private long expires; - private String jsonData; - - public Event(String type, long time, long expires, String jsonData) { - this.type = type; - this.time = time; - this.expires = expires; - this.jsonData = jsonData; - } - - public void serialize(JsonWriter writer) throws IOException { - writer.beginObject(); - - writer.name("type").value(type); - writer.name("time").value(time); - writer.name("jsonData").jsonValue(jsonData); - - writer.endObject(); - } - - public String getType() { - return type; - } - - public long getTime() { - return time; - } - - public long getExpireTime() { - return expires; - } - - public String getJsonData() { - return jsonData; - } - - - -} 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 index 755f4e35..7ca7c0f6 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java @@ -24,9 +24,16 @@ */ package de.bluecolored.bluemap.common.live; +import java.io.IOException; +import java.io.StringWriter; import java.util.HashMap; import java.util.Map; +import com.google.gson.stream.JsonWriter; + +import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.webserver.HttpRequest; import de.bluecolored.bluemap.core.webserver.HttpRequestHandler; import de.bluecolored.bluemap.core.webserver.HttpResponse; @@ -36,13 +43,15 @@ public class LiveAPIRequestHandler implements HttpRequestHandler { private HttpRequestHandler notFoundHandler; private Map liveAPIRequests; + private ServerInterface server; - public LiveAPIRequestHandler(HttpRequestHandler notFoundHandler) { + public LiveAPIRequestHandler(ServerInterface server, HttpRequestHandler notFoundHandler) { + this.server = server; this.notFoundHandler = notFoundHandler; this.liveAPIRequests = new HashMap<>(); - this.liveAPIRequests.put("live/events", this::handleEventsRequest); + this.liveAPIRequests.put("live/players", this::handlePlayersRequest); } @Override @@ -53,10 +62,44 @@ public HttpResponse handle(HttpRequest request) { return this.notFoundHandler.handle(request); } - public HttpResponse handleEventsRequest(HttpRequest request) { + public HttpResponse handlePlayersRequest(HttpRequest request) { if (!request.getMethod().equalsIgnoreCase("GET")) return new HttpResponse(HttpStatusCode.BAD_REQUEST); - return new HttpResponse(HttpStatusCode.NOT_IMPLEMENTED); + try ( + StringWriter data = new StringWriter(); + JsonWriter json = new JsonWriter(data); + ){ + + json.beginObject(); + json.name("players").beginArray(); + for (PlayerState player : server.getOnlinePlayers()) { + + if (player.isInvisible()) continue; + if (player.isSneaking()) continue; + if (player.getGamemode() == Gamemode.SPECTATOR) 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/plugin/MapUpdateHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java index f315298a..eb478b30 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java @@ -34,7 +34,6 @@ import de.bluecolored.bluemap.common.MapType; import de.bluecolored.bluemap.common.RenderManager; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.text.Text; @@ -142,11 +141,6 @@ public void onPlayerJoin(UUID playerUuid) { @Override public void onPlayerLeave(UUID playerUuid) { - } - - @Override - public void onPlayerMove(UUID playerUuid) { - } @Override 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 160b1efa..c7c092c8 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 @@ -297,7 +297,7 @@ public synchronized void load() throws IOException, ParseResourceException { //start webserver if (config.isWebserverEnabled()) { - webServer = new BlueMapWebServer(config); + webServer = new BlueMapWebServer(config, serverInterface); webServer.updateWebfiles(); webServer.start(); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerState.java similarity index 59% rename from BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerInterface.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerState.java index a7f1b8ee..6af4cd8e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerInterface.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerState.java @@ -26,33 +26,86 @@ import java.util.UUID; +import com.flowpowered.math.vector.Vector3d; +import com.google.common.base.MoreObjects; + import de.bluecolored.bluemap.common.plugin.text.Text; -public interface PlayerInterface extends CommandSource { +public class PlayerState { - UUID getUuid(); + private final UUID uuid; + protected Text name; + protected UUID world; + protected Vector3d position = Vector3d.ZERO; + protected boolean online = false; + protected boolean sneaking = false; + protected boolean invisible = false; + protected Gamemode gamemode = Gamemode.SURVIVAL; - Text getName(); + public PlayerState(UUID uuid, Text name, UUID world, Vector3d position) { + this.uuid = uuid; + this.name = name; + this.world = world; + this.position = position; + } + + public UUID getUuid() { + return uuid; + } + + public Text getName() { + return name; + } + + public UUID getWorld() { + return world; + } + + public Vector3d getPosition() { + return position; + } - boolean isOnline(); + public boolean isOnline() { + return online; + } /** * Return true if the player is sneaking. *

If the player is offline the value of this method is undetermined.

* @return */ - boolean isSneaking(); + public boolean isSneaking() { + return sneaking; + } /** * Returns true if the player has an invisibillity effect *

If the player is offline the value of this method is undetermined.

*/ - boolean isInvisible(); + public boolean isInvisible() { + return invisible; + } /** * Returns the {@link Gamemode} this player is in *

If the player is offline the value of this method is undetermined.

*/ - Gamemode getGamemode(); + public Gamemode getGamemode() { + return gamemode; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("uuid", uuid) + //.add("name", name) + //.add("world", world) + //.add("position", position) + //.add("online", online) + //.add("sneaking", sneaking) + //.add("invisible", invisible) + //.add("gamemode", gamemode) + .toString(); + } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java index eee4f028..aa530ae1 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java @@ -43,8 +43,6 @@ public interface ServerEventListener { void onPlayerLeave(UUID playerUuid); - void onPlayerMove(UUID playerUuid); - void onChatMessage(Text message); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java index 7a06fbcd..eab11097 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java @@ -78,13 +78,13 @@ default boolean isMetricsEnabled(boolean configValue) { /** * Returns a collection of players that are currently online */ - Collection getOnlinePlayers(); + Collection getOnlinePlayers(); /** * Returns the player with that UUID if present
* this method is only guaranteed to return a player if the player is currently online. */ - Optional getPlayer(UUID uuid); + Optional getPlayer(UUID uuid); } diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index dd9223e1..67d5d0fd 100644 --- a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -40,7 +40,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -212,13 +212,13 @@ public Commands getCommands() { } @Override - public Collection getOnlinePlayers() { + public Collection getOnlinePlayers() { // TODO Implement return Collections.emptyList(); } @Override - public Optional getPlayer(UUID uuid) { + public Optional getPlayer(UUID uuid) { // TODO Implement return Optional.empty(); } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java index 06bd3022..5b58ba2f 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java @@ -32,6 +32,8 @@ import org.spongepowered.api.event.Order; import org.spongepowered.api.event.block.ChangeBlockEvent; import org.spongepowered.api.event.filter.type.Exclude; +import org.spongepowered.api.event.message.MessageChannelEvent; +import org.spongepowered.api.event.network.ClientConnectionEvent; import org.spongepowered.api.event.world.SaveWorldEvent; import org.spongepowered.api.event.world.chunk.PopulateChunkEvent; import org.spongepowered.api.world.Location; @@ -40,6 +42,7 @@ import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.plugin.text.Text; public class EventForwarder { @@ -73,4 +76,19 @@ public void onChunkFinishedGeneration(PopulateChunkEvent.Post evt) { listener.onChunkFinishedGeneration(evt.getTargetChunk().getWorld().getUniqueId(), new Vector2i(chunkPos.getX(), chunkPos.getZ())); } + @Listener(order = Order.POST) + public void onPlayerJoin(ClientConnectionEvent.Join evt) { + listener.onPlayerJoin(evt.getTargetEntity().getUniqueId()); + } + + @Listener(order = Order.POST) + public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) { + listener.onPlayerJoin(evt.getTargetEntity().getUniqueId()); + } + + @Listener(order = Order.POST) + public void onPlayerChat(MessageChannelEvent.Chat evt) { + listener.onChatMessage(Text.of(evt.getMessage().toPlain())); + } + } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java new file mode 100644 index 00000000..3ca5b7c9 --- /dev/null +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java @@ -0,0 +1,76 @@ +/* + * 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.sponge; + +import java.lang.ref.WeakReference; + +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.gamemode.GameMode; +import org.spongepowered.api.entity.living.player.gamemode.GameModes; + +import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; +import de.bluecolored.bluemap.common.plugin.text.Text; + +public class SpongePlayerState extends PlayerState { + + private WeakReference playerRef; + + public SpongePlayerState(Player player) { + super(player.getUniqueId(), Text.of(player.getName()), player.getWorld().getUniqueId(), player.getPosition()); + this.playerRef = new WeakReference(player); + + update(); + } + + void update() { + Player player = playerRef.get(); + if (player == null) { + this.online = false; + return; + } + + this.online = player.isOnline(); + + if (this.online) { + this.name = Text.of(player.getName()); + + this.world = player.getWorld().getUniqueId(); + this.position = player.getPosition(); + + GameMode gm = player.get(Keys.GAME_MODE).orElse(GameModes.NOT_SET); + if (gm == GameModes.SURVIVAL) this.gamemode = Gamemode.SURVIVAL; + else if (gm == GameModes.CREATIVE) this.gamemode = Gamemode.CREATIVE; + else if (gm == GameModes.SPECTATOR) this.gamemode = Gamemode.SPECTATOR; + else if (gm == GameModes.ADVENTURE) this.gamemode = Gamemode.ADVENTURE; + else this.gamemode = Gamemode.SURVIVAL; + + this.invisible = player.get(Keys.INVISIBLE).orElse(false); + this.sneaking = player.get(Keys.IS_SNEAKING).orElse(false); + } + } + +} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index feaf3258..11f1efd1 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -28,9 +28,13 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Collection; -import java.util.Collections; +import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import javax.inject.Inject; @@ -41,6 +45,7 @@ import org.spongepowered.api.event.game.GameReloadEvent; import org.spongepowered.api.event.game.state.GameStartingServerEvent; import org.spongepowered.api.event.game.state.GameStoppingEvent; +import org.spongepowered.api.event.network.ClientConnectionEvent; import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.scheduler.SpongeExecutorService; import org.spongepowered.api.util.Tristate; @@ -48,7 +53,7 @@ import org.spongepowered.api.world.storage.WorldProperties; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerInterface; +import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -74,13 +79,19 @@ public class SpongePlugin implements ServerInterface { private Plugin bluemap; private SpongeCommands commands; - + + private SpongeExecutorService syncExecutor; private SpongeExecutorService asyncExecutor; + private long lastPlayerUpdate = -1; + private Map onlinePlayers; + @Inject public SpongePlugin(org.slf4j.Logger logger) { Logger.global = new Slf4jLogger(logger); + this.onlinePlayers = new ConcurrentHashMap<>(); + this.bluemap = new Plugin("sponge", this); this.commands = new SpongeCommands(bluemap); } @@ -88,6 +99,7 @@ public SpongePlugin(org.slf4j.Logger logger) { @Listener public void onServerStart(GameStartingServerEvent evt) { asyncExecutor = Sponge.getScheduler().createAsyncExecutor(this); + syncExecutor = Sponge.getScheduler().createSyncExecutor(this); //save all world properties to generate level_sponge.dat files for (WorldProperties properties : Sponge.getServer().getAllWorldProperties()) { @@ -130,6 +142,17 @@ public void onServerReload(GameReloadEvent evt) { }); } + + @Listener + public void onPlayerJoin(ClientConnectionEvent.Join evt) { + onlinePlayers.put(evt.getTargetEntity().getUniqueId(), new SpongePlayerState(evt.getTargetEntity())); + } + + @Listener + public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) { + onlinePlayers.remove(evt.getTargetEntity().getUniqueId()); + } + @Override public void registerListener(ServerEventListener listener) { Sponge.getEventManager().registerListeners(this, new EventForwarder(listener)); @@ -167,15 +190,38 @@ public File getConfigFolder() { } @Override - public Collection getOnlinePlayers() { - // TODO Implement - return Collections.emptyList(); + public Collection getOnlinePlayers() { + updatePlayers(); + return onlinePlayers.values(); } @Override - public Optional getPlayer(UUID uuid) { - // TODO Implement - return Optional.empty(); + public Optional getPlayer(UUID uuid) { + updatePlayers(); + return Optional.ofNullable(onlinePlayers.get(uuid)); + } + + private synchronized void updatePlayers() { + if (lastPlayerUpdate + 1000 > System.currentTimeMillis()) return; //only update once a second + + if (Sponge.getServer().isMainThread()) { + updatePlayersSync(); + } else { + try { + syncExecutor.submit(this::updatePlayersSync).get(3, TimeUnit.SECONDS); + } catch (TimeoutException | InterruptedException ignore) { + } catch (ExecutionException e) { + Logger.global.logError("Unexpected exception trying to update player-states!", e); + } + } + + lastPlayerUpdate = System.currentTimeMillis(); + } + + private void updatePlayersSync() { + for (PlayerState player : onlinePlayers.values()) { + if (player instanceof SpongePlayerState) ((SpongePlayerState) player).update(); + } } @Override From b28667095304150beaeecbaceb7bcf18164c6a42 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Thu, 6 Aug 2020 17:24:36 +0200 Subject: [PATCH 03/10] Finish implementing the first live-api for sponge --- .../bluemap/bukkit/BukkitPlugin.java | 6 +- .../common/live/LiveAPIRequestHandler.java | 13 +- .../{PlayerState.java => Player.java} | 65 ++------ .../serverinterface/ServerInterface.java | 10 +- .../bluemap/core/web/WebSettings.java | 1 + .../bluecolored/bluemap/fabric/FabricMod.java | 16 ++ .../bluecolored/bluemap/forge/ForgeMod.java | 6 +- .../bluemap/sponge/SpongePlayer.java | 151 ++++++++++++++++++ .../bluemap/sponge/SpongePlayerState.java | 76 --------- .../bluemap/sponge/SpongePlugin.java | 88 +++++----- 10 files changed, 245 insertions(+), 187 deletions(-) rename BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/{PlayerState.java => Player.java} (60%) create mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java delete mode 100644 BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index 7794fd44..b478dfb6 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -43,7 +43,7 @@ import org.bukkit.plugin.java.JavaPlugin; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -184,13 +184,13 @@ public static BukkitPlugin getInstance() { } @Override - public Collection getOnlinePlayers() { + public Collection getOnlinePlayers() { // TODO Implement return Collections.emptyList(); } @Override - public Optional getPlayer(UUID uuid) { + public Optional getPlayer(UUID uuid) { // TODO Implement return Optional.empty(); } 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 index 7ca7c0f6..94dbdf3f 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java @@ -32,7 +32,7 @@ import com.google.gson.stream.JsonWriter; import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.webserver.HttpRequest; import de.bluecolored.bluemap.core.webserver.HttpRequestHandler; @@ -50,7 +50,8 @@ public LiveAPIRequestHandler(ServerInterface server, HttpRequestHandler notFound this.notFoundHandler = notFoundHandler; this.liveAPIRequests = new HashMap<>(); - + + this.liveAPIRequests.put("live", this::handleLivePingRequest); this.liveAPIRequests.put("live/players", this::handlePlayersRequest); } @@ -62,6 +63,12 @@ public HttpResponse handle(HttpRequest request) { 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); @@ -72,7 +79,7 @@ public HttpResponse handlePlayersRequest(HttpRequest request) { json.beginObject(); json.name("players").beginArray(); - for (PlayerState player : server.getOnlinePlayers()) { + for (Player player : server.getOnlinePlayers()) { if (player.isInvisible()) continue; if (player.isSneaking()) continue; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerState.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Player.java similarity index 60% rename from BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerState.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Player.java index 6af4cd8e..91f4c60e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/PlayerState.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Player.java @@ -27,85 +27,38 @@ import java.util.UUID; import com.flowpowered.math.vector.Vector3d; -import com.google.common.base.MoreObjects; import de.bluecolored.bluemap.common.plugin.text.Text; -public class PlayerState { +public interface Player { - private final UUID uuid; - protected Text name; - protected UUID world; - protected Vector3d position = Vector3d.ZERO; - protected boolean online = false; - protected boolean sneaking = false; - protected boolean invisible = false; - protected Gamemode gamemode = Gamemode.SURVIVAL; + public UUID getUuid(); - public PlayerState(UUID uuid, Text name, UUID world, Vector3d position) { - this.uuid = uuid; - this.name = name; - this.world = world; - this.position = position; - } + public Text getName(); - public UUID getUuid() { - return uuid; - } + public UUID getWorld(); - public Text getName() { - return name; - } - - public UUID getWorld() { - return world; - } - - public Vector3d getPosition() { - return position; - } + public Vector3d getPosition(); - public boolean isOnline() { - return online; - } + public boolean isOnline(); /** * Return true if the player is sneaking. *

If the player is offline the value of this method is undetermined.

* @return */ - public boolean isSneaking() { - return sneaking; - } + public boolean isSneaking(); /** * Returns true if the player has an invisibillity effect *

If the player is offline the value of this method is undetermined.

*/ - public boolean isInvisible() { - return invisible; - } + public boolean isInvisible(); /** * Returns the {@link Gamemode} this player is in *

If the player is offline the value of this method is undetermined.

*/ - public Gamemode getGamemode() { - return gamemode; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("uuid", uuid) - //.add("name", name) - //.add("world", world) - //.add("position", position) - //.add("online", online) - //.add("sneaking", sneaking) - //.add("invisible", invisible) - //.add("gamemode", gamemode) - .toString(); - } + public Gamemode getGamemode(); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java index eab11097..92ca74c6 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerInterface.java @@ -76,15 +76,15 @@ default boolean isMetricsEnabled(boolean configValue) { } /** - * Returns a collection of players that are currently online + * Returns a collection of the states of players that are currently online */ - Collection getOnlinePlayers(); + Collection getOnlinePlayers(); /** - * Returns the player with that UUID if present
- * this method is only guaranteed to return a player if the player is currently online. + * Returns the state of the player with that UUID if present
+ * this method is only guaranteed to return a {@link PlayerState} if the player is currently online. */ - Optional getPlayer(UUID uuid); + Optional getPlayer(UUID uuid); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/WebSettings.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/WebSettings.java index 5914eba8..fc33c212 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/WebSettings.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/WebSettings.java @@ -136,6 +136,7 @@ public void setFrom(TileRenderer tileRenderer, String mapId) { public void setFrom(World world, String mapId) { set(world.getSpawnPoint().getX(), "maps", mapId, "startPos", "x"); set(world.getSpawnPoint().getZ(), "maps", mapId, "startPos", "z"); + set(world.getUUID().toString(), "maps", mapId, "world"); } public void setFrom(MapConfig mapConfig, String mapId) { diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index 7f75f262..6d14bd15 100644 --- a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -26,7 +26,10 @@ import java.io.File; import java.io.IOException; +import java.util.Collection; +import java.util.Collections; import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; @@ -39,6 +42,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -146,5 +150,17 @@ private UUID loadUUIDForWorld(ServerWorld world) throws IOException { public File getConfigFolder() { return new File("config/bluemap"); } + + @Override + public Collection getOnlinePlayers() { + // TODO Implement + return Collections.emptyList(); + } + + @Override + public Optional getPlayer(UUID uuid) { + // TODO Implement + return Optional.empty(); + } } diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 5c514c7e..94830ebd 100644 --- a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -44,7 +44,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -237,13 +237,13 @@ public Commands getCommands() { } @Override - public Collection getOnlinePlayers() { + public Collection getOnlinePlayers() { // TODO Implement return Collections.emptyList(); } @Override - public Optional getPlayer(UUID uuid) { + public Optional getPlayer(UUID uuid) { // TODO Implement return Optional.empty(); } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java new file mode 100644 index 00000000..5f3dcb60 --- /dev/null +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java @@ -0,0 +1,151 @@ +/* + * 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.sponge; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.effect.potion.PotionEffect; +import org.spongepowered.api.effect.potion.PotionEffectTypes; +import org.spongepowered.api.entity.living.player.gamemode.GameMode; +import org.spongepowered.api.entity.living.player.gamemode.GameModes; + +import com.flowpowered.math.vector.Vector3d; + +import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; +import de.bluecolored.bluemap.common.plugin.text.Text; + +public class SpongePlayer implements Player { + + private static final Map GAMEMODE_MAP = new HashMap<>(5); + static { + GAMEMODE_MAP.put(GameModes.ADVENTURE, Gamemode.ADVENTURE); + GAMEMODE_MAP.put(GameModes.SURVIVAL, Gamemode.SURVIVAL); + GAMEMODE_MAP.put(GameModes.CREATIVE, Gamemode.CREATIVE); + GAMEMODE_MAP.put(GameModes.SPECTATOR, Gamemode.SPECTATOR); + GAMEMODE_MAP.put(GameModes.NOT_SET, Gamemode.SURVIVAL); + } + + private UUID uuid; + private Text name; + private UUID world; + private Vector3d position; + private boolean online; + private boolean sneaking; + private boolean invisible; + private Gamemode gamemode; + + private WeakReference delegate; + + public SpongePlayer(org.spongepowered.api.entity.living.player.Player delegate) { + this.uuid = delegate.getUniqueId(); + this.delegate = new WeakReference<>(delegate); + update(); + } + + @Override + public UUID getUuid() { + return this.uuid; + } + + @Override + public Text getName() { + return this.name; + } + + @Override + public UUID getWorld() { + return this.world; + } + + @Override + public Vector3d getPosition() { + return this.position; + } + + @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; + } + + /** + * API access, only call on server thread! + */ + public void update() { + org.spongepowered.api.entity.living.player.Player player = delegate.get(); + if (player == null) { + player = Sponge.getServer().getPlayer(uuid).orElse(null); + if (player == null) { + this.online = false; + return; + } + + delegate = new WeakReference<>(player); + } + + this.gamemode = GAMEMODE_MAP.get(player.get(Keys.GAME_MODE).orElse(GameModes.NOT_SET)); + + boolean invis = player.get(Keys.VANISH).orElse(false); + if (!invis && player.get(Keys.INVISIBLE).orElse(false)) invis = true; + if (!invis) { + Optional> effects = player.get(Keys.POTION_EFFECTS); + if (effects.isPresent()) { + for (PotionEffect effect : effects.get()) { + if (effect.getType().equals(PotionEffectTypes.INVISIBILITY) && effect.getDuration() > 0) invis = true; + } + } + } + this.invisible = invis; + + this.name = Text.of(player.getName()); + this.online = player.isOnline(); + this.position = player.getPosition(); + this.sneaking = player.get(Keys.IS_SNEAKING).orElse(false); + this.world = player.getWorld().getUniqueId(); + } + +} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java deleted file mode 100644 index 3ca5b7c9..00000000 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayerState.java +++ /dev/null @@ -1,76 +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.sponge; - -import java.lang.ref.WeakReference; - -import org.spongepowered.api.data.key.Keys; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.entity.living.player.gamemode.GameMode; -import org.spongepowered.api.entity.living.player.gamemode.GameModes; - -import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; -import de.bluecolored.bluemap.common.plugin.text.Text; - -public class SpongePlayerState extends PlayerState { - - private WeakReference playerRef; - - public SpongePlayerState(Player player) { - super(player.getUniqueId(), Text.of(player.getName()), player.getWorld().getUniqueId(), player.getPosition()); - this.playerRef = new WeakReference(player); - - update(); - } - - void update() { - Player player = playerRef.get(); - if (player == null) { - this.online = false; - return; - } - - this.online = player.isOnline(); - - if (this.online) { - this.name = Text.of(player.getName()); - - this.world = player.getWorld().getUniqueId(); - this.position = player.getPosition(); - - GameMode gm = player.get(Keys.GAME_MODE).orElse(GameModes.NOT_SET); - if (gm == GameModes.SURVIVAL) this.gamemode = Gamemode.SURVIVAL; - else if (gm == GameModes.CREATIVE) this.gamemode = Gamemode.CREATIVE; - else if (gm == GameModes.SPECTATOR) this.gamemode = Gamemode.SPECTATOR; - else if (gm == GameModes.ADVENTURE) this.gamemode = Gamemode.ADVENTURE; - else this.gamemode = Gamemode.SURVIVAL; - - this.invisible = player.get(Keys.INVISIBLE).orElse(false); - this.sneaking = player.get(Keys.IS_SNEAKING).orElse(false); - } - } - -} diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index b3492a41..0055b9c2 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -27,14 +27,13 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import javax.inject.Inject; @@ -48,12 +47,13 @@ import org.spongepowered.api.event.network.ClientConnectionEvent; import org.spongepowered.api.plugin.PluginContainer; import org.spongepowered.api.scheduler.SpongeExecutorService; +import org.spongepowered.api.scheduler.Task; import org.spongepowered.api.util.Tristate; import org.spongepowered.api.world.World; import org.spongepowered.api.world.storage.WorldProperties; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.plugin.serverinterface.PlayerState; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; @@ -61,7 +61,7 @@ import net.querz.nbt.CompoundTag; import net.querz.nbt.NBTUtil; -@org.spongepowered.api.plugin.Plugin( +@org.spongepowered.api.plugin.Plugin ( id = Plugin.PLUGIN_ID, name = Plugin.PLUGIN_NAME, authors = { "Blue (Lukas Rieger)" }, @@ -80,17 +80,18 @@ public class SpongePlugin implements ServerInterface { private Plugin bluemap; private SpongeCommands commands; - private SpongeExecutorService syncExecutor; private SpongeExecutorService asyncExecutor; - private long lastPlayerUpdate = -1; - private Map onlinePlayers; + private int playerUpdateIndex = 0; + private Map onlinePlayerMap; + private List onlinePlayerList; @Inject public SpongePlugin(org.slf4j.Logger logger) { Logger.global = new Slf4jLogger(logger); - this.onlinePlayers = new ConcurrentHashMap<>(); + this.onlinePlayerMap = new ConcurrentHashMap<>(); + this.onlinePlayerList = new ArrayList<>(); this.bluemap = new Plugin("sponge", this); this.commands = new SpongeCommands(bluemap); @@ -99,7 +100,6 @@ public SpongePlugin(org.slf4j.Logger logger) { @Listener public void onServerStart(GameStartingServerEvent evt) { asyncExecutor = Sponge.getScheduler().createAsyncExecutor(this); - syncExecutor = Sponge.getScheduler().createSyncExecutor(this); //save all world properties to generate level_sponge.dat files for (WorldProperties properties : Sponge.getServer().getAllWorldProperties()) { @@ -111,6 +111,12 @@ public void onServerStart(GameStartingServerEvent evt) { Sponge.getCommandManager().register(this, command, command.getLabel()); } + //start updating players + Task.builder() + .intervalTicks(1) + .execute(this::updateSomePlayers) + .submit(this); + asyncExecutor.execute(() -> { try { Logger.global.logInfo("Loading..."); @@ -145,12 +151,16 @@ public void onServerReload(GameReloadEvent evt) { @Listener public void onPlayerJoin(ClientConnectionEvent.Join evt) { - onlinePlayers.put(evt.getTargetEntity().getUniqueId(), new SpongePlayerState(evt.getTargetEntity())); + SpongePlayer player = new SpongePlayer(evt.getTargetEntity()); + onlinePlayerMap.put(evt.getTargetEntity().getUniqueId(), player); + onlinePlayerList.add(player); } @Listener public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) { - onlinePlayers.remove(evt.getTargetEntity().getUniqueId()); + UUID playerUUID = evt.getTargetEntity().getUniqueId(); + onlinePlayerMap.remove(playerUUID); + onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); } @Override @@ -191,38 +201,13 @@ public File getConfigFolder() { } @Override - public Collection getOnlinePlayers() { - updatePlayers(); - return onlinePlayers.values(); + public Collection getOnlinePlayers() { + return onlinePlayerMap.values(); } @Override - public Optional getPlayer(UUID uuid) { - updatePlayers(); - return Optional.ofNullable(onlinePlayers.get(uuid)); - } - - private synchronized void updatePlayers() { - if (lastPlayerUpdate + 1000 > System.currentTimeMillis()) return; //only update once a second - - if (Sponge.getServer().isMainThread()) { - updatePlayersSync(); - } else { - try { - syncExecutor.submit(this::updatePlayersSync).get(3, TimeUnit.SECONDS); - } catch (TimeoutException | InterruptedException ignore) { - } catch (ExecutionException e) { - Logger.global.logError("Unexpected exception trying to update player-states!", e); - } - } - - lastPlayerUpdate = System.currentTimeMillis(); - } - - private void updatePlayersSync() { - for (PlayerState player : onlinePlayers.values()) { - if (player instanceof SpongePlayerState) ((SpongePlayerState) player).update(); - } + public Optional getPlayer(UUID uuid) { + return Optional.ofNullable(onlinePlayerMap.get(uuid)); } @Override @@ -238,4 +223,25 @@ public boolean isMetricsEnabled(boolean configValue) { return Sponge.getMetricsConfigManager().getGlobalCollectionState().asBoolean(); } + /** + * 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(i).update(); + } + } + } + } From c4f0256ca03851c7c438eadcb4a738870025d1d8 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Fri, 7 Aug 2020 14:17:48 +0200 Subject: [PATCH 04/10] Implement playermarkers on the web-app --- .../common/plugin/MapUpdateHandler.java | 16 -- .../bluemap/common/plugin/Plugin.java | 6 + .../serverinterface/ServerEventListener.java | 12 +- .../common/plugin/skins/PlayerSkin.java | 152 ++++++++++++++++++ .../plugin/skins/PlayerSkinUpdater.java | 63 ++++++++ .../core/resourcepack/TextureGallery.java | 4 +- .../main/webroot/assets/playerheads/alex.png | Bin 0 -> 2956 bytes .../main/webroot/assets/playerheads/steve.png | Bin 0 -> 3009 bytes .../src/main/webroot/js/libs/BlueMap.js | 3 +- .../main/webroot/js/libs/hud/MarkerManager.js | 54 ++++++- .../src/main/webroot/js/libs/hud/POIMarker.js | 1 - .../main/webroot/js/libs/hud/PlayerMarker.js | 90 +++++++++++ .../webroot/js/libs/hud/PlayerMarkerSet.js | 85 ++++++++++ .../main/webroot/style/modules/hudInfo.scss | 37 ++++- 14 files changed, 490 insertions(+), 33 deletions(-) create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkin.java create mode 100644 BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java create mode 100644 BlueMapCore/src/main/webroot/assets/playerheads/alex.png create mode 100644 BlueMapCore/src/main/webroot/assets/playerheads/steve.png create mode 100644 BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarker.js create mode 100644 BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarkerSet.js diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java index 7d60f966..ac69cd1a 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java @@ -35,7 +35,6 @@ import de.bluecolored.bluemap.common.MapType; import de.bluecolored.bluemap.common.RenderManager; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.plugin.text.Text; public class MapUpdateHandler implements ServerEventListener { @@ -135,20 +134,5 @@ public void flushTileBuffer() { updateBuffer.clear(); } } - - @Override - public void onPlayerJoin(UUID playerUuid) { - - } - - @Override - public void onPlayerLeave(UUID playerUuid) { - - } - - @Override - public void onChatMessage(Text message) { - - } } 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 ac31bc3c..5bdfff37 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 @@ -51,6 +51,7 @@ import de.bluecolored.bluemap.common.RenderManager; import de.bluecolored.bluemap.common.api.BlueMapAPIImpl; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater; import de.bluecolored.bluemap.core.config.ConfigManager; import de.bluecolored.bluemap.core.config.MainConfig; import de.bluecolored.bluemap.core.config.MainConfig.MapConfig; @@ -86,6 +87,7 @@ public class Plugin { private Map maps; private MapUpdateHandler updateHandler; + private PlayerSkinUpdater skinUpdater; private RenderManager renderManager; private BlueMapWebServer webServer; @@ -269,6 +271,10 @@ public synchronized void load() throws IOException, ParseResourceException { this.updateHandler = new MapUpdateHandler(this); serverInterface.registerListener(updateHandler); + //start skin updater + this.skinUpdater = new PlayerSkinUpdater(config.getWebRoot().resolve("assets").resolve("playerheads").toFile()); + serverInterface.registerListener(skinUpdater); + //create/update webfiles WebFilesManager webFilesManager = new WebFilesManager(config.getWebRoot()); if (webFilesManager.needsUpdate()) { diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java index aa530ae1..cbb2a97f 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/ServerEventListener.java @@ -33,16 +33,16 @@ public interface ServerEventListener { - void onWorldSaveToDisk(UUID world); + default void onWorldSaveToDisk(UUID world) {}; - void onBlockChange(UUID world, Vector3i blockPos); + default void onBlockChange(UUID world, Vector3i blockPos) {}; - void onChunkFinishedGeneration(UUID world, Vector2i chunkPos); + default void onChunkFinishedGeneration(UUID world, Vector2i chunkPos) {}; - void onPlayerJoin(UUID playerUuid); + default void onPlayerJoin(UUID playerUuid) {}; - void onPlayerLeave(UUID playerUuid); + default void onPlayerLeave(UUID playerUuid) {}; - void onChatMessage(Text message); + default void onChatMessage(Text message) {}; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkin.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkin.java new file mode 100644 index 00000000..5201f8dc --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkin.java @@ -0,0 +1,152 @@ +/* + * 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.plugin.skins; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.Base64; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.imageio.ImageIO; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +import de.bluecolored.bluemap.core.logger.Logger; + +public class PlayerSkin { + + private final UUID uuid; + private long lastUpdate; + + public PlayerSkin(UUID uuid) { + this.uuid = uuid; + this.lastUpdate = -1; + } + + public void update(File storageFolder) { + long now = System.currentTimeMillis(); + if (lastUpdate > 0 && lastUpdate + 600000 > now) return; // only update if skin is older than 10 minutes + + lastUpdate = now; + + new Thread(() -> { + try { + Future futureSkin = loadSkin(); + BufferedImage skin = futureSkin.get(10, TimeUnit.SECONDS); + BufferedImage head = createHead(skin); + ImageIO.write(head, "png", new File(storageFolder, uuid.toString() + ".png")); + } catch (ExecutionException | TimeoutException e) { + Logger.global.logWarning("Failed to load player-skin from mojang-servers: " + e); + } catch (IOException e) { + Logger.global.logError("Failed to write player-head image!", e); + } catch (InterruptedException ignore) {} + }).start(); + } + + public BufferedImage createHead(BufferedImage skinTexture) { + BufferedImage head = new BufferedImage(8, 8, skinTexture.getType()); + + BufferedImage layer1 = skinTexture.getSubimage(8, 8, 8, 8); + BufferedImage layer2 = skinTexture.getSubimage(40, 8, 8, 8); + + try { + Graphics2D g = head.createGraphics(); + g.drawImage(layer1, 0, 0, null); + g.drawImage(layer2, 0, 0, null); + } catch (Throwable t) { // There might be problems with headless servers when loading the graphics class + Logger.global.noFloodWarning("headless-graphics-fail", + "Could not access Graphics2D to render player-skin texture. Try adding '-Djava.awt.headless=true' to your startup flags or ignore this warning."); + + layer1.copyData(head.getRaster()); + } + + return head; + } + + public Future loadSkin() { + CompletableFuture image = new CompletableFuture<>(); + + new Thread(() -> { + try { + JsonParser parser = new JsonParser(); + try (Reader reader = requestProfileJson()) { + String textureInfoJson = readTextureInfoJson(parser.parse(reader)); + String textureUrl = readTextureUrl(parser.parse(textureInfoJson)); + image.complete(ImageIO.read(new URL(textureUrl))); + } + } catch (IOException e) { + image.completeExceptionally(e); + } + }).start(); + + return image; + } + + private Reader requestProfileJson() throws IOException { + URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + this.uuid); + return new InputStreamReader(url.openStream()); + } + + private String readTextureInfoJson(JsonElement json) throws IOException { + try { + JsonArray properties = json.getAsJsonObject().getAsJsonArray("properties"); + + for (JsonElement element : properties) { + if (element.getAsJsonObject().get("name").getAsString().equals("textures")) { + return new String(Base64.getDecoder().decode(element.getAsJsonObject().get("value").getAsString().getBytes())); + } + } + + throw new IOException("No texture info found!"); + } catch (IllegalStateException | ClassCastException e) { + throw new IOException(e); + } + + } + + private String readTextureUrl(JsonElement json) throws IOException { + try { + return json.getAsJsonObject() + .getAsJsonObject("textures") + .getAsJsonObject("SKIN") + .get("url").getAsString(); + } catch (IllegalStateException | ClassCastException e) { + throw new IOException(e); + } + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java new file mode 100644 index 00000000..2332a3be --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java @@ -0,0 +1,63 @@ +/* + * 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.plugin.skins; + +import java.io.File; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; + +public class PlayerSkinUpdater implements ServerEventListener { + + private File storageFolder; + + private Map skins; + + public PlayerSkinUpdater(File storageFolder) { + this.storageFolder = storageFolder; + this.skins = new ConcurrentHashMap<>(); + + this.storageFolder.mkdirs(); + } + + public void updateSkin(UUID playerUuid) { + PlayerSkin skin = skins.get(playerUuid); + + if (skin == null) { + skin = new PlayerSkin(playerUuid); + skins.put(playerUuid, skin); + } + + skin.update(storageFolder); + } + + @Override + public void onPlayerJoin(UUID playerUuid) { + updateSkin(playerUuid); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureGallery.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureGallery.java index 4aa93004..4c6f5afd 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureGallery.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resourcepack/TextureGallery.java @@ -198,9 +198,7 @@ public synchronized Texture loadTexture(FileAccess fileAccess, String path) thro //crop off animation frames if (image.getHeight() > image.getWidth()){ - BufferedImage cropped = new BufferedImage(image.getWidth(), image.getWidth(), image.getType()); - image.copyData(cropped.getRaster()); - image = cropped; + image = image.getSubimage(0, 0, image.getWidth(), image.getWidth()); } //check halfTransparency diff --git a/BlueMapCore/src/main/webroot/assets/playerheads/alex.png b/BlueMapCore/src/main/webroot/assets/playerheads/alex.png new file mode 100644 index 0000000000000000000000000000000000000000..a49a52174023b120172acc6165e2e33fbf551c5b GIT binary patch literal 2956 zcmV;73v={|P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002DNklFD~6_Nuulmf9vS_ACLRFi;4uJU6jQ@}Ljphm0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002&NklYE0Knm2E|*+VX{%70B83V*fG^-If@^n&f{TwJ?t+_xE@Pi`+}XVx;GAFAkfxej711RBtyIhX&e9;?^sJ>nsI<7 zWL{{jw@85V<3m&wlO_#T%LRmj{P&x)y9a{X(<5I~ia)IhLq)#G>1AyuAFlw2no=B` zTyXQeO;VNgvNl)u8H39qjn*c?^').appendTo(element)[0]; this.dataRoot = dataRoot; + this.liveApiRoot = liveApiRoot; this.locationHash = ''; this.cacheSuffix = ''; diff --git a/BlueMapCore/src/main/webroot/js/libs/hud/MarkerManager.js b/BlueMapCore/src/main/webroot/js/libs/hud/MarkerManager.js index ea722a8e..ec15a1d2 100644 --- a/BlueMapCore/src/main/webroot/js/libs/hud/MarkerManager.js +++ b/BlueMapCore/src/main/webroot/js/libs/hud/MarkerManager.js @@ -3,6 +3,7 @@ import $ from "jquery"; import ToggleButton from "../ui/ToggleButton"; import Label from "../ui/Label"; import {cachePreventionNr} from "../utils"; +import PlayerMarkerSet from "./PlayerMarkerSet"; export default class MarkerManager { @@ -10,14 +11,23 @@ export default class MarkerManager { this.blueMap = blueMap; this.ui = ui; + this.markerData = null; + this.liveData = null; this.markerSets = []; + this.playerMarkerSet = null; + this.readyPromise = - this.loadMarkerData() - .catch(ignore => { - if (this.blueMap.debugInfo) console.debug("Failed load markers:", ignore); - }) - .then(this.loadMarkers); + Promise.all([ + this.loadMarkerData() + .catch(ignore => { + if (this.blueMap.debugInfo) console.debug("Failed load markers:", ignore); + }), + this.checkLiveAPI() + .then(this.initializePlayerMarkers) + ]) + .then(this.loadMarkers) + .then(this.updatePlayerMarkerLoop); $(document).on('bluemap-map-change', this.onBlueMapMapChange); } @@ -41,6 +51,25 @@ export default class MarkerManager { }); } + checkLiveAPI() { + return new Promise((resolve, reject) => { + this.blueMap.fileLoader.load(this.blueMap.liveApiRoot + 'players?' + cachePreventionNr(), + liveData => { + try { + this.liveData = JSON.parse(liveData); + resolve(); + } catch (e){ + reject(e); + } + }, + xhr => {}, + error => { + reject(); + } + ); + }); + } + loadMarkers = () => { if (this.markerData && this.markerData.markerSets) { this.markerData.markerSets.forEach(setData => { @@ -49,12 +78,27 @@ export default class MarkerManager { } }; + initializePlayerMarkers = () => { + if (this.liveData){ + this.playerMarkerSet = new PlayerMarkerSet(this.blueMap); + this.markerSets.push(this.playerMarkerSet); + } + }; + update(){ this.markerSets.forEach(markerSet => { markerSet.update(); }); } + updatePlayerMarkerLoop = () => { + if (this.playerMarkerSet){ + this.playerMarkerSet.updateLive(); + } + + setTimeout(this.updatePlayerMarkerLoop, 2000); + }; + addMenuElements(menu){ let addedLabel = false; this.markerSets.forEach(markerSet => { diff --git a/BlueMapCore/src/main/webroot/js/libs/hud/POIMarker.js b/BlueMapCore/src/main/webroot/js/libs/hud/POIMarker.js index 3a8cdfd4..216f5581 100644 --- a/BlueMapCore/src/main/webroot/js/libs/hud/POIMarker.js +++ b/BlueMapCore/src/main/webroot/js/libs/hud/POIMarker.js @@ -1,7 +1,6 @@ import $ from 'jquery'; import Marker from "./Marker"; import {CSS2DObject} from "./CSS2DRenderer"; -import {Vector3} from "three"; import POI from "../../../assets/poi.svg"; diff --git a/BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarker.js b/BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarker.js new file mode 100644 index 00000000..1129fa5b --- /dev/null +++ b/BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarker.js @@ -0,0 +1,90 @@ +import $ from 'jquery'; +import Marker from "./Marker"; +import {CSS2DObject} from "./CSS2DRenderer"; + +export default class PlayerMarker extends Marker { + + constructor(blueMap, markerSet, markerData, playerUuid, worldUuid) { + super(blueMap, markerSet, markerData); + + this.online = false; + this.player = playerUuid; + this.world = worldUuid; + + this.animationRunning = false; + this.lastFrame = -1; + } + + setVisible(visible){ + this.visible = visible && this.online && this.world === this.blueMap.settings.maps[this.blueMap.map].world; + + this.blueMap.updateFrame = true; + + if (!this.renderObject){ + let iconElement = $(`
${this.label}
`); + iconElement.find("img").click(this.onClick); + + this.renderObject = new CSS2DObject(iconElement[0]); + this.renderObject.position.copy(this.position); + this.renderObject.onBeforeRender = (renderer, scene, camera) => { + let distanceSquared = this.position.distanceToSquared(camera.position); + if (distanceSquared > 1000000) { + iconElement.addClass("distant"); + } else { + iconElement.removeClass("distant"); + } + + this.updateRenderObject(this.renderObject, scene, camera); + }; + } + + if (this.visible) { + this.blueMap.hudScene.add(this.renderObject); + } else { + this.blueMap.hudScene.remove(this.renderObject); + } + } + + updatePosition = () => { + if (this.renderObject && !this.renderObject.position.equals(this.position)) { + if (this.visible) { + if (!this.animationRunning) { + this.animationRunning = true; + requestAnimationFrame(this.moveAnimation); + } + } else { + this.renderObject.position.copy(this.position); + } + } + }; + + moveAnimation = (time) => { + let delta = time - this.lastFrame; + if (this.lastFrame === -1){ + delta = 20; + } + this.lastFrame = time; + + if (this.renderObject && !this.renderObject.position.equals(this.position)) { + this.renderObject.position.x += (this.position.x - this.renderObject.position.x) * 0.01 * delta; + this.renderObject.position.y += (this.position.y - this.renderObject.position.y) * 0.01 * delta; + this.renderObject.position.z += (this.position.z - this.renderObject.position.z) * 0.01 * delta; + + if (this.renderObject.position.distanceToSquared(this.position) < 0.001) { + this.renderObject.position.copy(this.position); + } + + this.blueMap.updateFrame = true; + + requestAnimationFrame(this.moveAnimation); + } else { + this.animationRunning = false; + this.lastFrame = -1; + } + }; + + onClick = () => { + + } + +} \ No newline at end of file diff --git a/BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarkerSet.js b/BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarkerSet.js new file mode 100644 index 00000000..9668af24 --- /dev/null +++ b/BlueMapCore/src/main/webroot/js/libs/hud/PlayerMarkerSet.js @@ -0,0 +1,85 @@ +import POIMarker from "./POIMarker"; +import ShapeMarker from "./ShapeMarker"; +import {cachePreventionNr} from "../utils"; +import PlayerMarker from "./PlayerMarker"; +import {Vector3} from "three"; + +export default class PlayerMarkerSet { + + constructor(blueMap) { + this.blueMap = blueMap; + this.id = "bluemap-live-players"; + this.label = "players"; + this.toggleable = true; + this.defaultHide = false; + this.marker = []; + this.markerMap = {}; + + this.visible = true; + } + + update() { + this.marker.forEach(marker => { + marker.setVisible(this.visible); + }); + } + + async updateLive(){ + await new Promise((resolve, reject) => { + this.blueMap.fileLoader.load(this.blueMap.liveApiRoot + 'players?' + cachePreventionNr(), + liveData => { + try { + liveData = JSON.parse(liveData); + resolve(liveData); + } catch (e){ + reject(e); + } + }, + xhr => {}, + error => { + reject(error); + } + ); + }).then((liveData) => { + this.updateWith(liveData) + }).catch((e) => { + console.error("Failed to update player-markers!", e); + }); + } + + updateWith(liveData){ + this.marker.forEach(marker => { + marker.nowOnline = false; + }); + + for(let i = 0; i < liveData.players.length; i++){ + let player = liveData.players[i]; + let marker = this.markerMap[player.uuid]; + + if (!marker){ + marker = new PlayerMarker(this.blueMap, this, { + type: "playermarker", + map: null, + position: player.position, + label: player.name, + link: null, + newTab: false + }, player.uuid, player.world); + + this.markerMap[player.uuid] = marker; + this.marker.push(marker); + } + + marker.nowOnline = true; + marker.position = new Vector3(player.position.x, player.position.y + 1.5, player.position.z); + marker.updatePosition(); + } + + this.marker.forEach(marker => { + if (marker.nowOnline !== marker.online){ + marker.online = marker.nowOnline; + marker.setVisible(this.visible); + } + }); + } +} \ No newline at end of file diff --git a/BlueMapCore/src/main/webroot/style/modules/hudInfo.scss b/BlueMapCore/src/main/webroot/style/modules/hudInfo.scss index 997cd8ca..cfdf07e7 100644 --- a/BlueMapCore/src/main/webroot/style/modules/hudInfo.scss +++ b/BlueMapCore/src/main/webroot/style/modules/hudInfo.scss @@ -76,7 +76,7 @@ } } -.bluemap-container .marker-poi { +.bluemap-container .marker-poi, .bluemap-container .marker-player { pointer-events: none; > * { @@ -86,4 +86,39 @@ > img { filter: drop-shadow(1px 1px 3px #0008); } +} + +.bluemap-container .marker-player { + img { + width: 32px; + height: 32px; + + image-rendering: pixelated; + image-rendering: crisp-edges; + + transition: all 0.3s; + } + + .nameplate { + position: absolute; + left: 50%; + top: 0; + transform: translate(-50%, -110%); + background: rgba(50, 50, 50, 0.75); + padding: 0.2em 0.3em; + color: white; + + transition: all 0.3s; + } + + &.distant { + img { + width: 16px; + height: 16px; + } + + .nameplate { + opacity: 0; + } + } } \ No newline at end of file From 7982765586221d9f34704ac01ea6ddb2b8f4bf57 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Fri, 7 Aug 2020 17:29:28 +0200 Subject: [PATCH 05/10] Implement live-api for bukkit --- .../bluemap/bukkit/BukkitPlayer.java | 138 ++++++++++++++++++ .../bluemap/bukkit/BukkitPlugin.java | 66 ++++++++- .../bluemap/bukkit/EventForwarder.java | 20 +++ .../bluemap/sponge/SpongePlugin.java | 8 +- 4 files changed, 225 insertions(+), 7 deletions(-) create mode 100644 BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java new file mode 100644 index 00000000..277833ff --- /dev/null +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java @@ -0,0 +1,138 @@ +/* + * 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.bukkit; + +import java.lang.ref.WeakReference; +import java.util.EnumMap; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.potion.PotionEffectType; + +import com.flowpowered.math.vector.Vector3d; + +import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; +import de.bluecolored.bluemap.common.plugin.text.Text; + +public class BukkitPlayer implements Player { + + private static final Map GAMEMODE_MAP = new EnumMap<>(GameMode.class); + static { + GAMEMODE_MAP.put(GameMode.ADVENTURE, Gamemode.ADVENTURE); + GAMEMODE_MAP.put(GameMode.SURVIVAL, Gamemode.SURVIVAL); + GAMEMODE_MAP.put(GameMode.CREATIVE, Gamemode.CREATIVE); + GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR); + } + + private UUID uuid; + private Text name; + private UUID world; + private Vector3d position; + private boolean online; + private boolean sneaking; + private boolean invisible; + private Gamemode gamemode; + + private WeakReference delegate; + + public BukkitPlayer(org.bukkit.entity.Player delegate) { + this.uuid = delegate.getUniqueId(); + this.delegate = new WeakReference<>(delegate); + update(); + } + + @Override + public UUID getUuid() { + return this.uuid; + } + + @Override + public Text getName() { + return this.name; + } + + @Override + public UUID getWorld() { + return this.world; + } + + @Override + public Vector3d getPosition() { + return this.position; + } + + @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; + } + + /** + * API access, only call on server thread! + */ + public void update() { + org.bukkit.entity.Player player = delegate.get(); + if (player == null) { + player = Bukkit.getPlayer(uuid); + if (player == null) { + this.online = false; + return; + } + + delegate = new WeakReference<>(player); + } + + this.gamemode = GAMEMODE_MAP.get(player.getGameMode()); + + this.invisible = player.hasPotionEffect(PotionEffectType.INVISIBILITY); + + this.name = Text.of(player.getName()); + this.online = player.isOnline(); + + Location location = player.getLocation(); + this.position = new Vector3d(location.getX(), location.getY(), location.getZ()); + this.sneaking = player.isSneaking(); + this.world = player.getWorld().getUID(); + } + +} diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index b478dfb6..dc7d2cb8 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -27,11 +27,15 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -40,6 +44,11 @@ import org.bukkit.World; import org.bukkit.command.CommandMap; import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.java.JavaPlugin; import de.bluecolored.bluemap.common.plugin.Plugin; @@ -48,7 +57,7 @@ import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; -public class BukkitPlugin extends JavaPlugin implements ServerInterface { +public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listener { private static BukkitPlugin instance; @@ -56,8 +65,15 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface { private EventForwarder eventForwarder; private BukkitCommands commands; + private int playerUpdateIndex = 0; + private Map onlinePlayerMap; + private List onlinePlayerList; + public BukkitPlugin() { Logger.global = new JavaLogger(getLogger()); + + this.onlinePlayerMap = new ConcurrentHashMap<>(); + this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>()); this.eventForwarder = new EventForwarder(); this.bluemap = new Plugin("bukkit", this); @@ -77,6 +93,7 @@ public void onEnable() { } //register events + getServer().getPluginManager().registerEvents(this, this); getServer().getPluginManager().registerEvents(eventForwarder, this); //register commands @@ -96,6 +113,9 @@ public void onEnable() { //tab completions getServer().getPluginManager().registerEvents(commands, this); + //start updating players + getServer().getScheduler().runTaskTimer(this, this::updateSomePlayers, 1, 1); + //load bluemap getServer().getScheduler().runTaskAsynchronously(this, () -> { try { @@ -111,6 +131,7 @@ public void onEnable() { @Override public void onDisable() { Logger.global.logInfo("Stopping..."); + getServer().getScheduler().cancelTasks(this); bluemap.unload(); Logger.global.logInfo("Saved and stopped!"); } @@ -182,17 +203,52 @@ public Plugin getBlueMap() { public static BukkitPlugin getInstance() { return instance; } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent evt) { + BukkitPlayer player = new BukkitPlayer(evt.getPlayer()); + onlinePlayerMap.put(evt.getPlayer().getUniqueId(), player); + onlinePlayerList.add(player); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerLeave(PlayerQuitEvent evt) { + UUID playerUUID = evt.getPlayer().getUniqueId(); + onlinePlayerMap.remove(playerUUID); + synchronized (onlinePlayerList) { + onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); + } + } @Override public Collection getOnlinePlayers() { - // TODO Implement - return Collections.emptyList(); + return onlinePlayerMap.values(); } @Override public Optional getPlayer(UUID uuid) { - // TODO Implement - return Optional.empty(); + 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(i).update(); + } + } } } diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java index a307437c..775768ef 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java @@ -42,6 +42,9 @@ import org.bukkit.event.block.BlockGrowEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.world.ChunkPopulateEvent; import org.bukkit.event.world.WorldSaveEvent; @@ -49,6 +52,7 @@ import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.plugin.text.Text; public class EventForwarder implements Listener { @@ -129,5 +133,21 @@ public synchronized void onChunkFinishedGeneration(ChunkPopulateEvent evt) { Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ()); listeners.forEach(l -> l.onChunkFinishedGeneration(world, chunkPos)); } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent evt) { + listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId())); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerLeave(PlayerQuitEvent evt) { + listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId())); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerChat(AsyncPlayerChatEvent evt) { + String message = String.format(evt.getFormat(), evt.getPlayer().getDisplayName(), evt.getMessage()); + listeners.forEach(l -> l.onChatMessage(Text.of(message))); + } } diff --git a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index 0055b9c2..607cd7f1 100644 --- a/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/BlueMapSponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -29,6 +29,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -91,7 +92,7 @@ public SpongePlugin(org.slf4j.Logger logger) { Logger.global = new Slf4jLogger(logger); this.onlinePlayerMap = new ConcurrentHashMap<>(); - this.onlinePlayerList = new ArrayList<>(); + this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>()); this.bluemap = new Plugin("sponge", this); this.commands = new SpongeCommands(bluemap); @@ -131,6 +132,7 @@ public void onServerStart(GameStartingServerEvent evt) { @Listener public void onServerStop(GameStoppingEvent evt) { Logger.global.logInfo("Stopping..."); + Sponge.getScheduler().getScheduledTasks(this).forEach(t -> t.cancel()); bluemap.unload(); Logger.global.logInfo("Saved and stopped!"); } @@ -160,7 +162,9 @@ public void onPlayerJoin(ClientConnectionEvent.Join evt) { public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) { UUID playerUUID = evt.getTargetEntity().getUniqueId(); onlinePlayerMap.remove(playerUUID); - onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); + synchronized (onlinePlayerList) { + onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); + } } @Override From 59c3f8adc5a20afdf418e92a7859749d4c5e2c19 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sat, 8 Aug 2020 16:50:47 +0200 Subject: [PATCH 06/10] Implement live-api for fabric --- .../bluemap/fabric/FabricEventForwarder.java | 19 +++ .../bluecolored/bluemap/fabric/FabricMod.java | 72 +++++++- .../bluemap/fabric/FabricPlayer.java | 155 ++++++++++++++++++ .../fabric/events/PlayerJoinCallback.java | 42 +++++ .../fabric/events/PlayerLeaveCallback.java | 42 +++++ .../fabric/mixin/MixinPlayerManager.java | 56 +++++++ .../src/main/resources/bluemap.mixins.json | 5 +- 7 files changed, 385 insertions(+), 6 deletions(-) create mode 100644 BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java create mode 100644 BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerJoinCallback.java create mode 100644 BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerLeaveCallback.java create mode 100644 BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinPlayerManager.java diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index cc3f74c0..230134f0 100644 --- a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -35,10 +35,14 @@ import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.fabric.events.ChunkFinalizeCallback; +import de.bluecolored.bluemap.fabric.events.PlayerJoinCallback; +import de.bluecolored.bluemap.fabric.events.PlayerLeaveCallback; import de.bluecolored.bluemap.fabric.events.WorldSaveCallback; import net.fabricmc.fabric.api.event.player.AttackBlockCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; @@ -60,6 +64,9 @@ public FabricEventForwarder(FabricMod mod) { ChunkFinalizeCallback.EVENT.register(this::onChunkFinalize); AttackBlockCallback.EVENT.register(this::onBlockAttack); UseBlockCallback.EVENT.register(this::onBlockUse); + + PlayerJoinCallback.EVENT.register(this::onPlayerJoin); + PlayerLeaveCallback.EVENT.register(this::onPlayerLeave); } public void addEventListener(ServerEventListener listener) { @@ -116,4 +123,16 @@ public void onChunkFinalize(ServerWorld world, Vector2i chunkPos) { } } + public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) { + if (this.mod.getServer() != server) return; + + this.eventListeners.forEach(l -> l.onPlayerJoin(player.getUuid())); + } + + public void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) { + if (this.mod.getServer() != server) return; + + this.eventListeners.forEach(l -> l.onPlayerLeave(player.getUuid())); + } + } diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index 6d14bd15..203f4693 100644 --- a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -26,8 +26,10 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -47,25 +49,37 @@ import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; +import de.bluecolored.bluemap.fabric.events.PlayerJoinCallback; +import de.bluecolored.bluemap.fabric.events.PlayerLeaveCallback; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.server.ServerStartCallback; import net.fabricmc.fabric.api.event.server.ServerStopCallback; +import net.fabricmc.fabric.api.event.server.ServerTickCallback; import net.fabricmc.fabric.api.registry.CommandRegistry; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; public class FabricMod implements ModInitializer, ServerInterface { private Plugin pluginInstance = null; + private MinecraftServer serverInstance = null; private Map worldUuids; private FabricEventForwarder eventForwarder; private LoadingCache worldUuidCache; + + private int playerUpdateIndex = 0; + private Map onlinePlayerMap; + private List onlinePlayerList; public FabricMod() { Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME)); + this.onlinePlayerMap = new ConcurrentHashMap<>(); + this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>()); + pluginInstance = new Plugin("fabric", this); this.worldUuids = new ConcurrentHashMap<>(); @@ -90,6 +104,8 @@ public void onInitialize() { }); ServerStartCallback.EVENT.register((MinecraftServer server) -> { + this.serverInstance = server; + new Thread(()->{ Logger.global.logInfo("Loading BlueMap..."); @@ -106,6 +122,13 @@ public void onInitialize() { pluginInstance.unload(); Logger.global.logInfo("BlueMap unloaded!"); }); + + PlayerJoinCallback.EVENT.register(this::onPlayerJoin); + PlayerLeaveCallback.EVENT.register(this::onPlayerLeave); + + ServerTickCallback.EVENT.register((MinecraftServer server) -> { + if (server == this.serverInstance) this.updateSomePlayers(); + }); } @Override @@ -151,16 +174,57 @@ public File getConfigFolder() { return new File("config/bluemap"); } + public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) { + if (this.serverInstance != server) return; + + FabricPlayer player = new FabricPlayer(this, playerInstance); + onlinePlayerMap.put(player.getUuid(), player); + onlinePlayerList.add(player); + } + + public void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) { + if (this.serverInstance != server) return; + + UUID playerUUID = player.getUuid(); + onlinePlayerMap.remove(playerUUID); + synchronized (onlinePlayerList) { + onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); + } + } + + public MinecraftServer getServer() { + return this.serverInstance; + } + @Override public Collection getOnlinePlayers() { - // TODO Implement - return Collections.emptyList(); + return onlinePlayerMap.values(); } @Override public Optional getPlayer(UUID uuid) { - // TODO Implement - return Optional.empty(); + 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(i).update(); + } + } } } diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java new file mode 100644 index 00000000..13b7ca66 --- /dev/null +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -0,0 +1,155 @@ +/* + * 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.fabric; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.EnumMap; +import java.util.Map; +import java.util.UUID; + +import com.flowpowered.math.vector.Vector3d; + +import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; +import de.bluecolored.bluemap.common.plugin.text.Text; +import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.GameMode; + +public class FabricPlayer implements Player { + + private static final UUID UNKNOWN_WORLD_UUID = UUID.randomUUID(); + + private static final Map GAMEMODE_MAP = new EnumMap<>(GameMode.class); + static { + GAMEMODE_MAP.put(GameMode.ADVENTURE, Gamemode.ADVENTURE); + GAMEMODE_MAP.put(GameMode.SURVIVAL, Gamemode.SURVIVAL); + GAMEMODE_MAP.put(GameMode.CREATIVE, Gamemode.CREATIVE); + GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR); + } + + private UUID uuid; + private Text name; + private UUID world; + private Vector3d position; + private boolean online; + private boolean sneaking; + private boolean invisible; + private Gamemode gamemode; + + private FabricMod mod; + private WeakReference delegate; + + public FabricPlayer(FabricMod mod, ServerPlayerEntity delegate) { + this.uuid = delegate.getUuid(); + this.mod = mod; + this.delegate = new WeakReference<>(delegate); + + update(); + } + + @Override + public UUID getUuid() { + return this.uuid; + } + + @Override + public Text getName() { + return this.name; + } + + @Override + public UUID getWorld() { + return this.world; + } + + @Override + public Vector3d getPosition() { + return this.position; + } + + @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; + } + + /** + * API access, only call on server thread! + */ + public void update() { + ServerPlayerEntity player = delegate.get(); + if (player == null) { + MinecraftServer server = mod.getServer(); + if (server != null) { + player = server.getPlayerManager().getPlayer(uuid); + } + + if (player == null) { + this.online = false; + return; + } + + delegate = new WeakReference<>(player); + } + + this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode()); + + StatusEffectInstance invis = player.getStatusEffect(StatusEffects.INVISIBILITY); + this.invisible = invis != null && invis.getDuration() > 0; + + this.name = Text.of(player.getName().getString()); + this.online = !player.removed; + + Vec3d pos = player.getPos(); + this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.sneaking = player.isSneaking(); + + try { + this.world = mod.getUUIDForWorld(player.getServerWorld()); + } catch (IOException e) { + this.world = UNKNOWN_WORLD_UUID; + } + } + +} diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerJoinCallback.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerJoinCallback.java new file mode 100644 index 00000000..d564012c --- /dev/null +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerJoinCallback.java @@ -0,0 +1,42 @@ +/* + * 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.fabric.events; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; + +public interface PlayerJoinCallback { + Event EVENT = EventFactory.createArrayBacked(PlayerJoinCallback.class, + (listeners) -> (server, player) -> { + for (PlayerJoinCallback event : listeners) { + event.onPlayerJoin(server, player); + } + } + ); + + void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player); +} diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerLeaveCallback.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerLeaveCallback.java new file mode 100644 index 00000000..f1ce2922 --- /dev/null +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/events/PlayerLeaveCallback.java @@ -0,0 +1,42 @@ +/* + * 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.fabric.events; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; + +public interface PlayerLeaveCallback { + Event EVENT = EventFactory.createArrayBacked(PlayerLeaveCallback.class, + (listeners) -> (server, player) -> { + for (PlayerLeaveCallback event : listeners) { + event.onPlayerLeave(server, player); + } + } + ); + + void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player); +} diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinPlayerManager.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinPlayerManager.java new file mode 100644 index 00000000..b7109357 --- /dev/null +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinPlayerManager.java @@ -0,0 +1,56 @@ +/* + * 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.fabric.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import de.bluecolored.bluemap.fabric.events.PlayerJoinCallback; +import de.bluecolored.bluemap.fabric.events.PlayerLeaveCallback; +import net.minecraft.network.ClientConnection; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; + +@Mixin(PlayerManager.class) +public abstract class MixinPlayerManager { + + @Shadow + public abstract MinecraftServer getServer(); + + @Inject(at = @At("RETURN"), method = "onPlayerConnect") + public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { + PlayerJoinCallback.EVENT.invoker().onPlayerJoin(this.getServer(), player); + } + + @Inject(at = @At("HEAD"), method = "remove") + public void remove(ServerPlayerEntity player, CallbackInfo ci) { + PlayerLeaveCallback.EVENT.invoker().onPlayerLeave(this.getServer(), player); + } + +} diff --git a/BlueMapFabric/src/main/resources/bluemap.mixins.json b/BlueMapFabric/src/main/resources/bluemap.mixins.json index e8c98a84..84d08a94 100644 --- a/BlueMapFabric/src/main/resources/bluemap.mixins.json +++ b/BlueMapFabric/src/main/resources/bluemap.mixins.json @@ -6,8 +6,9 @@ "mixins": [], "client": [], "server": [ - "MixinServerWorld", - "MixinChunkGenerator" + "MixinChunkGenerator", + "MixinPlayerManager", + "MixinServerWorld" ], "injectors": { "defaultRequire": 1 From e72fb4f21abde581151df5b316766f626d9eb64a Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Fri, 14 Aug 2020 20:16:06 +0200 Subject: [PATCH 07/10] Add forge live-implementation, improve fabric implementation and fix possible CME --- .../bluemap/bukkit/EventForwarder.java | 18 +- .../bluemap/fabric/FabricEventForwarder.java | 34 +-- .../bluecolored/bluemap/fabric/FabricMod.java | 10 +- .../bluemap/fabric/FabricPlayer.java | 5 +- .../bluemap/forge/ForgeEventForwarder.java | 116 ++++++++++ .../bluecolored/bluemap/forge/ForgeMod.java | 208 +++++++++--------- .../bluemap/forge/ForgePlayer.java | 156 +++++++++++++ 7 files changed, 410 insertions(+), 137 deletions(-) create mode 100644 BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java create mode 100644 BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java diff --git a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java index 775768ef..b800eaa5 100644 --- a/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java +++ b/BlueMapBukkit/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java @@ -72,7 +72,7 @@ public synchronized void removeAllListeners() { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public synchronized void onWorldSaveToDisk(WorldSaveEvent evt) { - listeners.forEach(l -> l.onWorldSaveToDisk(evt.getWorld().getUID())); + for (ServerEventListener listener : listeners) listener.onWorldSaveToDisk(evt.getWorld().getUID()); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -123,7 +123,7 @@ public void onBlockChange(BlockFertilizeEvent evt) { private synchronized void onBlockChange(Location loc) { UUID world = loc.getWorld().getUID(); Vector3i pos = new Vector3i(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - listeners.forEach(l -> l.onBlockChange(world, pos)); + for (ServerEventListener listener : listeners) listener.onBlockChange(world, pos); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -131,23 +131,23 @@ public synchronized void onChunkFinishedGeneration(ChunkPopulateEvent evt) { Chunk chunk = evt.getChunk(); UUID world = chunk.getWorld().getUID(); Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ()); - listeners.forEach(l -> l.onChunkFinishedGeneration(world, chunkPos)); + for (ServerEventListener listener : listeners) listener.onChunkFinishedGeneration(world, chunkPos); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerJoin(PlayerJoinEvent evt) { - listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId())); + public synchronized void onPlayerJoin(PlayerJoinEvent evt) { + for (ServerEventListener listener : listeners) listener.onPlayerJoin(evt.getPlayer().getUniqueId()); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerLeave(PlayerQuitEvent evt) { - listeners.forEach(l -> l.onPlayerJoin(evt.getPlayer().getUniqueId())); + public synchronized void onPlayerLeave(PlayerQuitEvent evt) { + for (ServerEventListener listener : listeners) listener.onPlayerJoin(evt.getPlayer().getUniqueId()); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onPlayerChat(AsyncPlayerChatEvent evt) { + public synchronized void onPlayerChat(AsyncPlayerChatEvent evt) { String message = String.format(evt.getFormat(), evt.getPlayer().getDisplayName(), evt.getMessage()); - listeners.forEach(l -> l.onChatMessage(Text.of(message))); + for (ServerEventListener listener : listeners) listener.onChatMessage(Text.of(message)); } } diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index 230134f0..c96846e6 100644 --- a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -69,11 +69,11 @@ public FabricEventForwarder(FabricMod mod) { PlayerLeaveCallback.EVENT.register(this::onPlayerLeave); } - public void addEventListener(ServerEventListener listener) { + public synchronized void addEventListener(ServerEventListener listener) { this.eventListeners.add(listener); } - public void removeAllListeners() { + public synchronized void removeAllListeners() { this.eventListeners.clear(); } @@ -94,45 +94,47 @@ public ActionResult onBlockAttack(PlayerEntity player, World world, Hand hand, B return ActionResult.PASS; } - public void onBlockChange(ServerWorld world, BlockPos blockPos) { + public synchronized void onBlockChange(ServerWorld world, BlockPos blockPos) { Vector3i position = new Vector3i(blockPos.getX(), blockPos.getY(), blockPos.getZ()); try { UUID uuid = mod.getUUIDForWorld(world); - eventListeners.forEach(e -> e.onBlockChange(uuid, position)); + for (ServerEventListener listener : eventListeners) listener.onBlockChange(uuid, position); } catch (IOException e) { - Logger.global.logError("Failed to get UUID for world: " + world, e); + Logger.global.noFloodError("Failed to get the UUID for a world!", e); } } - public void onWorldSave(ServerWorld world) { + public synchronized void onWorldSave(ServerWorld world) { try { UUID uuid = mod.getUUIDForWorld(world); - eventListeners.forEach(e -> e.onWorldSaveToDisk(uuid)); + for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(uuid); } catch (IOException e) { - Logger.global.logError("Failed to get UUID for world: " + world, e); + Logger.global.noFloodError("Failed to get the UUID for a world!", e); } } - public void onChunkFinalize(ServerWorld world, Vector2i chunkPos) { + public synchronized void onChunkFinalize(ServerWorld world, Vector2i chunkPos) { try { UUID uuid = mod.getUUIDForWorld(world); - eventListeners.forEach(e -> e.onChunkFinishedGeneration(uuid, chunkPos)); + for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(uuid, chunkPos); } catch (IOException e) { - Logger.global.logError("Failed to get UUID for world: " + world, e); + Logger.global.noFloodError("Failed to get the UUID for a world!", e); } } - public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) { + public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) { if (this.mod.getServer() != server) return; - this.eventListeners.forEach(l -> l.onPlayerJoin(player.getUuid())); + UUID uuid = player.getUuid(); + for (ServerEventListener listener : eventListeners) listener.onPlayerJoin(uuid); } - public void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) { + public synchronized void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) { if (this.mod.getServer() != server) return; - - this.eventListeners.forEach(l -> l.onPlayerLeave(player.getUuid())); + + UUID uuid = player.getUuid(); + for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); } } diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index 203f4693..e4f2f6da 100644 --- a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -65,7 +65,7 @@ public class FabricMod implements ModInitializer, ServerInterface { private Plugin pluginInstance = null; private MinecraftServer serverInstance = null; - private Map worldUuids; + private Map worldUUIDs; private FabricEventForwarder eventForwarder; private LoadingCache worldUuidCache; @@ -82,7 +82,7 @@ public FabricMod() { pluginInstance = new Plugin("fabric", this); - this.worldUuids = new ConcurrentHashMap<>(); + this.worldUUIDs = new ConcurrentHashMap<>(); this.eventForwarder = new FabricEventForwarder(this); this.worldUuidCache = CacheBuilder.newBuilder() .weakKeys() @@ -111,7 +111,7 @@ public void onInitialize() { try { pluginInstance.load(); - Logger.global.logInfo("BlueMap loaded!"); + if (pluginInstance.isLoaded()) Logger.global.logInfo("BlueMap loaded!"); } catch (IOException | ParseResourceException e) { Logger.global.logError("Failed to load bluemap!", e); } @@ -145,10 +145,10 @@ public void unregisterAllListeners() { public UUID getUUIDForWorld(File worldFolder) throws IOException { worldFolder = worldFolder.getCanonicalFile(); - UUID uuid = worldUuids.get(worldFolder); + UUID uuid = worldUUIDs.get(worldFolder); if (uuid == null) { uuid = UUID.randomUUID(); - worldUuids.put(worldFolder, uuid); + worldUUIDs.put(worldFolder, uuid); } return uuid; diff --git a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index 13b7ca66..7c8ce5b6 100644 --- a/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/BlueMapFabric/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -52,6 +52,7 @@ public class FabricPlayer implements Player { GAMEMODE_MAP.put(GameMode.SURVIVAL, Gamemode.SURVIVAL); GAMEMODE_MAP.put(GameMode.CREATIVE, Gamemode.CREATIVE); GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR); + GAMEMODE_MAP.put(GameMode.NOT_SET, Gamemode.SURVIVAL); } private UUID uuid; @@ -115,7 +116,7 @@ public Gamemode getGamemode() { } /** - * API access, only call on server thread! + * Only call on server thread! */ public void update() { ServerPlayerEntity player = delegate.get(); @@ -139,7 +140,7 @@ public void update() { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = !player.removed; + this.online = true; Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java new file mode 100644 index 00000000..4c0aa278 --- /dev/null +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -0,0 +1,116 @@ +/* + * 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 java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + +import com.flowpowered.math.vector.Vector3i; + +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.core.logger.Logger; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +public class ForgeEventForwarder { + + private ForgeMod mod; + private Collection eventListeners; + + public ForgeEventForwarder(ForgeMod mod) { + this.mod = mod; + 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 void onBlockBreak(BlockEvent.BreakEvent evt) { + onBlockChange(evt); + } + + @SubscribeEvent + public void onBlockPlace(BlockEvent.EntityPlaceEvent evt) { + onBlockChange(evt); + } + + private synchronized void onBlockChange(BlockEvent evt) { + if (!(evt.getWorld() instanceof ServerWorld)) return; + + try { + UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); + Vector3i position = new Vector3i( + evt.getPos().getX(), + evt.getPos().getY(), + evt.getPos().getZ() + ); + + for (ServerEventListener listener : eventListeners) listener.onBlockChange(world, position); + + } catch (IOException e) { + Logger.global.noFloodError("Failed to get the UUID for a world!", e); + } + } + + @SubscribeEvent + public synchronized void onWorldSave(WorldEvent.Save evt) { + if (!(evt.getWorld() instanceof ServerWorld)) return; + + try { + UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); + for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(world); + } catch (IOException e) { + Logger.global.noFloodError("Failed to get the UUID for a world!", e); + } + } + + @SubscribeEvent + public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) { + UUID uuid = evt.getPlayer().getUniqueID(); + for (ServerEventListener listener : eventListeners) listener.onPlayerJoin(uuid); + } + + @SubscribeEvent + public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { + UUID uuid = evt.getPlayer().getUniqueID(); + for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); + } + +} diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 94830ebd..0ebdb700 100644 --- a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -29,15 +29,15 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; -import com.flowpowered.math.vector.Vector3i; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -48,11 +48,15 @@ import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.logger.Logger; -import net.minecraft.command.CommandSource; +import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.server.MinecraftServer; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.event.world.WorldEvent; +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.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; @@ -60,20 +64,29 @@ @Mod(Plugin.PLUGIN_ID) public class ForgeMod implements ServerInterface { + + private Plugin pluginInstance = null; + private MinecraftServer serverInstance = null; - private Plugin bluemap; - private Commands commands; - private Map worldUUIDs; - private Collection eventListeners; + private Map worldUUIDs; + private ForgeEventForwarder eventForwarder; private LoadingCache worldUuidCache; + private int playerUpdateIndex = 0; + private Map onlinePlayerMap; + private List onlinePlayerList; + public ForgeMod() { Logger.global = new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME)); - this.bluemap = new Plugin("forge", this); - this.worldUUIDs = new HashMap<>(); - this.eventListeners = new ArrayList<>(1); + this.onlinePlayerMap = new ConcurrentHashMap<>(); + this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>()); + + this.pluginInstance = new Plugin("forge", this); + + this.worldUUIDs = new ConcurrentHashMap<>(); + this.eventForwarder = new ForgeEventForwarder(this); this.worldUuidCache = CacheBuilder.newBuilder() .weakKeys() .maximumSize(1000) @@ -83,113 +96,61 @@ public UUID load(ServerWorld key) throws Exception { return loadUUIDForWorld(key); } }); - + MinecraftForge.EVENT_BUS.register(this); } @SubscribeEvent public void onServerStarting(FMLServerStartingEvent event) { this.worldUUIDs.clear(); - - for (ServerWorld world : event.getServer().getWorlds()) { - try { - registerWorld(world); - } catch (IOException e) { - Logger.global.logError("Failed to register world: " + world.getProviderName(), e); - } - - try { - world.save(null, false, false); - } catch (Throwable t) { - Logger.global.logError("Failed to save world: " + world.getProviderName(), t); - } - } //register commands - this.commands = new Commands<>(bluemap, event.getCommandDispatcher(), forgeSource -> new ForgeCommandSource(this, bluemap, forgeSource)); + new Commands<>(pluginInstance, event.getCommandDispatcher(), forgeSource -> new ForgeCommandSource(this, pluginInstance, forgeSource)); new Thread(() -> { + Logger.global.logInfo("Loading..."); + try { - Logger.global.logInfo("Loading..."); - bluemap.load(); - if (bluemap.isLoaded()) Logger.global.logInfo("Loaded!"); - } catch (Throwable t) { - Logger.global.logError("Failed to load!", t); + pluginInstance.load(); + if (pluginInstance.isLoaded()) Logger.global.logInfo("Loaded!"); + } catch (IOException | ParseResourceException e) { + Logger.global.logError("Failed to load bluemap!", e); } }).start(); } - private void registerWorld(ServerWorld world) throws IOException { - getUUIDForWorld(world); - } - @SubscribeEvent public void onServerStopping(FMLServerStoppingEvent event) { - Logger.global.logInfo("Stopping..."); - bluemap.unload(); - Logger.global.logInfo("Saved and stopped!"); + pluginInstance.unload(); + Logger.global.logInfo("BlueMap unloaded!"); } + + @SubscribeEvent + public void onTick(ServerTickEvent evt) { + updateSomePlayers(); + } @Override public void registerListener(ServerEventListener listener) { - eventListeners.add(listener); + eventForwarder.addEventListener(listener); } @Override public void unregisterAllListeners() { - eventListeners.clear(); - } - - @SubscribeEvent - public void onBlockBreak(BlockEvent.BreakEvent evt) { - onBlockChange(evt); - } - - @SubscribeEvent - public void onBlockPlace(BlockEvent.EntityPlaceEvent evt) { - onBlockChange(evt); - } - - private void onBlockChange(BlockEvent evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - try { - UUID world = getUUIDForWorld((ServerWorld) evt.getWorld()); - Vector3i position = new Vector3i( - evt.getPos().getX(), - evt.getPos().getY(), - evt.getPos().getZ() - ); - - for (ServerEventListener listener : eventListeners) listener.onBlockChange(world, position); - - } catch (IOException ignore) {} - } - - @SubscribeEvent - public void onWorldSave(WorldEvent.Save evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - try { - UUID world = getUUIDForWorld((ServerWorld) evt.getWorld()); - - for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(world); - - } catch (IOException ignore) {} + eventForwarder.removeAllListeners(); } @Override public UUID getUUIDForWorld(File worldFolder) throws IOException { - synchronized (worldUUIDs) { - String key = worldFolder.getCanonicalPath(); - - UUID uuid = worldUUIDs.get(key); - if (uuid == null) { - throw new IOException("There is no world with this folder loaded: " + worldFolder.getPath()); - } - - return uuid; + worldFolder = worldFolder.getCanonicalFile(); + + UUID uuid = worldUUIDs.get(worldFolder); + if (uuid == null) { + uuid = UUID.randomUUID(); + worldUUIDs.put(worldFolder, uuid); } + + return uuid; } public UUID getUUIDForWorld(ServerWorld world) throws IOException { @@ -203,17 +164,15 @@ public UUID getUUIDForWorld(ServerWorld world) throws IOException { } private UUID loadUUIDForWorld(ServerWorld world) throws IOException { - synchronized (worldUUIDs) { - String key = getFolderForWorld(world).getPath(); - - UUID uuid = worldUUIDs.get(key); - if (uuid == null) { - uuid = UUID.randomUUID(); - worldUUIDs.put(key, uuid); - } - - return uuid; + File key = getFolderForWorld(world); + + UUID uuid = worldUUIDs.get(key); + if (uuid == null) { + uuid = UUID.randomUUID(); + worldUUIDs.put(key, uuid); } + + return uuid; } private File getFolderForWorld(ServerWorld world) throws IOException { @@ -231,21 +190,60 @@ private File getFolderForWorld(ServerWorld world) throws IOException { public File getConfigFolder() { return new File("config/bluemap"); } + + public void onPlayerJoin(PlayerLoggedInEvent evt) { + PlayerEntity playerInstance = evt.getPlayer(); + if (!(playerInstance instanceof ServerPlayerEntity)) return; + + ForgePlayer player = new ForgePlayer(this, (ServerPlayerEntity) playerInstance); + onlinePlayerMap.put(player.getUuid(), player); + onlinePlayerList.add(player); + } + + public void onPlayerLeave(PlayerLoggedOutEvent evt) { + PlayerEntity player = evt.getPlayer(); + if (!(player instanceof ServerPlayerEntity)) return; + + UUID playerUUID = player.getUniqueID(); + onlinePlayerMap.remove(playerUUID); + synchronized (onlinePlayerList) { + onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID)); + } + } - public Commands getCommands() { - return commands; + public MinecraftServer getServer() { + return this.serverInstance; } @Override public Collection getOnlinePlayers() { - // TODO Implement - return Collections.emptyList(); + return onlinePlayerMap.values(); } @Override public Optional getPlayer(UUID uuid) { - // TODO Implement - return Optional.empty(); + 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(i).update(); + } + } } } diff --git a/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java new file mode 100644 index 00000000..546f466a --- /dev/null +++ b/BlueMapForge/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -0,0 +1,156 @@ +/* + * 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 java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.EnumMap; +import java.util.Map; +import java.util.UUID; + +import com.flowpowered.math.vector.Vector3d; + +import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; +import de.bluecolored.bluemap.common.plugin.serverinterface.Player; +import de.bluecolored.bluemap.common.plugin.text.Text; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.GameType; + +public class ForgePlayer implements Player { + + private static final UUID UNKNOWN_WORLD_UUID = UUID.randomUUID(); + + 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); + GAMEMODE_MAP.put(GameType.NOT_SET, Gamemode.SURVIVAL); + } + + private UUID uuid; + private Text name; + private UUID world; + private Vector3d position; + private boolean online; + private boolean sneaking; + private boolean invisible; + private Gamemode gamemode; + + private ForgeMod mod; + private WeakReference delegate; + + public ForgePlayer(ForgeMod mod, ServerPlayerEntity delegate) { + this.uuid = delegate.getUniqueID(); + this.mod = mod; + this.delegate = new WeakReference<>(delegate); + + update(); + } + + @Override + public UUID getUuid() { + return this.uuid; + } + + @Override + public Text getName() { + return this.name; + } + + @Override + public UUID getWorld() { + return this.world; + } + + @Override + public Vector3d getPosition() { + return this.position; + } + + @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() { + ServerPlayerEntity player = delegate.get(); + if (player == null) { + MinecraftServer server = mod.getServer(); + if (server != null) { + player = server.getPlayerList().getPlayerByUUID(uuid); + } + + if (player == null) { + this.online = false; + return; + } + + delegate = new WeakReference<>(player); + } + + this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameType()); + + EffectInstance invis = player.getActivePotionEffect(Effects.INVISIBILITY); + this.invisible = invis != null && invis.getDuration() > 0; + + this.name = Text.of(player.getName().getString()); + this.online = true; + + Vec3d pos = player.getPositionVec(); + this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); + this.sneaking = player.isSneaking(); + + try { + this.world = mod.getUUIDForWorld(player.getServerWorld()); + } catch (IOException e) { + this.world = UNKNOWN_WORLD_UUID; + } + } + +} From 3a5dac58ea406fe7dc491d3bf68b8e0997bbbc67 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sat, 15 Aug 2020 15:22:10 +0200 Subject: [PATCH 08/10] Remove unneeded imports --- .../de/bluecolored/bluemap/core/web/FileRequestHandler.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java index ca439667..ca77973d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java @@ -33,9 +33,7 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.GregorianCalendar; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.TimeUnit; From 1716098622ce90c7a651758cac3b7d52ee7db376 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sun, 16 Aug 2020 13:49:40 +0200 Subject: [PATCH 09/10] Add live-api configurations --- .../resources/bluemap-bukkit-defaults.conf | 6 +++ .../src/main/resources/bluemap-bukkit.conf | 21 ++++++++++ .../bluemap/common/BlueMapWebServer.java | 5 ++- .../common/live/LiveAPIRequestHandler.java | 16 ++++--- .../bluemap/common/plugin/Plugin.java | 8 ++-- .../plugin/serverinterface/Gamemode.java | 28 +++++++++++-- .../bluemap/core/config/LiveAPISettings.java | 39 +++++++++++++++++ .../bluemap/core/config/MainConfig.java | 42 ++++++++++++++++++- .../resources/bluemap-fabric-defaults.conf | 6 +++ .../src/main/resources/bluemap-fabric.conf | 21 ++++++++++ .../resources/bluemap-forge-defaults.conf | 6 +++ .../src/main/resources/bluemap-forge.conf | 21 ++++++++++ .../resources/bluemap-sponge-defaults.conf | 6 +++ .../src/main/resources/bluemap-sponge.conf | 20 +++++++++ 14 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/LiveAPISettings.java diff --git a/BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf b/BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf index c6f20621..98e58691 100644 --- a/BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf +++ b/BlueMapBukkit/src/main/resources/bluemap-bukkit-defaults.conf @@ -9,3 +9,9 @@ webserver { port: 8100 maxConnectionCount: 100 } +liveUpdates { + enabled: true + hiddenGameModes: [] + hideInvisible: true + hideSneaking: false +} diff --git a/BlueMapBukkit/src/main/resources/bluemap-bukkit.conf b/BlueMapBukkit/src/main/resources/bluemap-bukkit.conf index c3a60ee9..bf44441c 100644 --- a/BlueMapBukkit/src/main/resources/bluemap-bukkit.conf +++ b/BlueMapBukkit/src/main/resources/bluemap-bukkit.conf @@ -38,6 +38,7 @@ webroot: "bluemap/web" #webdata: "path/to/data/folder" # If the web-application should use cookies to save the configurations of a user. +# Default is true useCookies: true webserver { @@ -165,3 +166,23 @@ maps: [ } ] + +liveUpdates { + # If the server should send live-updates and player-positions. + # Default is true + enabled: true + + # A list of gamemodes that will prevent a player from appearing on the map. + # Possible values are: survival, creative, spectator, adventure + hiddenGameModes: [ + "spectator" + ] + + # If this is true, players that have an invisibility (potion-)effect will be hidden on the map. + # Default is true + hideInvisible: true + + # If this is true, players that are sneaking will be hidden on the map. + # Default is false + hideSneaking: false +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java index e51dd030..bdfd1fe9 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapWebServer.java @@ -28,6 +28,7 @@ import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; +import de.bluecolored.bluemap.core.config.LiveAPISettings; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.web.FileRequestHandler; import de.bluecolored.bluemap.core.web.WebFilesManager; @@ -50,12 +51,12 @@ public BlueMapWebServer(WebServerConfig config) { this.webFilesManager = new WebFilesManager(config.getWebRoot()); } - public BlueMapWebServer(WebServerConfig config, ServerInterface server) { + public BlueMapWebServer(WebServerConfig config, LiveAPISettings liveSettings, ServerInterface server) { super( config.getWebserverPort(), config.getWebserverMaxConnections(), config.getWebserverBindAdress(), - new LiveAPIRequestHandler(server, new FileRequestHandler(config.getWebRoot(), "BlueMap/Webserver")) + new LiveAPIRequestHandler(server, liveSettings, new FileRequestHandler(config.getWebRoot(), "BlueMap/Webserver")) ); this.webFilesManager = new WebFilesManager(config.getWebRoot()); 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 index 94dbdf3f..2483cb71 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java @@ -31,9 +31,9 @@ import com.google.gson.stream.JsonWriter; -import de.bluecolored.bluemap.common.plugin.serverinterface.Gamemode; import de.bluecolored.bluemap.common.plugin.serverinterface.Player; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; +import de.bluecolored.bluemap.core.config.LiveAPISettings; import de.bluecolored.bluemap.core.webserver.HttpRequest; import de.bluecolored.bluemap.core.webserver.HttpRequestHandler; import de.bluecolored.bluemap.core.webserver.HttpResponse; @@ -45,7 +45,9 @@ public class LiveAPIRequestHandler implements HttpRequestHandler { private Map liveAPIRequests; private ServerInterface server; - public LiveAPIRequestHandler(ServerInterface server, HttpRequestHandler notFoundHandler) { + private LiveAPISettings config; + + public LiveAPIRequestHandler(ServerInterface server, LiveAPISettings config, HttpRequestHandler notFoundHandler) { this.server = server; this.notFoundHandler = notFoundHandler; @@ -53,10 +55,14 @@ public LiveAPIRequestHandler(ServerInterface server, HttpRequestHandler notFound this.liveAPIRequests.put("live", this::handleLivePingRequest); this.liveAPIRequests.put("live/players", this::handlePlayersRequest); + + this.config = config; } @Override public HttpResponse handle(HttpRequest request) { + if (!config.isLiveUpdatesEnabled()) this.notFoundHandler.handle(request); + HttpRequestHandler handler = liveAPIRequests.get(request.getPath()); if (handler != null) return handler.handle(request); @@ -81,9 +87,9 @@ public HttpResponse handlePlayersRequest(HttpRequest request) { json.name("players").beginArray(); for (Player player : server.getOnlinePlayers()) { - if (player.isInvisible()) continue; - if (player.isSneaking()) continue; - if (player.getGamemode() == Gamemode.SPECTATOR) continue; + if (config.isHideInvisible() && player.isInvisible()) continue; + if (config.isHideSneaking() && player.isSneaking()) continue; + if (config.getHiddenGameModes().contains(player.getGamemode().getId())) continue; json.beginObject(); json.name("uuid").value(player.getUuid().toString()); 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 5bdfff37..4c99bdac 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 @@ -272,8 +272,10 @@ public synchronized void load() throws IOException, ParseResourceException { serverInterface.registerListener(updateHandler); //start skin updater - this.skinUpdater = new PlayerSkinUpdater(config.getWebRoot().resolve("assets").resolve("playerheads").toFile()); - serverInterface.registerListener(skinUpdater); + if (config.isLiveUpdatesEnabled()) { + this.skinUpdater = new PlayerSkinUpdater(config.getWebRoot().resolve("assets").resolve("playerheads").toFile()); + serverInterface.registerListener(skinUpdater); + } //create/update webfiles WebFilesManager webFilesManager = new WebFilesManager(config.getWebRoot()); @@ -299,7 +301,7 @@ public synchronized void load() throws IOException, ParseResourceException { //start webserver if (config.isWebserverEnabled()) { - webServer = new BlueMapWebServer(config, serverInterface); + webServer = new BlueMapWebServer(config, config, serverInterface); webServer.updateWebfiles(); webServer.start(); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java index 9012e4b9..2ed0e03a 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/serverinterface/Gamemode.java @@ -26,9 +26,29 @@ public enum Gamemode { - SURVIVAL, - CREATIVE, - ADVENTURE, - SPECTATOR + SURVIVAL ("survival"), + CREATIVE ("creative"), + ADVENTURE ("adventure"), + SPECTATOR ("spectator"); + + private final String id; + + Gamemode(String id){ + this.id = id; + } + + public String getId() { + return id; + } + + public static Gamemode getById(String id) { + if (id == null) throw new NullPointerException("id cannot be null"); + + for (Gamemode gamemode : values()) { + if (gamemode.id.equals(id)) return gamemode; + } + + throw new IllegalArgumentException("There is no Gamemode with id: '" + id + "'"); + } } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/LiveAPISettings.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/LiveAPISettings.java new file mode 100644 index 00000000..121008e9 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/LiveAPISettings.java @@ -0,0 +1,39 @@ +/* + * 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.core.config; + +import java.util.Collection; + +public interface LiveAPISettings { + + boolean isLiveUpdatesEnabled(); + + Collection getHiddenGameModes(); + + boolean isHideInvisible(); + + boolean isHideSneaking(); + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java index 1e1b820e..c7da1a5d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MainConfig.java @@ -31,6 +31,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.regex.Pattern; @@ -43,7 +45,7 @@ import de.bluecolored.bluemap.core.web.WebServerConfig; import ninja.leaping.configurate.ConfigurationNode; -public class MainConfig implements WebServerConfig { +public class MainConfig implements WebServerConfig, LiveAPISettings { private static final Pattern VALID_ID_PATTERN = Pattern.compile("[a-zA-Z0-9_]+"); private boolean downloadAccepted = false; @@ -64,6 +66,11 @@ public class MainConfig implements WebServerConfig { private List mapConfigs = new ArrayList<>(); + private boolean liveUpdatesEnabled = false; + private Collection hiddenGameModes = Collections.emptyList(); + private boolean hideInvisible = false; + private boolean hideSneaking = false; + public MainConfig(ConfigurationNode node) throws OutdatedConfigException, IOException { checkOutdated(node); @@ -101,6 +108,19 @@ public MainConfig(ConfigurationNode node) throws OutdatedConfigException, IOExce //maps loadMapConfigs(node.getNode("maps")); + + //live-updates + ConfigurationNode liveNode = node.getNode("liveUpdates"); + liveUpdatesEnabled = liveNode.getNode("enabled").getBoolean(true); + + hiddenGameModes = new ArrayList<>(); + for (ConfigurationNode gameModeNode : liveNode.getNode("hiddenGameModes").getChildrenList()) { + hiddenGameModes.add(gameModeNode.getString()); + } + hiddenGameModes = Collections.unmodifiableCollection(hiddenGameModes); + + hideInvisible = liveNode.getNode("hideInvisible").getBoolean(true); + hideSneaking = liveNode.getNode("hideSneaking").getBoolean(false); } private void loadWebConfig(ConfigurationNode node) throws IOException { @@ -196,6 +216,26 @@ public List getMapConfigs(){ return mapConfigs; } + @Override + public boolean isLiveUpdatesEnabled() { + return this.liveUpdatesEnabled; + } + + @Override + public Collection getHiddenGameModes() { + return this.hiddenGameModes; + } + + @Override + public boolean isHideInvisible() { + return this.hideInvisible; + } + + @Override + public boolean isHideSneaking() { + return this.hideSneaking; + } + public class MapConfig implements RenderSettings { private String id; diff --git a/BlueMapFabric/src/main/resources/bluemap-fabric-defaults.conf b/BlueMapFabric/src/main/resources/bluemap-fabric-defaults.conf index c6f20621..98e58691 100644 --- a/BlueMapFabric/src/main/resources/bluemap-fabric-defaults.conf +++ b/BlueMapFabric/src/main/resources/bluemap-fabric-defaults.conf @@ -9,3 +9,9 @@ webserver { port: 8100 maxConnectionCount: 100 } +liveUpdates { + enabled: true + hiddenGameModes: [] + hideInvisible: true + hideSneaking: false +} diff --git a/BlueMapFabric/src/main/resources/bluemap-fabric.conf b/BlueMapFabric/src/main/resources/bluemap-fabric.conf index af9edf92..c56e51a9 100644 --- a/BlueMapFabric/src/main/resources/bluemap-fabric.conf +++ b/BlueMapFabric/src/main/resources/bluemap-fabric.conf @@ -38,6 +38,7 @@ webroot: "bluemap/web" #webdata: "path/to/data/folder" # If the web-application should use cookies to save the configurations of a user. +# Default is true useCookies: true webserver { @@ -165,3 +166,23 @@ maps: [ } ] + +liveUpdates { + # If the server should send live-updates and player-positions. + # Default is true + enabled: true + + # A list of gamemodes that will prevent a player from appearing on the map. + # Possible values are: survival, creative, spectator, adventure + hiddenGameModes: [ + "spectator" + ] + + # If this is true, players that have an invisibility (potion-)effect will be hidden on the map. + # Default is true + hideInvisible: true + + # If this is true, players that are sneaking will be hidden on the map. + # Default is false + hideSneaking: false +} diff --git a/BlueMapForge/src/main/resources/bluemap-forge-defaults.conf b/BlueMapForge/src/main/resources/bluemap-forge-defaults.conf index c6f20621..98e58691 100644 --- a/BlueMapForge/src/main/resources/bluemap-forge-defaults.conf +++ b/BlueMapForge/src/main/resources/bluemap-forge-defaults.conf @@ -9,3 +9,9 @@ webserver { port: 8100 maxConnectionCount: 100 } +liveUpdates { + enabled: true + hiddenGameModes: [] + hideInvisible: true + hideSneaking: false +} diff --git a/BlueMapForge/src/main/resources/bluemap-forge.conf b/BlueMapForge/src/main/resources/bluemap-forge.conf index af9edf92..c56e51a9 100644 --- a/BlueMapForge/src/main/resources/bluemap-forge.conf +++ b/BlueMapForge/src/main/resources/bluemap-forge.conf @@ -38,6 +38,7 @@ webroot: "bluemap/web" #webdata: "path/to/data/folder" # If the web-application should use cookies to save the configurations of a user. +# Default is true useCookies: true webserver { @@ -165,3 +166,23 @@ maps: [ } ] + +liveUpdates { + # If the server should send live-updates and player-positions. + # Default is true + enabled: true + + # A list of gamemodes that will prevent a player from appearing on the map. + # Possible values are: survival, creative, spectator, adventure + hiddenGameModes: [ + "spectator" + ] + + # If this is true, players that have an invisibility (potion-)effect will be hidden on the map. + # Default is true + hideInvisible: true + + # If this is true, players that are sneaking will be hidden on the map. + # Default is false + hideSneaking: false +} diff --git a/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf b/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf index f0a3bdb0..cf42dfc8 100644 --- a/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf +++ b/BlueMapSponge/src/main/resources/bluemap-sponge-defaults.conf @@ -9,3 +9,9 @@ webserver { port: 8100 maxConnectionCount: 100 } +liveUpdates { + enabled: true + hiddenGameModes: [] + hideInvisible: true + hideSneaking: false +} diff --git a/BlueMapSponge/src/main/resources/bluemap-sponge.conf b/BlueMapSponge/src/main/resources/bluemap-sponge.conf index c97e6ea8..c126f710 100644 --- a/BlueMapSponge/src/main/resources/bluemap-sponge.conf +++ b/BlueMapSponge/src/main/resources/bluemap-sponge.conf @@ -160,3 +160,23 @@ maps: [ } ] + +liveUpdates { + # If the server should send live-updates and player-positions. + # Default is true + enabled: true + + # A list of gamemodes that will prevent a player from appearing on the map. + # Possible values are: survival, creative, spectator, adventure + hiddenGameModes: [ + "spectator" + ] + + # If this is true, players that have an invisibility (potion-)effect will be hidden on the map. + # Default is true + hideInvisible: true + + # If this is true, players that are sneaking will be hidden on the map. + # Default is false + hideSneaking: false +} From 724d553a642f8be8568c486fa8167ede9db44e71 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Sun, 16 Aug 2020 14:47:21 +0200 Subject: [PATCH 10/10] Fix live-enable config option not working --- .../bluecolored/bluemap/common/live/LiveAPIRequestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 2483cb71..f61c611e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LiveAPIRequestHandler.java @@ -61,7 +61,7 @@ public LiveAPIRequestHandler(ServerInterface server, LiveAPISettings config, Htt @Override public HttpResponse handle(HttpRequest request) { - if (!config.isLiveUpdatesEnabled()) this.notFoundHandler.handle(request); + if (!config.isLiveUpdatesEnabled()) return this.notFoundHandler.handle(request); HttpRequestHandler handler = liveAPIRequests.get(request.getPath()); if (handler != null) return handler.handle(request);