diff --git a/BlueMapCommon/BlueMapVue b/BlueMapCommon/BlueMapVue index 4bef101c..514659e5 160000 --- a/BlueMapCommon/BlueMapVue +++ b/BlueMapCommon/BlueMapVue @@ -1 +1 @@ -Subproject commit 4bef101c6ee6ddef23049287cc1a3736d954979e +Subproject commit 514659e5dd4c0ad3f51ecb98cc70e5c1248d994d diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java index 57c74978..603e3594 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java @@ -24,20 +24,17 @@ */ package de.bluecolored.bluemap.common; -import com.flowpowered.math.vector.Vector2i; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.config.*; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.mca.MCAWorld; -import de.bluecolored.bluemap.core.render.RenderSettings; -import de.bluecolored.bluemap.core.render.TileRenderer; -import de.bluecolored.bluemap.core.render.hires.HiresModelManager; -import de.bluecolored.bluemap.core.render.lowres.LowresModelManager; +import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.resourcepack.ResourcePack; -import de.bluecolored.bluemap.core.web.WebSettings; +import de.bluecolored.bluemap.common.web.WebSettings; import de.bluecolored.bluemap.core.world.SlicedWorld; import de.bluecolored.bluemap.core.world.World; import org.apache.commons.io.FileUtils; @@ -51,12 +48,12 @@ * This is the attempt to generalize as many actions as possible to have CLI and Plugins run on the same general setup-code. */ public class BlueMapService { - private MinecraftVersion minecraftVersion; - private File configFolder; - private ThrowingFunction worldUUIDProvider; - private ThrowingFunction worldNameProvider; + private final MinecraftVersion minecraftVersion; + private final File configFolder; + private final ThrowingFunction worldUUIDProvider; + private final ThrowingFunction worldNameProvider; - private ConfigManager configManager; + private final ConfigManager configManager; private CoreConfig coreConfig; private RenderConfig renderConfig; @@ -65,7 +62,7 @@ public class BlueMapService { private ResourcePack resourcePack; private Map worlds; - private Map maps; + private Map maps; public BlueMapService(MinecraftVersion minecraftVersion, File configFolder) { this.minecraftVersion = minecraftVersion; @@ -106,16 +103,15 @@ public synchronized WebSettings updateWebAppSettings() throws IOException, Inter WebSettings webSettings = new WebSettings(new File(getRenderConfig().getWebRoot(), "data" + File.separator + "settings.json")); webSettings.set(getRenderConfig().isUseCookies(), "useCookies"); webSettings.setAllMapsEnabled(false); - for (MapType map : getMaps().values()) { + for (BmMap map : getMaps().values()) { webSettings.setMapEnabled(true, map.getId()); - webSettings.setFrom(map.getTileRenderer(), map.getId()); - webSettings.setFrom(map.getWorld(), map.getId()); + webSettings.setFrom(map); } int ordinal = 0; for (MapConfig map : getRenderConfig().getMapConfigs()) { if (!getMaps().containsKey(map.getId())) continue; //don't add not loaded maps webSettings.setOrdinal(ordinal++, map.getId()); - webSettings.setFrom(map, map.getId()); + webSettings.setFrom(map); } webSettings.save(); @@ -127,7 +123,7 @@ public synchronized Map getWorlds() throws IOException, Interrupted return worlds; } - public synchronized Map getMaps() throws IOException, InterruptedException { + public synchronized Map getMaps() throws IOException, InterruptedException { if (maps == null) loadWorldsAndMaps(); return maps; } @@ -135,6 +131,9 @@ public synchronized Map getMaps() throws IOException, Interrupt private synchronized void loadWorldsAndMaps() throws IOException, InterruptedException { maps = new HashMap<>(); worlds = new HashMap<>(); + + ConfigManager configManager = getConfigManager(); + configManager.loadResourceConfigs(configFolder, getResourcePack()); for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) { String id = mapConfig.getId(); @@ -154,9 +153,6 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc continue; } - ConfigManager configManager = getConfigManager(); - configManager.loadResourceConfigs(configFolder, getResourcePack()); - World world = worlds.get(worldUUID); if (world == null) { try { @@ -165,7 +161,7 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc } catch (MissingResourcesException e) { throw e; // rethrow this to stop loading and display resource-missing message } catch (IOException e) { - Logger.global.logError("Failed to load map '" + id + "': Failed to read level.dat", e); + Logger.global.logError("Failed to load map '" + id + "'!", e); continue; } } @@ -179,28 +175,20 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc world, mapConfig.getMin().min(mapConfig.getMin().sub(2, 2, 2)), // protect from int-overflow mapConfig.getMax().max(mapConfig.getMax().add(2, 2, 2)) // protect from int-overflow - ); + ); } } - - HiresModelManager hiresModelManager = new HiresModelManager( - getRenderConfig().getWebRoot().toPath().resolve("data").resolve(id).resolve("hires"), + + BmMap map = new BmMap( + id, + name, + world, + getRenderConfig().getWebRoot().toPath().resolve("data").resolve(id), getResourcePack(), - mapConfig, - new Vector2i(mapConfig.getHiresTileSize(), mapConfig.getHiresTileSize()) - ); - - LowresModelManager lowresModelManager = new LowresModelManager( - getRenderConfig().getWebRoot().toPath().resolve("data").resolve(id).resolve("lowres"), - new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()), - new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile()), - mapConfig.useGzipCompression() - ); - - TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager); - - MapType mapType = new MapType(id, name, world, tileRenderer); - maps.put(id, mapType); + mapConfig + ); + + maps.put(id, map); } worlds = Collections.unmodifiableMap(worlds); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/MapType.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/MapType.java deleted file mode 100644 index 188d0b82..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/MapType.java +++ /dev/null @@ -1,89 +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; - -import com.flowpowered.math.vector.Vector2i; -import com.google.common.base.Preconditions; - -import de.bluecolored.bluemap.core.render.TileRenderer; -import de.bluecolored.bluemap.core.render.WorldTile; -import de.bluecolored.bluemap.core.world.World; - -public class MapType { - - private final String id; - private String name; - private World world; - private TileRenderer tileRenderer; - - public MapType(String id, String name, World world, TileRenderer tileRenderer) { - Preconditions.checkNotNull(id); - Preconditions.checkNotNull(name); - Preconditions.checkNotNull(world); - Preconditions.checkNotNull(tileRenderer); - - this.id = id; - this.name = name; - this.world = world; - this.tileRenderer = tileRenderer; - } - - public String getId() { - return id; - } - - public String getName() { - return name; - } - - public World getWorld() { - return world; - } - - public TileRenderer getTileRenderer() { - return tileRenderer; - } - - public void renderTile(Vector2i tile) { - getTileRenderer().render(new WorldTile(getWorld(), tile)); - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj != null && obj instanceof MapType) { - MapType that = (MapType) obj; - - return this.id.equals(that.id); - } - - return false; - } - -} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java deleted file mode 100644 index f391238d..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java +++ /dev/null @@ -1,287 +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; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.flowpowered.math.vector.Vector2i; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.MultimapBuilder; - -import de.bluecolored.bluemap.core.logger.Logger; - -public class RenderManager { - - private volatile boolean running; - - private Thread[] renderThreads; - private ArrayDeque renderTickets; - private Map renderTicketMap; - private Deque renderTasks; - - public RenderManager(int threadCount) { - running = false; - renderThreads = new Thread[threadCount]; - renderTickets = new ArrayDeque<>(1000); - renderTicketMap = new HashMap<>(1000); - renderTasks = new ArrayDeque<>(); - } - - public synchronized void start() { - stop(); //ensure everything is stopped first - running = true; - - for (int i = 0; i < renderThreads.length; i++) { - renderThreads[i] = new Thread(this::renderThread); - renderThreads[i].setPriority(Thread.MIN_PRIORITY); - renderThreads[i].start(); - } - } - - public synchronized void stop() { - for (int i = 0; i < renderThreads.length; i++) { - if (renderThreads[i] != null) { - renderThreads[i].interrupt(); - } - } - - running = false; - } - - public void addRenderTask(RenderTask task) { - synchronized (renderTasks) { - renderTasks.add(task); - } - } - - public RenderTicket createTicket(MapType mapType, Vector2i tile) { - return createTicket(new RenderTicket(mapType, tile)); - } - - private RenderTicket createTicket(RenderTicket ticket) { - synchronized (renderTickets) { - if (renderTicketMap.putIfAbsent(ticket, ticket) == null) { - renderTickets.add(ticket); - return ticket; - } else { - return renderTicketMap.get(ticket); - } - } - } - - public Collection createTickets(MapType mapType, Collection tiles) { - if (tiles.size() < 0) return Collections.emptyList(); - - Collection tickets = new ArrayList<>(tiles.size()); - synchronized (renderTickets) { - for (Vector2i tile : tiles) { - tickets.add(createTicket(mapType, tile)); - } - } - - return tickets; - } - - public boolean prioritizeRenderTask(RenderTask renderTask) { - synchronized (renderTasks) { - if (renderTasks.remove(renderTask)) { - - //pause first task - RenderTask currentFirst = renderTasks.peek(); - if (currentFirst != null) currentFirst.pause(); - - renderTasks.addFirst(renderTask); - return true; - } - - return false; - } - } - - public boolean removeRenderTask(RenderTask renderTask) { - synchronized (renderTasks) { - return renderTasks.remove(renderTask); - } - } - - private void renderThread() { - RenderTicket ticket = null; - - while (running) { - synchronized (renderTickets) { - ticket = renderTickets.poll(); - if (ticket != null) renderTicketMap.remove(ticket); - } - - if (ticket == null) { - synchronized (renderTasks) { - RenderTask task = renderTasks.peek(); - if (task != null) { - ticket = task.poll(); - if (task.isFinished()) { - renderTasks.poll(); - task.getMapType().getTileRenderer().save(); - } - } - } - } - - if (ticket != null) { - try { - ticket.render(); - } catch (RuntimeException e) { - //catch possible runtime exceptions, display them, and wait a while .. then resurrect this render-thread - Logger.global.logError("Unexpected exception in render-thread!", e); - try { - Thread.sleep(10000); - } catch (InterruptedException interrupt) { Thread.currentThread().interrupt(); break; } - } - } else { - try { - Thread.sleep(1000); // we don't need a super fast response time, so waiting a second is totally fine - } catch (InterruptedException interrupt) { Thread.currentThread().interrupt(); break; } - } - - if (Thread.interrupted()) { - Thread.currentThread().interrupt(); - break; - } - } - } - - public int getQueueSize() { - return renderTickets.size(); - } - - public int getRenderThreadCount() { - return renderThreads.length; - } - - /** - * Returns a copy of the deque with the render tasks in order as array - */ - public RenderTask[] getRenderTasks(){ - synchronized (renderTasks) { - return renderTasks.toArray(new RenderTask[renderTasks.size()]); - } - } - - public int getRenderTaskCount(){ - return renderTasks.size(); - } - - public RenderTask getCurrentRenderTask() { - return renderTasks.peek(); - } - - public boolean isRunning() { - return running; - } - - public void writeState(DataOutputStream out) throws IOException { - //prepare renderTickets - ListMultimap tileMap = MultimapBuilder.hashKeys().arrayListValues().build(); - synchronized (renderTickets) { - for (RenderTicket ticket : renderTickets) { - tileMap.put(ticket.getMapType(), ticket.getTile()); - } - } - - //write renderTickets - Set maps = tileMap.keySet(); - out.writeInt(maps.size()); - for (MapType map : maps) { - List tiles = tileMap.get(map); - - out.writeUTF(map.getId()); - out.writeInt(tiles.size()); - for (Vector2i tile : tiles) { - out.writeInt(tile.getX()); - out.writeInt(tile.getY()); - } - } - - //write tasks - synchronized (renderTasks) { - out.writeInt(renderTasks.size()); - for (RenderTask task : renderTasks) { - task.write(out); - } - } - } - - public void readState(DataInputStream in, Collection mapTypes) throws IOException { - //read renderTickets - int mapCount = in.readInt(); - for (int i = 0; i < mapCount; i++) { - String mapId = in.readUTF(); - - MapType mapType = null; - for (MapType map : mapTypes) { - if (map.getId().equals(mapId)) { - mapType = map; - break; - } - } - if (mapType == null) { - Logger.global.logWarning("Some render-tickets can not be loaded because the map (id: '" + mapId + "') does not exist anymore. They will be discarded."); - } - - int tileCount = in.readInt(); - List tiles = new ArrayList<>(); - for (int j = 0; j < tileCount; j++) { - int x = in.readInt(); - int y = in.readInt(); - Vector2i tile = new Vector2i(x, y); - tiles.add(tile); - } - - if (mapType != null) createTickets(mapType, tiles); - } - - //read tasks - int taskCount = in.readInt(); - for (int i = 0; i < taskCount; i++) { - try { - RenderTask task = RenderTask.read(in, mapTypes); - addRenderTask(task); - } catch (IOException ex) { - Logger.global.logWarning("A render-task can not be loaded. It will be discared. (Error message: " + ex.toString() + ")"); - } - } - } - -} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTask.java deleted file mode 100644 index dddcdb17..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTask.java +++ /dev/null @@ -1,217 +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; - -import com.flowpowered.math.vector.Vector2d; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.*; - -public class RenderTask { - - private final UUID uuid; - private final String name; - - private final MapType mapType; - private final Deque renderTiles; - - private long firstTileTime; - private long additionalRunTime; - private int renderedTiles; - - public RenderTask(String name, MapType mapType) { - this.uuid = UUID.randomUUID(); - this.name = name; - this.mapType = mapType; - this.renderTiles = new ArrayDeque<>(); - this.firstTileTime = -1; - this.additionalRunTime = 0; - this.renderedTiles = 0; - } - - public void optimizeQueue(Vector2i centerBlockPos) { - //Find a good grid size to match the MCAWorlds chunk-cache size of 500 - Vector2d sortGridSize = new Vector2d(20, 20).div(mapType.getTileRenderer().getHiresModelManager().getTileSize().toDouble().div(16)).ceil().max(1, 1); - - Vector2i centerTile = mapType.getTileRenderer().getHiresModelManager().posToTile(new Vector3i(centerBlockPos.getX(), 0, centerBlockPos.getY())); - - synchronized (renderTiles) { - ArrayList tileList = new ArrayList<>(renderTiles); - tileList.sort((v1, v2) -> { - v1 = v1.sub(centerTile); - v2 = v2.sub(centerTile); - - Vector2i v1SortGridPos = v1.toDouble().div(sortGridSize).floor().toInt(); - Vector2i v2SortGridPos = v2.toDouble().div(sortGridSize).floor().toInt(); - - if (v1SortGridPos != v2SortGridPos){ - int v1Dist = v1SortGridPos.distanceSquared(Vector2i.ZERO); - int v2Dist = v2SortGridPos.distanceSquared(Vector2i.ZERO); - - if (v1Dist < v2Dist) return -1; - if (v1Dist > v2Dist) return 1; - - if (v1SortGridPos.getY() < v2SortGridPos.getY()) return -1; - if (v1SortGridPos.getY() > v2SortGridPos.getY()) return 1; - if (v1SortGridPos.getX() < v2SortGridPos.getX()) return -1; - if (v1SortGridPos.getX() > v2SortGridPos.getX()) return 1; - } - - if (v1.getY() < v2.getY()) return -1; - if (v1.getY() > v2.getY()) return 1; - if (v1.getX() < v2.getX()) return -1; - if (v1.getX() > v2.getX()) return 1; - - return 0; - }); - - renderTiles.clear(); - renderTiles.addAll(tileList); - } - } - - public void addTile(Vector2i tile) { - synchronized (renderTiles) { - renderTiles.add(tile); - } - } - - public void addTiles(Collection tiles) { - synchronized (renderTiles) { - renderTiles.addAll(tiles); - } - } - - public RenderTicket poll() { - synchronized (renderTiles) { - Vector2i tile = renderTiles.poll(); - if (tile != null) { - renderedTiles++; - if (firstTileTime < 0) firstTileTime = System.currentTimeMillis(); - return new RenderTicket(mapType, tile); - } else { - return null; - } - } - } - - /** - * Pauses the render-time counter. - * So if the rendering gets paused, the statistics remain correct. - * It will resume as soon as a new ticket gets polled - */ - public void pause() { - if (firstTileTime < 0) return; - - synchronized (renderTiles) { - additionalRunTime += System.currentTimeMillis() - firstTileTime; - firstTileTime = -1; - } - } - - public long getActiveTime() { - if (firstTileTime < 0) return additionalRunTime; - return (System.currentTimeMillis() - firstTileTime) + additionalRunTime; - } - - public UUID getUuid() { - return uuid; - } - - public String getName() { - return name; - } - - public MapType getMapType() { - return mapType; - } - - public int getRenderedTileCount() { - return renderedTiles; - } - - public int getRemainingTileCount() { - return renderTiles.size(); - } - - public boolean isFinished() { - return renderTiles.isEmpty(); - } - - public void write(DataOutputStream out) throws IOException { - synchronized (renderTiles) { - pause(); - - out.writeUTF(name); - out.writeUTF(mapType.getId()); - - out.writeLong(additionalRunTime); - out.writeInt(renderedTiles); - - out.writeInt(renderTiles.size()); - for (Vector2i tile : renderTiles) { - out.writeInt(tile.getX()); - out.writeInt(tile.getY()); - } - } - } - - public static RenderTask read(DataInputStream in, Collection mapTypes) throws IOException { - String name = in.readUTF(); - String mapId = in.readUTF(); - - MapType mapType = null; - for (MapType map : mapTypes) { - if (map.getId().equals(mapId)) { - mapType = map; - break; - } - } - if (mapType == null) throw new IOException("Map type with id '" + mapId + "' does not exist!"); - - RenderTask task = new RenderTask(name, mapType); - - task.additionalRunTime = in.readLong(); - task.renderedTiles = in.readInt(); - - int tileCount = in.readInt(); - List tiles = new ArrayList<>(); - for (int i = 0; i < tileCount; i++) { - int x = in.readInt(); - int y = in.readInt(); - Vector2i tile = new Vector2i(x, y); - tiles.add(tile); - } - - task.addTiles(tiles); - - return task; - } - -} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java deleted file mode 100644 index 69842450..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderTicket.java +++ /dev/null @@ -1,67 +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; - -import java.util.Objects; - -import com.flowpowered.math.vector.Vector2i; - -public class RenderTicket { - - private final MapType map; - private final Vector2i tile; - - public RenderTicket(MapType map, Vector2i tile) { - this.map = map; - this.tile = tile; - } - - public synchronized void render() { - map.renderTile(tile); - } - - public MapType getMapType() { - return map; - } - - public Vector2i getTile() { - return tile; - } - - @Override - public int hashCode() { - return Objects.hash(map.getId(), tile); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof RenderTicket)) return false; - RenderTicket ticket = (RenderTicket) other; - - if (!ticket.tile.equals(tile)) return false; - return ticket.map.getId().equals(map.getId()); - } - -} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java index 26e1383f..d17c36fd 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java @@ -27,7 +27,7 @@ import de.bluecolored.bluemap.api.BlueMapAPI; import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.BlueMapWorld; -import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.common.api.marker.MarkerAPIImpl; import de.bluecolored.bluemap.common.api.render.RenderAPIImpl; import de.bluecolored.bluemap.common.plugin.Plugin; @@ -68,7 +68,7 @@ public BlueMapAPIImpl(Plugin plugin) { } maps = new HashMap<>(); - for (MapType map : plugin.getMapTypes()) { + for (BmMap map : plugin.getMapTypes()) { BlueMapMapImpl m = new BlueMapMapImpl(this, map); maps.put(m.getId(), m); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapMapImpl.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapMapImpl.java index 574df23c..2006e57c 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapMapImpl.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapMapImpl.java @@ -27,14 +27,14 @@ import com.flowpowered.math.vector.Vector2i; import de.bluecolored.bluemap.api.BlueMapMap; -import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.core.map.BmMap; public class BlueMapMapImpl implements BlueMapMap { private BlueMapAPIImpl api; - private MapType delegate; + private BmMap delegate; - protected BlueMapMapImpl(BlueMapAPIImpl api, MapType delegate) { + protected BlueMapMapImpl(BlueMapAPIImpl api, BmMap delegate) { this.api = api; this.delegate = delegate; } @@ -56,15 +56,15 @@ public BlueMapWorldImpl getWorld() { @Override public Vector2i getTileSize() { - return delegate.getTileRenderer().getHiresModelManager().getTileSize(); + return delegate.getHiresModelManager().getTileGrid().getGridSize(); } @Override public Vector2i getTileOffset() { - return delegate.getTileRenderer().getHiresModelManager().getGridOrigin(); + return delegate.getHiresModelManager().getTileGrid().getOffset(); } - public MapType getMapType() { + public BmMap getMapType() { return delegate; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/render/RenderAPIImpl.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/render/RenderAPIImpl.java index f9dfdfd8..f6d6c2ae 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/render/RenderAPIImpl.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/render/RenderAPIImpl.java @@ -24,16 +24,15 @@ */ package de.bluecolored.bluemap.common.api.render; -import java.util.UUID; - import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.renderer.RenderAPI; -import de.bluecolored.bluemap.common.RenderManager; import de.bluecolored.bluemap.common.api.BlueMapAPIImpl; import de.bluecolored.bluemap.common.api.BlueMapMapImpl; +import de.bluecolored.bluemap.common.rendermanager.RenderManager; + +import java.util.UUID; public class RenderAPIImpl implements RenderAPI { @@ -68,18 +67,19 @@ public void render(BlueMapMap map, Vector2i tile) { } else { cmap = api.getMapForId(map.getId()); } - - renderManager.createTicket(cmap.getMapType(), tile); + + //TODO + //renderManager.createTicket(cmap.getMapType(), tile); } @Override public int renderQueueSize() { - return renderManager.getQueueSize(); + return renderManager.getScheduledRenderTasks().size(); } @Override public int renderThreadCount() { - return renderManager.getRenderThreadCount(); + return renderManager.getWorkerThreadCount(); } @Override @@ -89,7 +89,7 @@ public boolean isRunning() { @Override public void start() { - if (!isRunning()) renderManager.start(); + if (!isRunning()) renderManager.start(api.plugin.getCoreConfig().getRenderThreadCount()); } @Override 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 deleted file mode 100644 index 941774a0..00000000 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/MapUpdateHandler.java +++ /dev/null @@ -1,220 +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.plugin; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Timer; -import java.util.TimerTask; -import java.util.UUID; - -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; - -import de.bluecolored.bluemap.common.MapType; -import de.bluecolored.bluemap.common.RenderManager; -import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.core.world.World; - -public class MapUpdateHandler implements ServerEventListener { - - private Plugin plugin; - private Multimap updateBuffer; - private Timer flushTimer; - - public MapUpdateHandler(Plugin plugin) { - this.plugin = plugin; - updateBuffer = MultimapBuilder.hashKeys().hashSetValues().build(); - - flushTimer = new Timer("MapUpdateHandlerTimer", true); - } - - @Override - public void onWorldSaveToDisk(final UUID world) { - - // wait 5 sec before rendering so saving has finished - flushTimer.schedule(new TimerTask() { - @Override public void run() { flushUpdateBufferForWorld(world); } - }, 5000); - - } - - @Override - public void onChunkSaveToDisk(final UUID world, final Vector2i chunkPos) { - - // wait 5 sec before rendering so saving has finished - flushTimer.schedule(new TimerTask() { - @Override public void run() { flushUpdateBufferForChunk(world, chunkPos); } - }, 5000); - - } - - @Override - public void onBlockChange(UUID world, Vector3i blockPos) { - updateBlock(world, blockPos); - } - - @Override - public void onChunkFinishedGeneration(UUID world, Vector2i chunkPos) { - int x = chunkPos.getX(); - int z = chunkPos.getY(); - - // also update the chunks around, because they might be modified or not rendered yet due to finalizations - for (int dx = -1; dx <= 1; dx++) { - for (int dz = -1; dz <= 1; dz++) { - updateChunk(world, new Vector2i(x + dx, z + dz)); - } - } - } - - private void updateChunk(UUID worldUUID, Vector2i chunkPos) { - World world = plugin.getWorld(worldUUID); - if (world == null) return; - - synchronized (updateBuffer) { - updateBuffer.put(worldUUID, chunkPos); - } - } - - private void updateBlock(UUID worldUUID, Vector3i pos){ - World world = plugin.getWorld(worldUUID); - if (world == null) return; - - synchronized (updateBuffer) { - Vector2i chunkPos = world.blockPosToChunkPos(pos); - updateBuffer.put(worldUUID, chunkPos); - } - } - - public int getUpdateBufferCount() { - return updateBuffer.size(); - } - - public void flushUpdateBuffer() { - RenderManager renderManager = plugin.getRenderManager(); - - synchronized (updateBuffer) { - for (MapType map : plugin.getMapTypes()) { - Collection chunks = updateBuffer.get(map.getWorld().getUUID()); - Collection tiles = new HashSet<>(chunks.size() * 2); - - for (Vector2i chunk : chunks) { - Vector3i min = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16); - Vector3i max = min.add(15, 255, 15); - - Vector3i xmin = new Vector3i(min.getX(), 0, max.getY()); - Vector3i xmax = new Vector3i(max.getX(), 255, min.getY()); - - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(min)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(max)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(xmin)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(xmax)); - } - - //invalidate caches of updated chunks - for (Vector2i chunk : chunks) { - map.getWorld().invalidateChunkCache(chunk); - } - - //create render-tickets - renderManager.createTickets(map, tiles); - } - - updateBuffer.clear(); - } - } - - public void flushUpdateBufferForWorld(UUID world) { - RenderManager renderManager = plugin.getRenderManager(); - - synchronized (updateBuffer) { - for (MapType map : plugin.getMapTypes()) { - if (!map.getWorld().getUUID().equals(world)) continue; - - Collection chunks = updateBuffer.get(world); - Collection tiles = new HashSet<>(chunks.size() * 2); - - for (Vector2i chunk : chunks) { - Vector3i min = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16); - Vector3i max = min.add(15, 255, 15); - - Vector3i xmin = new Vector3i(min.getX(), 0, max.getY()); - Vector3i xmax = new Vector3i(max.getX(), 255, min.getY()); - - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(min)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(max)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(xmin)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(xmax)); - } - - //invalidate caches of updated chunks - for (Vector2i chunk : chunks) { - map.getWorld().invalidateChunkCache(chunk); - } - - //create render-tickets - renderManager.createTickets(map, tiles); - } - - updateBuffer.removeAll(world); - } - } - - public void flushUpdateBufferForChunk(UUID world, Vector2i chunkPos) { - RenderManager renderManager = plugin.getRenderManager(); - - synchronized (updateBuffer) { - if (!updateBuffer.containsEntry(world, chunkPos)) return; - - for (MapType map : plugin.getMapTypes()) { - if (!map.getWorld().getUUID().equals(world)) continue; - - Collection tiles = new HashSet<>(4); - - Vector3i min = new Vector3i(chunkPos.getX() * 16, 0, chunkPos.getY() * 16); - Vector3i max = min.add(15, 255, 15); - - Vector3i xmin = new Vector3i(min.getX(), 0, max.getY()); - Vector3i xmax = new Vector3i(max.getX(), 255, min.getY()); - - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(min)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(max)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(xmin)); - tiles.add(map.getTileRenderer().getHiresModelManager().posToTile(xmax)); - - //invalidate caches of updated chunk - map.getWorld().invalidateChunkCache(chunkPos); - - //create render-tickets - renderManager.createTickets(map, tiles); - } - - updateBuffer.remove(world, chunkPos); - } - } - -} 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 f7818ce4..2e1b20da 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 @@ -24,30 +24,33 @@ */ package de.bluecolored.bluemap.common.plugin; -import de.bluecolored.bluemap.common.*; +import de.bluecolored.bluemap.common.BlueMapService; +import de.bluecolored.bluemap.common.InterruptableReentrantLock; +import de.bluecolored.bluemap.common.MissingResourcesException; import de.bluecolored.bluemap.common.api.BlueMapAPIImpl; import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler; import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater; +import de.bluecolored.bluemap.common.rendermanager.RenderManager; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.config.CoreConfig; import de.bluecolored.bluemap.core.config.RenderConfig; import de.bluecolored.bluemap.core.config.WebServerConfig; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.metrics.Metrics; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.util.FileUtils; -import de.bluecolored.bluemap.core.web.FileRequestHandler; +import de.bluecolored.bluemap.common.web.FileRequestHandler; import de.bluecolored.bluemap.core.webserver.HttpRequestHandler; import de.bluecolored.bluemap.core.webserver.WebServer; import de.bluecolored.bluemap.core.world.World; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; public class Plugin { @@ -58,23 +61,26 @@ public class Plugin { private final MinecraftVersion minecraftVersion; private final String implementationType; - private ServerInterface serverInterface; + private final ServerInterface serverInterface; private BlueMapService blueMap; private BlueMapAPIImpl api; + private CoreConfig coreConfig; + private RenderConfig renderConfig; + private WebServerConfig webServerConfig; + private PluginConfig pluginConfig; + private Map worlds; - private Map maps; + private Map maps; private RenderManager renderManager; private WebServer webServer; private final Timer daemonTimer; - private TimerTask periodicalSaveTask; + private TimerTask saveTask; private TimerTask metricsTask; - private PluginConfig pluginConfig; - private MapUpdateHandler updateHandler; private PlayerSkinUpdater skinUpdater; private boolean loaded = false; @@ -98,9 +104,9 @@ public void load() throws IOException, ParseResourceException { blueMap = new BlueMapService(minecraftVersion, serverInterface); //load configs - CoreConfig coreConfig = blueMap.getCoreConfig(); - RenderConfig renderConfig = blueMap.getRenderConfig(); - WebServerConfig webServerConfig = blueMap.getWebServerConfig(); + coreConfig = blueMap.getCoreConfig(); + renderConfig = blueMap.getRenderConfig(); + webServerConfig = blueMap.getWebServerConfig(); //load plugin config pluginConfig = new PluginConfig(blueMap.getConfigManager().loadOrCreate( @@ -154,45 +160,8 @@ public void load() throws IOException, ParseResourceException { } //initialize render manager - renderManager = new RenderManager(coreConfig.getRenderThreadCount()); - renderManager.start(); - - //load render-manager state - try { - File saveFile = getRenderManagerSaveFile(); - if (saveFile.exists()) { - try (DataInputStream in = new DataInputStream(new GZIPInputStream(new FileInputStream(saveFile)))) { - renderManager.readState(in, getMapTypes()); - } - } - } catch (IOException ex) { - Logger.global.logError("Failed to load render-manager state!", ex); - } - - //do periodical saves - periodicalSaveTask = new TimerTask() { - @Override - public void run() { - try { - saveRenderManagerState(); - - //clean up caches - for (World world : blueMap.getWorlds().values()) { - world.cleanUpChunkCache(); - } - } catch (IOException ex) { - Logger.global.logError("Failed to save render-manager state!", ex); - } catch (InterruptedException ex) { - this.cancel(); - Thread.currentThread().interrupt(); - } - } - }; - daemonTimer.schedule(periodicalSaveTask, TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(5)); - - //start map updater - this.updateHandler = new MapUpdateHandler(this); - serverInterface.registerListener(updateHandler); + renderManager = new RenderManager(); + renderManager.start(coreConfig.getRenderThreadCount()); //update webapp and settings blueMap.createOrUpdateWebApp(false); @@ -206,6 +175,20 @@ public void run() { ); serverInterface.registerListener(skinUpdater); } + + //periodically save + saveTask = new TimerTask() { + @Override + public void run() { + synchronized (Plugin.this) { + if (maps == null) return; + for (BmMap map : maps.values()) { + map.save(); + } + } + } + }; + daemonTimer.schedule(saveTask, TimeUnit.MINUTES.toMillis(10)); //metrics metricsTask = new TimerTask() { @@ -222,6 +205,8 @@ public void run() { //enable api this.api = new BlueMapAPIImpl(this); this.api.register(); + + // } } catch (InterruptedException e) { @@ -246,26 +231,15 @@ public void unload() { //stop scheduled threads metricsTask.cancel(); - periodicalSaveTask.cancel(); //stop services if (renderManager != null) renderManager.stop(); if (webServer != null) webServer.close(); - //save render-manager state - if (updateHandler != null) updateHandler.flushUpdateBuffer(); //first write all buffered changes to the render manager to save them too - if (renderManager != null) { - try { - saveRenderManagerState(); - } catch (IOException ex) { - Logger.global.logError("Failed to save render-manager state!", ex); - } - } - //save renders if (maps != null) { - for (MapType map : maps.values()) { - map.getTileRenderer().save(); + for (BmMap map : maps.values()) { + map.save(); } } @@ -275,7 +249,8 @@ public void unload() { maps = null; renderManager = null; webServer = null; - updateHandler = null; + + pluginConfig = null; loaded = false; @@ -285,39 +260,32 @@ public void unload() { loadingLock.unlock(); } } - - public void saveRenderManagerState() throws IOException { - File saveFile = getRenderManagerSaveFile(); - - if (saveFile.exists()) FileUtils.delete(saveFile); - FileUtils.createFile(saveFile); - - try (DataOutputStream out = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(saveFile)))) { - renderManager.writeState(out); - } - } public void reload() throws IOException, ParseResourceException { unload(); load(); } + + public boolean flushWorldUpdates(UUID worldUUID) throws IOException { + return serverInterface.persistWorldChanges(worldUUID); + } public ServerInterface getServerInterface() { return serverInterface; } - public CoreConfig getCoreConfig() throws IOException { - return blueMap.getCoreConfig(); + public CoreConfig getCoreConfig() { + return coreConfig; } - public RenderConfig getRenderConfig() throws IOException { - return blueMap.getRenderConfig(); + public RenderConfig getRenderConfig() { + return renderConfig; } - public WebServerConfig getWebServerConfig() throws IOException { - return blueMap.getWebServerConfig(); + public WebServerConfig getWebServerConfig() { + return webServerConfig; } - + public PluginConfig getPluginConfig() { return pluginConfig; } @@ -330,7 +298,7 @@ public Collection getWorlds(){ return worlds.values(); } - public Collection getMapTypes(){ + public Collection getMapTypes(){ return maps.values(); } @@ -338,24 +306,6 @@ public RenderManager getRenderManager() { return renderManager; } - public File getRenderManagerSaveFile() throws IOException { - if (blueMap == null) return null; - return new File(blueMap.getCoreConfig().getDataFolder(), "rmstate"); - } - - public MapUpdateHandler getUpdateHandler() { - return updateHandler; - } - - public boolean flushWorldUpdates(UUID worldUUID) throws IOException { - if (serverInterface.persistWorldChanges(worldUUID)) { - updateHandler.onWorldSaveToDisk(worldUUID); - return true; - } - - return false; - } - public WebServer getWebServer() { return webServer; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java index 3b33a5a7..eefe9e9e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java @@ -24,29 +24,16 @@ */ package de.bluecolored.bluemap.common.plugin.commands; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.StringJoiner; -import java.util.function.Predicate; - -import com.flowpowered.math.vector.Vector2l; -import org.apache.commons.lang3.time.DurationFormatUtils; - -import com.flowpowered.math.GenericMath; -import com.flowpowered.math.vector.Vector2i; - -import de.bluecolored.bluemap.common.MapType; -import de.bluecolored.bluemap.common.RenderManager; -import de.bluecolored.bluemap.common.RenderTask; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.plugin.text.TextColor; -import de.bluecolored.bluemap.common.plugin.text.TextFormat; -import de.bluecolored.bluemap.core.render.hires.HiresModelManager; +import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.world.World; +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; + public class CommandHelper { private Plugin plugin; @@ -57,119 +44,12 @@ public CommandHelper(Plugin plugin) { public List createStatusMessage(){ List lines = new ArrayList<>(); - - RenderManager renderer = plugin.getRenderManager(); - - lines.add(Text.of()); - lines.add(Text.of(TextColor.BLUE, "Tile-Updates:")); - if (renderer.isRunning()) { - lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", - Text.of(TextColor.GREEN, "running") - .setHoverText(Text.of("click to pause rendering")) - .setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap pause"), - TextColor.GRAY, "!")); - } else { - lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", - Text.of(TextColor.RED, "paused") - .setHoverText(Text.of("click to resume rendering")) - .setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap resume"), - TextColor.GRAY, "!")); - } - - lines.add(Text.of( - TextColor.WHITE, " Scheduled tile-updates: ", - TextColor.GOLD, renderer.getQueueSize()).setHoverText( - Text.of( - TextColor.WHITE, "Tiles waiting for a free render-thread: ", TextColor.GOLD, renderer.getQueueSize(), - TextColor.WHITE, "\n\nChunks marked as changed: ", TextColor.GOLD, plugin.getUpdateHandler().getUpdateBufferCount(), - TextColor.GRAY, TextFormat.ITALIC, "\n(Changed chunks will be rendered as soon as they are saved back to the world-files)" - ) - ) - ); - - RenderTask[] tasks = renderer.getRenderTasks(); - if (tasks.length > 0) { - RenderTask task = tasks[0]; - - long time = task.getActiveTime(); - String durationString = DurationFormatUtils.formatDurationWords(time, true, true); - double pct = (double)task.getRenderedTileCount() / (double)(task.getRenderedTileCount() + task.getRemainingTileCount()); - - long ert = (long)((time / pct) * (1d - pct)); - String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); - - double tps = task.getRenderedTileCount() / (time / 1000.0); - - lines.add(Text.of(TextColor.BLUE, "Current task:")); - lines.add(Text.of(" ", createCancelTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())))); - lines.add(Text.of(TextColor.WHITE, " rendered ", TextColor.GOLD, task.getRenderedTileCount(), TextColor.WHITE, " tiles ", TextColor.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "% | " + GenericMath.round(tps, 1) + "t/s)", TextColor.WHITE, " in ", TextColor.GOLD, durationString)); - lines.add(Text.of(TextColor.WHITE, " with ", TextColor.GOLD, task.getRemainingTileCount(), TextColor.WHITE, " tiles to go. ETA: ", TextColor.GOLD, ertDurationString)); - } - - if (tasks.length > 1) { - lines.add(Text.of(TextColor.BLUE, "Waiting tasks:")); - for (int i = 1; i < tasks.length; i++) { - RenderTask task = tasks[i]; - lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColor.WHITE, " Task ", TextColor.GOLD, task.getName(), TextColor.WHITE, " for map ", Text.of(TextColor.GOLD, task.getMapType().getName()).setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GOLD, task.getMapType().getWorld().getName())), TextColor.GRAY, " (" + task.getRemainingTileCount() + " tiles)")); - } - } + //TODO return lines; } - private Text createCancelTaskText(RenderTask task) { - return Text.of(TextColor.RED, "[X]").setHoverText(Text.of(TextColor.GRAY, "click to cancel this render-task")).setClickAction(Text.ClickAction.RUN_COMMAND,"/bluemap render cancel " + task.getUuid()); - } - - private Text createPrioritizeTaskText(RenderTask task) { - return Text.of(TextColor.GREEN, "[^]").setHoverText(Text.of(TextColor.GRAY, "click to prioritize this render-task")).setClickAction(Text.ClickAction.RUN_COMMAND,"/bluemap render prioritize " + task.getUuid()); - } - - public void createWorldRenderTask(CommandSource source, World world, Vector2i center, long blockRadius) { - - for (MapType map : plugin.getMapTypes()) { - if (!map.getWorld().getUUID().equals(world.getUUID())) continue; - - createMapRenderTask(source, map, center, blockRadius); - } - - source.sendMessage(Text.of(TextColor.GREEN, "All render tasks created! Use /bluemap to view the progress!")); - } - - public void createMapRenderTask(CommandSource source, MapType map, Vector2i center, long blockRadius) { - source.sendMessage(Text.of(TextColor.GOLD, "Creating render-task for map: " + map.getId())); - source.sendMessage(Text.of(TextColor.GOLD, "Collecting chunks...")); - - String taskName = "world-render"; - Vector2i renderCenter = map.getWorld().getSpawnPoint().toVector2(true); - - Predicate filter; - if (center == null || blockRadius < 0) { - filter = c -> true; - } else { - Vector2l centerL = center.toLong(); //use longs to avoid int-overflow - filter = c -> c.toLong().mul(16).distanceSquared(centerL) <= blockRadius * blockRadius; - taskName = "radius-render"; - renderCenter = center; - } - - Collection chunks = map.getWorld().getChunkList(filter); - - source.sendMessage(Text.of(TextColor.GREEN, chunks.size() + " chunks found!")); - source.sendMessage(Text.of(TextColor.GOLD, "Collecting tiles...")); - - HiresModelManager hmm = map.getTileRenderer().getHiresModelManager(); - Collection tiles = hmm.getTilesForChunks(chunks); - - RenderTask task = new RenderTask(taskName, map); - task.addTiles(tiles); - task.optimizeQueue(renderCenter); - plugin.getRenderManager().addRenderTask(task); - - source.sendMessage(Text.of(TextColor.GREEN, tiles.size() + " tiles found! Task created.")); - } - public Text worldHelperHover() { StringJoiner joiner = new StringJoiner("\n"); for (World world : plugin.getWorlds()) { @@ -181,27 +61,10 @@ public Text worldHelperHover() { public Text mapHelperHover() { StringJoiner joiner = new StringJoiner("\n"); - for (MapType map : plugin.getMapTypes()) { + for (BmMap map : plugin.getMapTypes()) { joiner.add(map.getId()); } return Text.of("map").setHoverText(Text.of(TextColor.WHITE, "Available maps: \n", TextColor.GRAY, joiner.toString())); } - - public boolean checkLoaded(CommandSource source) { - if (!plugin.isLoaded()) { - source.sendMessage(Text.of(TextColor.RED, "BlueMap is not loaded!", TextColor.GRAY, "(Try /bluemap reload)")); - return false; - } - - return true; - } - - public boolean checkPermission(CommandSource source, String permission) { - if (source.hasPermission(permission)) return true; - - source.sendMessage(Text.of(TextColor.RED, "You don't have the permissions to use this command!")); - return false; - } - } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java index 0f9782c0..adfe6c8c 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java @@ -24,18 +24,6 @@ */ package de.bluecolored.bluemap.common.plugin.commands; -import java.io.File; -import java.io.IOException; -import java.util.Optional; -import java.util.UUID; -import java.util.function.Function; -import java.util.function.Predicate; - -import de.bluecolored.bluemap.common.plugin.text.TextFormat; -import de.bluecolored.bluemap.core.BlueMap; -import de.bluecolored.bluemap.core.MinecraftVersion; -import org.apache.commons.io.FileUtils; - import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3d; import com.flowpowered.math.vector.Vector3i; @@ -49,25 +37,34 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.tree.LiteralCommandNode; - import de.bluecolored.bluemap.api.BlueMapAPI; import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.marker.MarkerAPI; import de.bluecolored.bluemap.api.marker.MarkerSet; import de.bluecolored.bluemap.api.marker.POIMarker; -import de.bluecolored.bluemap.common.MapType; -import de.bluecolored.bluemap.common.RenderTask; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.plugin.text.TextColor; +import de.bluecolored.bluemap.common.plugin.text.TextFormat; +import de.bluecolored.bluemap.core.BlueMap; +import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.mca.Chunk; +import de.bluecolored.bluemap.core.map.BmMap; +import de.bluecolored.bluemap.core.mca.MCAChunk; import de.bluecolored.bluemap.core.mca.ChunkAnvil112; import de.bluecolored.bluemap.core.mca.MCAWorld; import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.World; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.Predicate; public class Commands { @@ -172,23 +169,6 @@ public void init() { .then(argument("radius", IntegerArgumentType.integer()) .executes(this::renderCommand))))) // /bluemap render .build(); - - LiteralCommandNode prioRenderCommand = - literal("prioritize") - .requires(requirements("bluemap.render")) - .then(argument("uuid", StringArgumentType.string()) - .executes(this::prioritizeRenderTaskCommand)) - .build(); - - LiteralCommandNode cancelRenderCommand = - literal("cancel") - .requires(requirements("bluemap.render")) - .executes(this::cancelLastRenderTaskCommand) - - .then(argument("uuid", StringArgumentType.string()) - .executes(this::cancelRenderTaskCommand)) - - .build(); LiteralCommandNode purgeCommand = literal("purge") @@ -247,8 +227,6 @@ public void init() { baseCommand.addChild(resumeCommand); baseCommand.addChild(renderCommand); baseCommand.addChild(purgeCommand); - renderCommand.addChild(prioRenderCommand); - renderCommand.addChild(cancelRenderCommand); baseCommand.addChild(worldsCommand); baseCommand.addChild(mapsCommand); baseCommand.addChild(markerCommand); @@ -296,8 +274,8 @@ private Optional parseWorld(String worldName) { return Optional.empty(); } - private Optional parseMap(String mapId) { - for (MapType map : plugin.getMapTypes()) { + private Optional parseMap(String mapId) { + for (BmMap map : plugin.getMapTypes()) { if (map.getId().equalsIgnoreCase(mapId)) { return Optional.of(map); } @@ -334,7 +312,7 @@ public int versionCommand(CommandContext context) { int renderThreadCount = 0; if (plugin.isLoaded()) { - renderThreadCount = plugin.getRenderManager().getRenderThreadCount(); + renderThreadCount = plugin.getRenderManager().getWorkerThreadCount(); } source.sendMessage(Text.of(TextFormat.BOLD, TextColor.BLUE, "Version: ", TextColor.WHITE, BlueMap.VERSION)); @@ -504,7 +482,7 @@ public int debugBlockCommand(CommandContext context) { String blockBelowIdMeta = ""; if (world instanceof MCAWorld) { - Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(blockPos)); + MCAChunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(blockPos)); if (chunk instanceof ChunkAnvil112) { blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos) + ")"; blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos.add(0, -1, 0)) + ")"; @@ -512,7 +490,6 @@ public int debugBlockCommand(CommandContext context) { } source.sendMessages(Lists.newArrayList( - Text.of(TextColor.GOLD, "Is generated: ", TextColor.WHITE, world.isChunkGenerated(world.blockPosToChunkPos(blockPos))), Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta), Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta) )); @@ -538,7 +515,7 @@ public int resumeCommand(CommandContext context) { CommandSource source = commandSourceInterface.apply(context.getSource()); if (!plugin.getRenderManager().isRunning()) { - plugin.getRenderManager().start(); + plugin.getRenderManager().start(plugin.getCoreConfig().getRenderThreadCount()); source.sendMessage(Text.of(TextColor.GREEN, "BlueMap renders resumed!")); return 1; } else { @@ -554,7 +531,7 @@ public int renderCommand(CommandContext context) { Optional worldOrMap = getOptionalArgument(context, "world|map", String.class); final World world; - final MapType map; + final BmMap map; if (worldOrMap.isPresent()) { world = parseWorld(worldOrMap.get()).orElse(null); @@ -605,10 +582,10 @@ public int renderCommand(CommandContext context) { try { if (world != null) { plugin.getServerInterface().persistWorldChanges(world.getUUID()); - helper.createWorldRenderTask(source, world, center, radius); + //TODO: helper.createWorldRenderTask(source, world, center, radius); } else { plugin.getServerInterface().persistWorldChanges(map.getWorld().getUUID()); - helper.createMapRenderTask(source, map, center, radius); + //TODO: helper.createMapRenderTask(source, map, center, radius); } } catch (IOException ex) { source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to save the world. Please check the console for more details...")); @@ -644,68 +621,6 @@ public int purgeCommand(CommandContext context) { return 1; } - public int prioritizeRenderTaskCommand(CommandContext context) { - CommandSource source = commandSourceInterface.apply(context.getSource()); - - String uuidString = context.getArgument("uuid", String.class); - Optional taskUUID = parseUUID(uuidString); - if (!taskUUID.isPresent()) { - source.sendMessage(Text.of(TextColor.RED, "Not a valid UUID: " + uuidString)); - return 0; - } - - for (RenderTask task : plugin.getRenderManager().getRenderTasks()) { - if (task.getUuid().equals(taskUUID.get())) { - plugin.getRenderManager().prioritizeRenderTask(task); - - source.sendMessages(helper.createStatusMessage()); - return 1; - } - } - - source.sendMessage(Text.of(TextColor.RED, "There is no render-task with this UUID: " + uuidString)); - return 0; - } - - public int cancelLastRenderTaskCommand(CommandContext context) { - CommandSource source = commandSourceInterface.apply(context.getSource()); - - RenderTask[] tasks = plugin.getRenderManager().getRenderTasks(); - if (tasks.length == 0) { - source.sendMessage(Text.of(TextColor.RED, "There is currently no render task scheduled!")); - return 0; - } - - RenderTask task = tasks[tasks.length - 1]; - - plugin.getRenderManager().removeRenderTask(task); - source.sendMessage(Text.of(TextColor.GREEN, "The render-task '" + task.getName() + "' has been canceled!")); - return 1; - } - - public int cancelRenderTaskCommand(CommandContext context) { - CommandSource source = commandSourceInterface.apply(context.getSource()); - - String uuidString = context.getArgument("uuid", String.class); - Optional taskUUID = parseUUID(uuidString); - if (!taskUUID.isPresent()) { - source.sendMessage(Text.of(TextColor.RED, "Not a valid UUID: " + uuidString)); - return 0; - } - - for (RenderTask task : plugin.getRenderManager().getRenderTasks()) { - if (task.getUuid().equals(taskUUID.get())) { - plugin.getRenderManager().removeRenderTask(task); - - source.sendMessages(helper.createStatusMessage()); - return 1; - } - } - - source.sendMessage(Text.of(TextColor.RED, "There is no render-task with this UUID: " + uuidString)); - return 0; - } - public int worldsCommand(CommandContext context) { CommandSource source = commandSourceInterface.apply(context.getSource()); @@ -721,7 +636,7 @@ public int mapsCommand(CommandContext context) { CommandSource source = commandSourceInterface.apply(context.getSource()); source.sendMessage(Text.of(TextColor.BLUE, "Maps loaded by BlueMap:")); - for (MapType map : plugin.getMapTypes()) { + for (BmMap map : plugin.getMapTypes()) { source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, map.getId(), TextColor.GRAY, " (" + map.getName() + ")").setHoverText(Text.of(TextColor.WHITE, "World: ", TextColor.GRAY, map.getWorld().getName()))); } @@ -738,7 +653,7 @@ public int createMarkerCommand(CommandContext context) { // parse world/map argument String mapString = context.getArgument("map", String.class); - MapType map = parseMap(mapString).orElse(null); + BmMap map = parseMap(mapString).orElse(null); if (map == null) { source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString)); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java index b683a7ab..f72f5d3c 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java @@ -27,7 +27,7 @@ import java.util.Collection; import java.util.HashSet; -import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.common.plugin.Plugin; public class MapSuggestionProvider extends AbstractSuggestionProvider { @@ -42,7 +42,7 @@ public MapSuggestionProvider(Plugin plugin) { public Collection getPossibleValues() { Collection values = new HashSet<>(); - for (MapType map : plugin.getMapTypes()) { + for (BmMap map : plugin.getMapTypes()) { values.add(map.getId()); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java index 5bc100f1..4e32798d 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java @@ -27,7 +27,7 @@ import java.util.Collection; import java.util.HashSet; -import de.bluecolored.bluemap.common.MapType; +import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.core.world.World; @@ -47,7 +47,7 @@ public Collection getPossibleValues() { values.add(world.getName()); } - for (MapType map : plugin.getMapTypes()) { + for (BmMap map : plugin.getMapTypes()) { values.add(map.getId()); } 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 44dee9ba..2c62f89f 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 @@ -24,23 +24,15 @@ */ package de.bluecolored.bluemap.common.plugin.serverinterface; -import java.util.UUID; - -import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.common.plugin.text.Text; +import java.util.UUID; + public interface ServerEventListener { - default void onWorldSaveToDisk(UUID world) {}; - - default void onChunkSaveToDisk(UUID world, Vector2i chunkPos) {}; - default void onBlockChange(UUID world, Vector3i blockPos) {}; - default void onChunkFinishedGeneration(UUID world, Vector2i chunkPos) {}; - default void onPlayerJoin(UUID playerUuid) {}; default void onPlayerLeave(UUID playerUuid) {}; 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 6e18abd0..5c1740a7 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 @@ -95,7 +95,7 @@ default boolean isMetricsEnabled(boolean configValue) { /** * 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. + * this method is only guaranteed to return a {@link Player} if the player is currently online. */ Optional getPlayer(UUID uuid); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/CombinedRenderTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/CombinedRenderTask.java new file mode 100644 index 00000000..2f603c3c --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/CombinedRenderTask.java @@ -0,0 +1,57 @@ +package de.bluecolored.bluemap.common.rendermanager; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class CombinedRenderTask implements RenderTask { + + private final List tasks; + private int currentTaskIndex; + + public CombinedRenderTask(Collection tasks) { + this.tasks = new ArrayList<>(); + this.tasks.addAll(tasks); + this.currentTaskIndex = 0; + } + + @Override + public void doWork() throws Exception { + T task; + + synchronized (this.tasks) { + if (!hasMoreWork()) return; + task = this.tasks.get(this.currentTaskIndex); + + if (!task.hasMoreWork()){ + this.currentTaskIndex++; + return; + } + } + + task.doWork(); + } + + @Override + public boolean hasMoreWork() { + return this.currentTaskIndex < this.tasks.size(); + } + + @Override + public double estimateProgress() { + synchronized (this.tasks) { + if (!hasMoreWork()) return 1; + + double total = currentTaskIndex; + total += this.tasks.get(this.currentTaskIndex).estimateProgress(); + + return total / tasks.size(); + } + } + + @Override + public void cancel() { + for (T task : tasks) task.cancel(); + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/RenderManager.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/RenderManager.java new file mode 100644 index 00000000..23b3c774 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/RenderManager.java @@ -0,0 +1,253 @@ +/* + * 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.rendermanager; + +import de.bluecolored.bluemap.core.logger.Logger; + +import java.util.*; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicInteger; + +public class RenderManager { + private static final AtomicInteger nextRenderManagerIndex = new AtomicInteger(0); + + private final int id; + private volatile boolean running; + + private final AtomicInteger nextWorkerThreadIndex; + private final Collection workerThreads; + private final AtomicInteger busyCount; + + private final LinkedList renderTasks; + private final Set renderTaskSet; + + public RenderManager() { + this.id = nextRenderManagerIndex.getAndIncrement(); + this.nextWorkerThreadIndex = new AtomicInteger(0); + + this.running = false; + this.workerThreads = new ConcurrentLinkedDeque<>(); + this.busyCount = new AtomicInteger(0); + + this.renderTasks = new LinkedList<>(); + this.renderTaskSet = new HashSet<>(); + } + + public void start(int threadCount) throws IllegalStateException { + if (threadCount <= 0) throw new IllegalArgumentException("threadCount has to be 1 or more!"); + + synchronized (this.workerThreads) { + if (isRunning()) throw new IllegalStateException("RenderManager is already running!"); + this.workerThreads.clear(); + this.busyCount.set(0); + + this.running = true; + + for (int i = 0; i < threadCount; i++) { + WorkerThread worker = new WorkerThread(); + this.workerThreads.add(worker); + worker.start(); + } + } + } + + public void stop() { + synchronized (this.workerThreads) { + this.running = false; + for (WorkerThread worker : workerThreads) worker.interrupt(); + } + } + + public boolean isIdle() { + return busyCount.get() == 0; + } + + public boolean isRunning() { + synchronized (this.workerThreads) { + for (WorkerThread worker : workerThreads) { + if (worker.isAlive()) return true; + } + + return false; + } + } + + public void awaitShutdown() throws InterruptedException { + synchronized (this.workerThreads) { + while (isRunning()) + this.workerThreads.wait(10000); + } + } + + public boolean scheduleRenderTask(RenderTask task) { + synchronized (this.renderTasks) { + if (renderTaskSet.add(task)) { + renderTasks.addLast(task); + renderTasks.notifyAll(); + return true; + } else { + return false; + } + } + } + + public boolean scheduleRenderTaskNext(RenderTask task) { + synchronized (this.renderTasks) { + if (renderTasks.size() <= 1) return scheduleRenderTask(task); + + if (renderTaskSet.add(task)) { + renderTasks.add(1, task); + renderTasks.notifyAll(); + return true; + } else { + return false; + } + } + } + + public void reorderRenderTasks(Comparator taskComparator) { + synchronized (this.renderTasks) { + if (renderTasks.size() <= 2) return; + + RenderTask currentTask = renderTasks.removeFirst(); + renderTasks.sort(taskComparator); + renderTasks.addFirst(currentTask); + } + } + + public boolean removeTask(RenderTask task) { + synchronized (this.renderTasks) { + if (this.renderTasks.isEmpty()) return false; + + // cancel the task if it is currently processed + RenderTask first = renderTasks.getFirst(); + if (first.equals(task)) { + first.cancel(); + return true; + } + + // else remove it + return renderTaskSet.remove(task) && renderTasks.remove(task); + } + } + + public void removeAllTasks() { + synchronized (this.renderTasks) { + if (this.renderTasks.isEmpty()) return; + + RenderTask first = renderTasks.removeFirst(); + first.cancel(); + renderTasks.clear(); + renderTasks.addFirst(first); + } + } + + public List getScheduledRenderTasks() { + return Collections.unmodifiableList(renderTasks); + } + + public int getWorkerThreadCount() { + return workerThreads.size(); + } + + private void doWork() throws Exception { + RenderTask task; + + synchronized (this.renderTasks) { + while (this.renderTasks.isEmpty()) + this.renderTasks.wait(10000); + + task = this.renderTasks.getFirst(); + + // the following is making sure every render-thread is done working on this task (no thread is "busy") + // before continuing working on the next RenderTask + if (!task.hasMoreWork()) { + if (busyCount.get() <= 0) { + this.renderTaskSet.remove(this.renderTasks.removeFirst()); + busyCount.set(0); + } else { + this.renderTasks.wait(10000); + } + + return; + } + + this.busyCount.incrementAndGet(); + } + + try { + task.doWork(); + } finally { + synchronized (renderTasks) { + this.busyCount.decrementAndGet(); + this.renderTasks.notifyAll(); + } + } + } + + public class WorkerThread extends Thread { + + private final int id; + + private WorkerThread() { + this.id = RenderManager.this.nextWorkerThreadIndex.getAndIncrement(); + this.setName("RenderManager-" + RenderManager.this.id + "-" + this.id); + } + + @Override + @SuppressWarnings("BusyWait") + public void run() { + try { + while (RenderManager.this.running) { + try { + RenderManager.this.doWork(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + Logger.global.logError( + "RenderManager(" + RenderManager.this.id + "): WorkerThread(" + this.id + + "): Exception while doing some work!", e); + + try { + // on error, wait a few seconds before resurrecting this render-thread + // if something goes wrong, this prevents running into the same error on all render-threads + // with full-speed over and over again :D + Thread.sleep(10000); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + } + } finally { + synchronized (RenderManager.this.workerThreads) { + RenderManager.this.workerThreads.remove(this); + RenderManager.this.workerThreads.notifyAll(); + } + } + } + + } + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/RenderTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/RenderTask.java new file mode 100644 index 00000000..9218797f --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/RenderTask.java @@ -0,0 +1,25 @@ +package de.bluecolored.bluemap.common.rendermanager; + +public interface RenderTask { + + void doWork() throws Exception; + + /** + * Whether this task is requesting more calls to its {@link #doWork()} method.
+ * This can be false because the task is finished, OR because the task got cancelled and decides to interrupt. + */ + boolean hasMoreWork(); + + /** + * The estimated progress made so far, from 0 to 1. + */ + default double estimateProgress() { + return 0d; + } + + /** + * Requests to cancel this task. The task then self-decides what to do with this request. + */ + void cancel(); + +} diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/WorldRegionRenderTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/WorldRegionRenderTask.java new file mode 100644 index 00000000..b6fdfe66 --- /dev/null +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/WorldRegionRenderTask.java @@ -0,0 +1,136 @@ +package de.bluecolored.bluemap.common.rendermanager; + +import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.core.map.BmMap; +import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.world.Region; + +import java.util.Collection; +import java.util.TreeSet; + +public class WorldRegionRenderTask implements RenderTask { + + private final BmMap map; + private final Vector2i worldRegion; + private final boolean force; + + private TreeSet tiles; + private int tileCount; + private long startTime; + + private volatile int atWork; + private volatile boolean cancelled; + + public WorldRegionRenderTask(BmMap map, Vector2i worldRegion) { + this(map, worldRegion, false); + } + + public WorldRegionRenderTask(BmMap map, Vector2i worldRegion, boolean force) { + this.map = map; + this.worldRegion = worldRegion; + this.force = force; + + this.tiles = null; + this.tileCount = -1; + this.startTime = -1; + + this.atWork = 0; + this.cancelled = false; + } + + private synchronized void init() { + tiles = new TreeSet<>(); + startTime = System.currentTimeMillis(); + + long changesSince = 0; + if (!force) changesSince = map.getRenderState().getRenderTime(worldRegion); + + Region region = map.getWorld().getRegion(worldRegion.getX(), worldRegion.getY()); + Collection chunks = region.listChunks(changesSince); + + Grid tileGrid = map.getHiresModelManager().getTileGrid(); + Grid chunkGrid = map.getWorld().getChunkGrid(); + + for (Vector2i chunk : chunks) { + Vector2i tileMin = chunkGrid.getCellMin(chunk, tileGrid); + Vector2i tileMax = chunkGrid.getCellMax(chunk, tileGrid); + + for (int x = tileMin.getX(); x < tileMax.getX(); x++) { + for (int z = tileMin.getY(); z < tileMax.getY(); z++) { + tiles.add(new Vector2i(x, z)); + } + } + } + + this.tileCount = tiles.size(); + + if (tiles.isEmpty()) complete(); + } + + @Override + public void doWork() { + if (cancelled) return; + + Vector2i tile; + + synchronized (this) { + if (tiles == null) init(); + if (tiles.isEmpty()) return; + + tile = tiles.pollFirst(); + + this.atWork++; + } + + map.renderTile(tile); // <- actual work + + synchronized (this) { + this.atWork--; + + if (atWork <= 0 && tiles.isEmpty() && !cancelled) { + complete(); + } + } + } + + private void complete() { + map.getRenderState().setRenderTime(worldRegion, startTime); + } + + @Override + public boolean hasMoreWork() { + return !cancelled && !tiles.isEmpty(); + } + + @Override + public double estimateProgress() { + if (tiles == null) return 0; + if (tileCount == 0) return 1; + + double remainingTiles = tiles.size(); + return remainingTiles / this.tileCount; + } + + @Override + public void cancel() { + this.cancelled = true; + + synchronized (this) { + if (tiles != null) this.tiles.clear(); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WorldRegionRenderTask that = (WorldRegionRenderTask) o; + return force == that.force && map.equals(that.map) && worldRegion.equals(that.worldRegion); + } + + @Override + public int hashCode() { + return worldRegion.hashCode(); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java similarity index 99% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java index f26f447c..a53b5c69 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/FileRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java @@ -22,7 +22,7 @@ * 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.web; import de.bluecolored.bluemap.core.webserver.HttpRequest; import de.bluecolored.bluemap.core.webserver.HttpRequestHandler; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/WebSettings.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/WebSettings.java similarity index 66% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/WebSettings.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/WebSettings.java index 3d388491..1086f71a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/web/WebSettings.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/WebSettings.java @@ -22,15 +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.web; import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3f; import de.bluecolored.bluemap.core.config.MapConfig; -import de.bluecolored.bluemap.core.render.TileRenderer; +import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.util.FileUtils; import de.bluecolored.bluemap.core.util.MathUtils; -import de.bluecolored.bluemap.core.world.World; import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.gson.GsonConfigurationLoader; import ninja.leaping.configurate.loader.ConfigurationLoader; @@ -92,7 +91,7 @@ public double getDouble(Object... path) { } public Collection getMapIds() { - return rootNode.getNode("maps").getChildrenMap().keySet().stream().map(o -> o.toString()).collect(Collectors.toSet()); + return rootNode.getNode("maps").getChildrenMap().keySet().stream().map(Object::toString).collect(Collectors.toSet()); } public void setAllMapsEnabled(boolean enabled) { @@ -105,51 +104,49 @@ public void setMapEnabled(boolean enabled, String mapId) { set(enabled, "maps", mapId, "enabled"); } - public void setFrom(TileRenderer tileRenderer, String mapId) { - Vector2i hiresTileSize = tileRenderer.getHiresModelManager().getTileSize(); - Vector2i gridOrigin = tileRenderer.getHiresModelManager().getGridOrigin(); - Vector2i lowresTileSize = tileRenderer.getLowresModelManager().getTileSize(); - Vector2i lowresPointsPerHiresTile = tileRenderer.getLowresModelManager().getPointsPerHiresTile(); + public void setFrom(BmMap map) { + Vector2i hiresTileSize = map.getHiresModelManager().getTileGrid().getGridSize(); + Vector2i gridOrigin = map.getHiresModelManager().getTileGrid().getOffset(); + Vector2i lowresTileSize = map.getLowresModelManager().getTileSize(); + Vector2i lowresPointsPerHiresTile = map.getLowresModelManager().getPointsPerHiresTile(); - set(hiresTileSize.getX(), "maps", mapId, "hires", "tileSize", "x"); - set(hiresTileSize.getY(), "maps", mapId, "hires", "tileSize", "z"); - set(1, "maps", mapId, "hires", "scale", "x"); - set(1, "maps", mapId, "hires", "scale", "z"); - set(gridOrigin.getX(), "maps", mapId, "hires", "translate", "x"); - set(gridOrigin.getY(), "maps", mapId, "hires", "translate", "z"); + set(hiresTileSize.getX(), "maps", map.getId(), "hires", "tileSize", "x"); + set(hiresTileSize.getY(), "maps", map.getId(), "hires", "tileSize", "z"); + set(1, "maps", map.getId(), "hires", "scale", "x"); + set(1, "maps", map.getId(), "hires", "scale", "z"); + set(gridOrigin.getX(), "maps", map.getId(), "hires", "translate", "x"); + set(gridOrigin.getY(), "maps", map.getId(), "hires", "translate", "z"); Vector2i pointSize = hiresTileSize.div(lowresPointsPerHiresTile); Vector2i tileSize = pointSize.mul(lowresTileSize); - set(tileSize.getX(), "maps", mapId, "lowres", "tileSize", "x"); - set(tileSize.getY(), "maps", mapId, "lowres", "tileSize", "z"); - set(pointSize.getX(), "maps", mapId, "lowres", "scale", "x"); - set(pointSize.getY(), "maps", mapId, "lowres", "scale", "z"); - set(pointSize.getX() / 2, "maps", mapId, "lowres", "translate", "x"); - set(pointSize.getY() / 2, "maps", mapId, "lowres", "translate", "z"); - } + set(tileSize.getX(), "maps", map.getId(), "lowres", "tileSize", "x"); + set(tileSize.getY(), "maps", map.getId(), "lowres", "tileSize", "z"); + set(pointSize.getX(), "maps", map.getId(), "lowres", "scale", "x"); + set(pointSize.getY(), "maps", map.getId(), "lowres", "scale", "z"); + set(pointSize.getX() / 2, "maps", map.getId(), "lowres", "translate", "x"); + set(pointSize.getY() / 2, "maps", map.getId(), "lowres", "translate", "z"); - 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"); + set(map.getWorld().getSpawnPoint().getX(), "maps", map.getId(), "startPos", "x"); + set(map.getWorld().getSpawnPoint().getZ(), "maps", map.getId(), "startPos", "z"); + set(map.getWorld().getUUID().toString(), "maps", map.getId(), "world"); } - public void setFrom(MapConfig mapConfig, String mapId) { + public void setFrom(MapConfig mapConfig) { Vector2i startPos = mapConfig.getStartPos(); if (startPos != null) { - set(startPos.getX(), "maps", mapId, "startPos", "x"); - set(startPos.getY(), "maps", mapId, "startPos", "z"); + set(startPos.getX(), "maps", mapConfig.getId(), "startPos", "x"); + set(startPos.getY(), "maps", mapConfig.getId(), "startPos", "z"); } Vector3f skyColor = MathUtils.color3FromInt(mapConfig.getSkyColor()); - set(skyColor.getX(), "maps", mapId, "skyColor", "r"); - set(skyColor.getY(), "maps", mapId, "skyColor", "g"); - set(skyColor.getZ(), "maps", mapId, "skyColor", "b"); + set(skyColor.getX(), "maps", mapConfig.getId(), "skyColor", "r"); + set(skyColor.getY(), "maps", mapConfig.getId(), "skyColor", "g"); + set(skyColor.getZ(), "maps", mapConfig.getId(), "skyColor", "b"); - set(mapConfig.getAmbientLight(), "maps", mapId, "ambientLight"); + set(mapConfig.getAmbientLight(), "maps", mapConfig.getId(), "ambientLight"); - setName(mapConfig.getName(), mapId); + setName(mapConfig.getName(), mapConfig.getId()); } public void setOrdinal(int ordinal, String mapId) { diff --git a/BlueMapCore/build.gradle b/BlueMapCore/build.gradle index 18f50393..c616520e 100644 --- a/BlueMapCore/build.gradle +++ b/BlueMapCore/build.gradle @@ -8,7 +8,11 @@ dependencies { compile 'org.spongepowered:configurate-gson:3.7.1' compile 'com.github.Querz:NBT:4.0' - testCompile 'junit:junit:4.12' + testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2' +} + +test { + useJUnitPlatform() } processResources { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MapConfig.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MapConfig.java index f1f20be1..0ba03d5d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MapConfig.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/config/MapConfig.java @@ -24,17 +24,17 @@ */ package de.bluecolored.bluemap.core.config; -import java.io.IOException; -import java.util.regex.Pattern; - import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - -import de.bluecolored.bluemap.core.render.RenderSettings; +import de.bluecolored.bluemap.core.map.MapSettings; +import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.util.ConfigUtils; import ninja.leaping.configurate.ConfigurationNode; -public class MapConfig implements RenderSettings { +import java.io.IOException; +import java.util.regex.Pattern; + +public class MapConfig implements MapSettings { private static final Pattern VALID_ID_PATTERN = Pattern.compile("[a-zA-Z0-9_]+"); private String id; @@ -86,12 +86,12 @@ public MapConfig(ConfigurationNode node) throws IOException { this.renderCaves = node.getNode("renderCaves").getBoolean(false); //bounds - int minX = node.getNode("minX").getInt(RenderSettings.super.getMin().getX()); - int maxX = node.getNode("maxX").getInt(RenderSettings.super.getMax().getX()); - int minZ = node.getNode("minZ").getInt(RenderSettings.super.getMin().getZ()); - int maxZ = node.getNode("maxZ").getInt(RenderSettings.super.getMax().getZ()); - int minY = node.getNode("minY").getInt(RenderSettings.super.getMin().getY()); - int maxY = node.getNode("maxY").getInt(RenderSettings.super.getMax().getY()); + int minX = node.getNode("minX").getInt(MapSettings.super.getMin().getX()); + int maxX = node.getNode("maxX").getInt(MapSettings.super.getMax().getX()); + int minZ = node.getNode("minZ").getInt(MapSettings.super.getMin().getZ()); + int maxZ = node.getNode("maxZ").getInt(MapSettings.super.getMax().getZ()); + int minY = node.getNode("minY").getInt(MapSettings.super.getMin().getY()); + int maxY = node.getNode("maxY").getInt(MapSettings.super.getMax().getY()); this.min = new Vector3i(minX, minY, minZ); this.max = new Vector3i(maxX, maxY, maxZ); @@ -146,15 +146,18 @@ public boolean isRenderCaves() { public boolean isIgnoreMissingLightData() { return ignoreMissingLightData; } - + + @Override public int getHiresTileSize() { return hiresTileSize; } + @Override public int getLowresPointsPerHiresTile() { return lowresPointsPerHiresTile; } + @Override public int getLowresPointsPerLowresTile() { return lowresPointsPerLowresTile; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/BmMap.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/BmMap.java new file mode 100644 index 00000000..1614e023 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/BmMap.java @@ -0,0 +1,143 @@ +/* + * 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.map; + +import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.map.hires.HiresModel; +import de.bluecolored.bluemap.core.map.hires.HiresModelManager; +import de.bluecolored.bluemap.core.map.lowres.LowresModelManager; +import de.bluecolored.bluemap.core.resourcepack.ResourcePack; +import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.world.World; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +public class BmMap { + + private final String id; + private final String name; + private final World world; + private final Path fileRoot; + + private final MapRenderState renderState; + + private final HiresModelManager hiresModelManager; + private final LowresModelManager lowresModelManager; + + public BmMap(String id, String name, World world, Path fileRoot, ResourcePack resourcePack, MapSettings settings) throws IOException { + this.id = Objects.requireNonNull(id); + this.name = Objects.requireNonNull(name); + this.world = Objects.requireNonNull(world); + this.fileRoot = Objects.requireNonNull(fileRoot); + + Objects.requireNonNull(resourcePack); + Objects.requireNonNull(settings); + + this.renderState = new MapRenderState(); + + File rstateFile = getRenderStateFile(); + if (rstateFile.exists()) { + this.renderState.load(rstateFile); + } + + this.hiresModelManager = new HiresModelManager( + fileRoot.resolve("hires"), + resourcePack, + settings, + new Grid(settings.getHiresTileSize(), 2) + ); + + this.lowresModelManager = new LowresModelManager( + fileRoot.resolve("lowres"), + new Vector2i(settings.getLowresPointsPerLowresTile(), settings.getLowresPointsPerLowresTile()), + new Vector2i(settings.getLowresPointsPerHiresTile(), settings.getLowresPointsPerHiresTile()), + settings.useGzipCompression() + ); + } + + public void renderTile(Vector2i tile) { + HiresModel hiresModel = hiresModelManager.render(world, tile); + lowresModelManager.render(hiresModel); + } + + public synchronized void save() { + lowresModelManager.save(); + + try { + this.renderState.save(getRenderStateFile()); + } catch (IOException ex){ + Logger.global.logError("Failed to save render-state for map: '" + this.id + "'!", ex); + } + } + + public File getRenderStateFile() { + return fileRoot.resolve(".rstate").toFile(); + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public World getWorld() { + return world; + } + + public MapRenderState getRenderState() { + return renderState; + } + + public HiresModelManager getHiresModelManager() { + return hiresModelManager; + } + + public LowresModelManager getLowresModelManager() { + return lowresModelManager; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof BmMap) { + BmMap that = (BmMap) obj; + + return this.id.equals(that.id); + } + + return false; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapRenderState.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapRenderState.java new file mode 100644 index 00000000..1166f276 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapRenderState.java @@ -0,0 +1,76 @@ +package de.bluecolored.bluemap.core.map; + +import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.core.util.FileUtils; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class MapRenderState { + + private final Map regionRenderTimes; + + public MapRenderState() { + regionRenderTimes = new HashMap<>(); + } + + public synchronized void setRenderTime(Vector2i regionPos, long renderTime) { + regionRenderTimes.put(regionPos, renderTime); + } + + public synchronized long getRenderTime(Vector2i regionPos) { + Long renderTime = regionRenderTimes.get(regionPos); + if (renderTime == null) return -1; + else return renderTime; + } + + public synchronized void save(File file) throws IOException { + FileUtils.delete(file); + FileUtils.createFile(file); + + try ( + FileOutputStream fOut = new FileOutputStream(file); + GZIPOutputStream gOut = new GZIPOutputStream(fOut); + DataOutputStream dOut = new DataOutputStream(gOut) + ) { + dOut.writeInt(regionRenderTimes.size()); + + for (Map.Entry entry : regionRenderTimes.entrySet()) { + Vector2i regionPos = entry.getKey(); + long renderTime = entry.getValue(); + + dOut.writeInt(regionPos.getX()); + dOut.writeInt(regionPos.getY()); + dOut.writeLong(renderTime); + } + + dOut.flush(); + } + } + + public synchronized void load(File file) throws IOException { + regionRenderTimes.clear(); + + try ( + FileInputStream fIn = new FileInputStream(file); + GZIPInputStream gIn = new GZIPInputStream(fIn); + DataInputStream dIn = new DataInputStream(gIn) + ) { + int size = dIn.readInt(); + + for (int i = 0; i < size; i++) { + Vector2i regionPos = new Vector2i( + dIn.readInt(), + dIn.readInt() + ); + long renderTime = dIn.readLong(); + + regionRenderTimes.put(regionPos, renderTime); + } + } + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettings.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettings.java new file mode 100644 index 00000000..419a330d --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettings.java @@ -0,0 +1,13 @@ +package de.bluecolored.bluemap.core.map; + +import de.bluecolored.bluemap.core.map.hires.RenderSettings; + +public interface MapSettings extends RenderSettings { + + int getHiresTileSize(); + + int getLowresPointsPerLowresTile(); + + int getLowresPointsPerHiresTile(); + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModel.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModel.java similarity index 90% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModel.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModel.java index 72471916..bbe01eb6 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModel.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModel.java @@ -22,31 +22,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.hires; +package de.bluecolored.bluemap.core.map.hires; -import java.util.UUID; - -import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector4f; - import de.bluecolored.bluemap.core.model.ExtendedModel; +import java.util.UUID; + /** * A model, containing additional information about the tile it represents */ public class HiresModel extends ExtendedModel { private UUID world; - private Vector2i tile; private Vector3i blockMin, blockMax, blockSize; private int[][] heights; private Vector4f[][] colors; - public HiresModel(UUID world, Vector2i tile, Vector3i blockMin, Vector3i blockMax) { + public HiresModel(UUID world, Vector3i blockMin, Vector3i blockMax) { this.world = world; - this.tile = tile; this.blockMin = blockMin; this.blockMax = blockMax; this.blockSize = blockMax.sub(blockMin).add(Vector3i.ONE); @@ -89,8 +85,4 @@ public Vector3i getBlockSize(){ return blockSize; } - public Vector2i getTile(){ - return tile; - } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelManager.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java similarity index 67% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelManager.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java index 6b2c8f6f..52e41401 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelManager.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java @@ -22,17 +22,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.hires; +package de.bluecolored.bluemap.core.map.hires; import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3d; import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.render.RenderSettings; -import de.bluecolored.bluemap.core.render.WorldTile; import de.bluecolored.bluemap.core.resourcepack.ResourcePack; -import de.bluecolored.bluemap.core.util.AABB; import de.bluecolored.bluemap.core.util.FileUtils; +import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.world.World; import java.io.*; import java.nio.charset.StandardCharsets; @@ -44,24 +43,20 @@ public class HiresModelManager { - private Path fileRoot; - private HiresModelRenderer renderer; - - private Vector2i tileSize; - private Vector2i gridOrigin; - - private boolean useGzip; - - public HiresModelManager(Path fileRoot, ResourcePack resourcePack, RenderSettings renderSettings, Vector2i tileSize) { - this(fileRoot, new HiresModelRenderer(resourcePack, renderSettings), tileSize, new Vector2i(2, 2), renderSettings.useGzipCompression()); + private final Path fileRoot; + private final HiresModelRenderer renderer; + private final Grid tileGrid; + private final boolean useGzip; + + public HiresModelManager(Path fileRoot, ResourcePack resourcePack, RenderSettings renderSettings, Grid tileGrid) { + this(fileRoot, new HiresModelRenderer(resourcePack, renderSettings), tileGrid, renderSettings.useGzipCompression()); } - - public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Vector2i tileSize, Vector2i gridOrigin, boolean useGzip) { + + public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Grid tileGrid, boolean useGzip) { this.fileRoot = fileRoot; this.renderer = renderer; - - this.tileSize = tileSize; - this.gridOrigin = gridOrigin; + + this.tileGrid = tileGrid; this.useGzip = useGzip; } @@ -69,19 +64,25 @@ public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Vector2i ti /** * Renders the given world tile with the provided render-settings */ - public HiresModel render(WorldTile tile) { - HiresModel model = renderer.render(tile, getTileRegion(tile)); - save(model); + public HiresModel render(World world, Vector2i tile) { + Vector2i tileMin = tileGrid.getCellMin(tile); + Vector2i tileMax = tileGrid.getCellMax(tile); + + Vector3i modelMin = new Vector3i(tileMin.getX(), world.getMinY(), tileMin.getY()); + Vector3i modelMax = new Vector3i(tileMax.getX(), world.getMaxY(), tileMax.getY()); + + HiresModel model = renderer.render(world, modelMin, modelMax); + save(model, tile); return model; } - private void save(final HiresModel model) { + private void save(final HiresModel model, Vector2i tile) { final String modelJson = model.toBufferGeometry().toJson(); - save(model, modelJson); + save(modelJson, tile); } - private void save(HiresModel model, String modelJson){ - File file = getFile(model.getTile(), useGzip); + private void save(String modelJson, Vector2i tile){ + File file = getFile(tile, useGzip); try { FileUtils.createFile(file); @@ -104,14 +105,15 @@ private void save(HiresModel model, String modelJson){ /** * Returns all tiles that the provided chunks are intersecting */ + @Deprecated public Collection getTilesForChunks(Iterable chunks){ Set tiles = new HashSet<>(); for (Vector2i chunk : chunks) { Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16); //loop to cover the case that a tile is smaller then a chunk, should normally only add one tile (at 0, 0) - for (int x = 0; x < 15; x += getTileSize().getX()) { - for (int z = 0; z < 15; z += getTileSize().getY()) { + for (int x = 0; x < 15; x += tileGrid.getGridSize().getX()) { + for (int z = 0; z < 15; z += tileGrid.getGridSize().getY()) { tiles.add(posToTile(minBlockPos.add(x, 0, z))); } } @@ -125,52 +127,24 @@ public Collection getTilesForChunks(Iterable chunks){ } /** - * Returns the region of blocks that a tile includes + * Returns the tile-grid */ - public AABB getTileRegion(WorldTile tile) { - Vector3i min = new Vector3i( - tile.getTile().getX() * tileSize.getX() + gridOrigin.getX(), - 0, - tile.getTile().getY() * tileSize.getY() + gridOrigin.getY() - ); - Vector3i max = min.add( - tileSize.getX() - 1, - 255, - tileSize.getY() - 1 - ); - return new AABB(min, max); - } - - /** - * Returns the tile-size - */ - public Vector2i getTileSize() { - return tileSize; - } - - /** - * Returns the grid-origin - */ - public Vector2i getGridOrigin() { - return gridOrigin; + public Grid getTileGrid() { + return tileGrid; } /** * Converts a block-position to a map-tile-coordinate */ public Vector2i posToTile(Vector3i pos){ - return posToTile(pos.toDouble()); + return tileGrid.getCell(pos.toVector2(true)); } /** * Converts a block-position to a map-tile-coordinate */ public Vector2i posToTile(Vector3d pos){ - pos = pos.sub(new Vector3d(gridOrigin.getX(), 0.0, gridOrigin.getY())); - return Vector2i.from( - (int) Math.floor(pos.getX() / getTileSize().getX()), - (int) Math.floor(pos.getZ() / getTileSize().getY()) - ); + return tileGrid.getCell(new Vector2i(pos.getFloorX(), pos.getFloorZ())); } /** diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelRenderer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java similarity index 85% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelRenderer.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java index 5e70ccd3..1adb5914 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/HiresModelRenderer.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java @@ -22,19 +22,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.hires; +package de.bluecolored.bluemap.core.map.hires; import com.flowpowered.math.vector.Vector3f; import com.flowpowered.math.vector.Vector3i; import com.flowpowered.math.vector.Vector4f; - -import de.bluecolored.bluemap.core.render.RenderSettings; -import de.bluecolored.bluemap.core.render.WorldTile; -import de.bluecolored.bluemap.core.render.hires.blockmodel.BlockStateModel; -import de.bluecolored.bluemap.core.render.hires.blockmodel.BlockStateModelFactory; +import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModel; +import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModelFactory; import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException; import de.bluecolored.bluemap.core.resourcepack.ResourcePack; -import de.bluecolored.bluemap.core.util.AABB; import de.bluecolored.bluemap.core.util.MathUtils; import de.bluecolored.bluemap.core.world.Block; import de.bluecolored.bluemap.core.world.BlockState; @@ -61,16 +57,11 @@ public HiresModelRenderer(ResourcePack resourcePack, RenderSettings renderSettin } } - public HiresModel render(WorldTile tile, AABB region) { - Vector3i modelMin = region.getMin(); - Vector3i modelMax = region.getMax(); - + public HiresModel render(World world, Vector3i modelMin, Vector3i modelMax) { Vector3i min = modelMin.max(renderSettings.getMin()); Vector3i max = modelMax.min(renderSettings.getMax()); - World world = tile.getWorld(); - - HiresModel model = new HiresModel(tile.getWorld().getUUID(), tile.getTile(), modelMin, modelMax); + HiresModel model = new HiresModel(world.getUUID(), modelMin, modelMax); for (int x = min.getX(); x <= max.getX(); x++){ for (int z = min.getZ(); z <= max.getZ(); z++){ @@ -104,7 +95,7 @@ public HiresModel render(WorldTile tile, AABB region) { color = MathUtils.overlayColors(blockModel.getMapColor(), color); } - //TODO: quick hack to random offset grass + //quick hack to random offset grass if (block.getBlockState().getFullId().equals(grassId)){ float dx = (MathUtils.hashToFloat(x, y, z, 123984) - 0.5f) * 0.75f; float dz = (MathUtils.hashToFloat(x, y, z, 345542) - 0.5f) * 0.75f; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/RenderSettings.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/RenderSettings.java similarity index 85% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/RenderSettings.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/RenderSettings.java index 3e8a11fc..d827ee0a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/RenderSettings.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/RenderSettings.java @@ -22,14 +22,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render; +package de.bluecolored.bluemap.core.map.hires; import com.flowpowered.math.vector.Vector3i; public interface RenderSettings { - static final Vector3i DEFAULT_MIN = Vector3i.from(Integer.MIN_VALUE); - static final Vector3i DEFAULT_MAX = Vector3i.from(Integer.MAX_VALUE); + Vector3i DEFAULT_MIN = Vector3i.from(Integer.MIN_VALUE); + Vector3i DEFAULT_MAX = Vector3i.from(Integer.MAX_VALUE); /** * Whether faces that have a sky-light-value of 0 will be rendered or not. @@ -68,13 +68,4 @@ default boolean useGzipCompression() { return true; } - default RenderSettings copy() { - return new StaticRenderSettings( - isExcludeFacesWithoutSunlight(), - getMin(), - getMax(), - isRenderEdges() - ); - } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/BlockStateModel.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModel.java similarity index 97% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/BlockStateModel.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModel.java index 27b5586d..0cc13514 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/BlockStateModel.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModel.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.hires.blockmodel; +package de.bluecolored.bluemap.core.map.hires.blockmodel; import com.flowpowered.math.vector.Vector4f; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/BlockStateModelFactory.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModelFactory.java similarity index 96% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/BlockStateModelFactory.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModelFactory.java index 74a4670c..9fdb5110 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/BlockStateModelFactory.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModelFactory.java @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.hires.blockmodel; +package de.bluecolored.bluemap.core.map.hires.blockmodel; -import de.bluecolored.bluemap.core.render.RenderSettings; +import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator; import de.bluecolored.bluemap.core.resourcepack.BlockStateResource; import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/LiquidModelBuilder.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java similarity index 98% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/LiquidModelBuilder.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java index 82c5fd5a..b366e7e2 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/LiquidModelBuilder.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.hires.blockmodel; +package de.bluecolored.bluemap.core.map.hires.blockmodel; import java.util.HashSet; @@ -35,7 +35,7 @@ import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.model.ExtendedFace; import de.bluecolored.bluemap.core.model.ExtendedModel; -import de.bluecolored.bluemap.core.render.RenderSettings; +import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator; import de.bluecolored.bluemap.core.resourcepack.BlockModelResource; import de.bluecolored.bluemap.core.resourcepack.Texture; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/ResourceModelBuilder.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java similarity index 98% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/ResourceModelBuilder.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java index b5515cd4..610a54fe 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/hires/blockmodel/ResourceModelBuilder.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.hires.blockmodel; +package de.bluecolored.bluemap.core.map.hires.blockmodel; import com.flowpowered.math.TrigMath; import com.flowpowered.math.imaginary.Complexf; @@ -34,7 +34,7 @@ import com.flowpowered.math.vector.Vector4f; import de.bluecolored.bluemap.core.model.ExtendedFace; -import de.bluecolored.bluemap.core.render.RenderSettings; +import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator; import de.bluecolored.bluemap.core.resourcepack.BlockModelResource; import de.bluecolored.bluemap.core.resourcepack.BlockModelResource.Element.Rotation; @@ -171,7 +171,7 @@ private void createElementFace(BlockStateModel model, TransformedBlockModelResou Quaternionf rot = Quaternionf.fromAxesAnglesDeg(rotation.getX(), rotation.getY(), 0); uvLockAngle = (int) rot.getAxesAnglesDeg().dot(faceDir.toVector().toFloat()); - //TODO: my math has stopped working, there has to be a more consistent solution + //my math has stopped working, there has to be a more consistent solution for this... if (rotation.getX() >= 180 && rotation.getY() != 90 && rotation.getY() != 270) uvLockAngle += 180; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/lowres/LowresModel.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresModel.java similarity index 85% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/lowres/LowresModel.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresModel.java index 871da8cc..00394a06 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/lowres/LowresModel.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresModel.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.lowres; +package de.bluecolored.bluemap.core.map.lowres; import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3f; @@ -33,21 +33,15 @@ import java.io.*; import java.nio.charset.StandardCharsets; -import java.util.HashMap; import java.util.Map; -import java.util.Objects; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.zip.GZIPOutputStream; public class LowresModel { - - private UUID world; - private Vector2i tilePos; - private BufferGeometry model; - + + private final BufferGeometry model; private Map changes; private boolean hasUnsavedChanges; @@ -56,17 +50,13 @@ public class LowresModel { fileLock = new Object(), modelLock = new Object(); - public LowresModel(UUID world, Vector2i tilePos, Vector2i gridSize) { + public LowresModel(Vector2i gridSize) { this( - world, - tilePos, ModelUtils.makeGrid(gridSize).toBufferGeometry() ); } - public LowresModel(UUID world, Vector2i tilePos, BufferGeometry model) { - this.world = world; - this.tilePos = tilePos; + public LowresModel(BufferGeometry model) { this.model = model; this.changes = new ConcurrentHashMap<>(); @@ -134,7 +124,7 @@ public void flush(){ if (changes.isEmpty()) return; Map points = changes; - changes = new HashMap<>(); + changes = new ConcurrentHashMap<>(); float[] position = model.attributes.get("position").values(); float[] color = model.attributes.get("color").values(); @@ -178,36 +168,12 @@ public BufferGeometry getBufferGeometry(){ return model; } - public UUID getWorld(){ - return world; - } - - public Vector2i getTile(){ - return tilePos; - } - - @Override - public int hashCode() { - return Objects.hash(world, tilePos); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof LowresModel){ - LowresModel other = (LowresModel) obj; - if (!other.world.equals(world)) return false; - if (other.tilePos.equals(tilePos)) return true; - } - - return false; - } - /** * a point on this lowres-model-grid */ - public class LowresPoint { - private float height; - private Vector3f color; + public static class LowresPoint { + private final float height; + private final Vector3f color; public LowresPoint(float height, Vector3f color) { this.height = height; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/lowres/LowresModelManager.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresModelManager.java similarity index 85% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/lowres/LowresModelManager.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresModelManager.java index 4b472fb0..b7e4762d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/lowres/LowresModelManager.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresModelManager.java @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.render.lowres; +package de.bluecolored.bluemap.core.map.lowres; import com.flowpowered.math.vector.*; import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.render.hires.HiresModel; +import de.bluecolored.bluemap.core.map.hires.HiresModel; import de.bluecolored.bluemap.core.threejs.BufferGeometry; import de.bluecolored.bluemap.core.util.FileUtils; import org.apache.commons.io.IOUtils; @@ -47,19 +47,17 @@ public class LowresModelManager { - private Path fileRoot; - - private Vector2i gridSize; - private Vector2i pointsPerHiresTile; - - private Map models; - - private boolean useGzip; + private final Path fileRoot; + private final Vector2i pointsPerLowresTile; + private final Vector2i pointsPerHiresTile; + private final boolean useGzip; + + private final Map models; - public LowresModelManager(Path fileRoot, Vector2i gridSize, Vector2i pointsPerHiresTile, boolean useGzip) { + public LowresModelManager(Path fileRoot, Vector2i pointsPerLowresTile, Vector2i pointsPerHiresTile, boolean useGzip) { this.fileRoot = fileRoot; - this.gridSize = gridSize; + this.pointsPerLowresTile = pointsPerLowresTile; this.pointsPerHiresTile = pointsPerHiresTile; models = new ConcurrentHashMap<>(); @@ -68,7 +66,7 @@ public LowresModelManager(Path fileRoot, Vector2i gridSize, Vector2i pointsPerHi } /** - * Renders all points from the given highres-model onto the lowres-grid + * Renders all points from the given hires-model onto the lowres-grid */ public void render(HiresModel hiresModel) { Vector3i min = hiresModel.getBlockMin(); @@ -124,15 +122,15 @@ public void render(HiresModel hiresModel) { * Saves all unsaved changes to the models to disk */ public synchronized void save(){ - for (CachedModel model : models.values()){ - saveModel(model); + for (Entry entry : models.entrySet()){ + saveModel(entry.getKey(), entry.getValue()); } tidyUpModelCache(); } /** - * Updates a point on the lowresmodel-grid + * Updates a point on the lowres-model-grid */ public void update(UUID world, Vector2i point, float height, Vector3f color) { Vector2i tile = pointToTile(point); @@ -186,7 +184,7 @@ private LowresModel getModel(UUID world, Vector2i tile) { String json = IOUtils.toString(is, StandardCharsets.UTF_8); - model = new CachedModel(world, tile, BufferGeometry.fromJson(json)); + model = new CachedModel(BufferGeometry.fromJson(json)); } catch (IllegalArgumentException | IOException ex){ Logger.global.logError("Failed to load lowres model: " + modelFile, ex); @@ -199,7 +197,7 @@ private LowresModel getModel(UUID world, Vector2i tile) { } if (model == null){ - model = new CachedModel(world, tile, gridSize); + model = new CachedModel(pointsPerLowresTile); } models.put(modelFile, model); @@ -227,18 +225,17 @@ public synchronized void tidyUpModelCache() { int size = entries.size(); for (Entry e : entries) { if (size > 10) { - saveAndRemoveModel(e.getValue()); + saveAndRemoveModel(e.getKey(), e.getValue()); continue; } if (e.getValue().getCacheTime() > 120000) { - saveModel(e.getValue()); + saveModel(e.getKey(), e.getValue()); } } } - private synchronized void saveAndRemoveModel(CachedModel model) { - File modelFile = getFile(model.getTile(), useGzip); + private synchronized void saveAndRemoveModel(File modelFile, CachedModel model) { models.remove(modelFile); try { model.save(modelFile, false, useGzip); @@ -248,8 +245,7 @@ private synchronized void saveAndRemoveModel(CachedModel model) { } } - private void saveModel(CachedModel model) { - File modelFile = getFile(model.getTile(), useGzip); + private void saveModel(File modelFile, CachedModel model) { try { model.save(modelFile, false, useGzip); //logger.logDebug("Saved lowres tile: " + model.getTile()); @@ -263,35 +259,35 @@ private void saveModel(CachedModel model) { private Vector2i pointToTile(Vector2i point){ return point .toDouble() - .div(gridSize.toDouble()) + .div(pointsPerLowresTile.toDouble()) .floor() .toInt(); } private Vector2i getPointRelativeToTile(Vector2i tile, Vector2i point){ - return point.sub(tile.mul(gridSize)); + return point.sub(tile.mul(pointsPerLowresTile)); } public Vector2i getTileSize() { - return gridSize; + return pointsPerLowresTile; } public Vector2i getPointsPerHiresTile() { return pointsPerHiresTile; } - private class CachedModel extends LowresModel { + private static class CachedModel extends LowresModel { private long cacheTime; - public CachedModel(UUID world, Vector2i tilePos, BufferGeometry model) { - super(world, tilePos, model); + public CachedModel(BufferGeometry model) { + super(model); cacheTime = System.currentTimeMillis(); } - public CachedModel(UUID world, Vector2i tilePos, Vector2i gridSize) { - super(world, tilePos, gridSize); + public CachedModel(Vector2i gridSize) { + super(gridSize); cacheTime = System.currentTimeMillis(); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil112.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil112.java index 6432f0f3..5bd10730 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil112.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil112.java @@ -24,10 +24,7 @@ */ package de.bluecolored.bluemap.core.mca; -import java.util.Arrays; - import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper; import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper; import de.bluecolored.bluemap.core.world.Biome; @@ -38,9 +35,13 @@ import net.querz.nbt.NumberTag; import net.querz.nbt.mca.MCAUtil; -public class ChunkAnvil112 extends Chunk { - private BlockIdMapper blockIdMapper; - private BiomeMapper biomeIdMapper; +import java.util.Arrays; +import java.util.function.IntFunction; + +public class ChunkAnvil112 extends MCAChunk { + private final BiomeMapper biomeIdMapper; + private final BlockIdMapper blockIdMapper; + private final IntFunction forgeBlockIdMapper; private boolean isGenerated; private boolean hasLight; @@ -48,11 +49,12 @@ public class ChunkAnvil112 extends Chunk { private byte[] biomes; @SuppressWarnings("unchecked") - public ChunkAnvil112(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) { - super(world, chunkTag); + public ChunkAnvil112(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper, BlockIdMapper blockIdMapper, IntFunction forgeBlockIdMapper) { + super(chunkTag); - blockIdMapper = getWorld().getBlockIdMapper(); - biomeIdMapper = getWorld().getBiomeIdMapper(); + this.blockIdMapper = blockIdMapper; + this.biomeIdMapper = biomeIdMapper; + this.forgeBlockIdMapper = forgeBlockIdMapper; CompoundTag levelData = chunkTag.getCompoundTag("Level"); @@ -118,9 +120,9 @@ public LightData getLightData(Vector3i pos) { } @Override - public Biome getBiome(Vector3i pos) { - int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16) - int z = pos.getZ() & 0xF; + public Biome getBiome(int x, int y, int z) { + x = x & 0xF; // Math.floorMod(pos.getX(), 16) + z = z & 0xF; int biomeByteIndex = z * 16 + x; return biomeIdMapper.get(biomes[biomeByteIndex] & 0xFF); @@ -168,7 +170,7 @@ public BlockState getBlockState(Vector3i pos) { int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf); - String forgeIdMapping = getWorld().getForgeBlockIdMapping(blockId); + String forgeIdMapping = forgeBlockIdMapper.apply(blockId); if (forgeIdMapping != null) { return blockIdMapper.get(forgeIdMapping, blockId, blockData); } else { @@ -191,7 +193,7 @@ public String getBlockIdMeta(Vector3i pos) { } int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf); - String forgeIdMapping = getWorld().getForgeBlockIdMapping(blockId); + String forgeIdMapping = forgeBlockIdMapper.apply(blockId); return blockId + ":" + blockData + " " + forgeIdMapping; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil113.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil113.java index 9937eaf7..76ab0afa 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil113.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil113.java @@ -24,13 +24,7 @@ */ package de.bluecolored.bluemap.core.mca; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper; import de.bluecolored.bluemap.core.world.Biome; @@ -39,7 +33,12 @@ import net.querz.nbt.*; import net.querz.nbt.mca.MCAUtil; -public class ChunkAnvil113 extends Chunk { +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class ChunkAnvil113 extends MCAChunk { private BiomeMapper biomeIdMapper; private boolean isGenerated; @@ -48,16 +47,16 @@ public class ChunkAnvil113 extends Chunk { private int[] biomes; @SuppressWarnings("unchecked") - public ChunkAnvil113(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) { - super(world, chunkTag); + public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) { + super(chunkTag); - biomeIdMapper = getWorld().getBiomeIdMapper(); + this.biomeIdMapper = biomeIdMapper; CompoundTag levelData = chunkTag.getCompoundTag("Level"); String status = levelData.getString("Status"); - isGenerated = status.equals("full") || status.equals("spawn"); // full is normal fully generated and spawn seems to be converted from old format but not yet loaded if you optimized your world - hasLight = isGenerated; + this.isGenerated = status.equals("full"); + this.hasLight = isGenerated; if (!isGenerated && ignoreMissingLightData) { isGenerated = !status.equals("empty"); @@ -121,9 +120,9 @@ public LightData getLightData(Vector3i pos) { } @Override - public Biome getBiome(Vector3i pos) { - int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16) - int z = pos.getZ() & 0xF; + public Biome getBiome(int x, int y, int z) { + x = x & 0xF; // Math.floorMod(pos.getX(), 16) + z = z & 0xF; int biomeIntIndex = z * 16 + x; return biomeIdMapper.get(biomes[biomeIntIndex]); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil115.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil115.java index 57e2d44f..b5d3d6fc 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil115.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil115.java @@ -24,13 +24,7 @@ */ package de.bluecolored.bluemap.core.mca; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper; import de.bluecolored.bluemap.core.world.Biome; @@ -39,7 +33,12 @@ import net.querz.nbt.*; import net.querz.nbt.mca.MCAUtil; -public class ChunkAnvil115 extends Chunk { +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class ChunkAnvil115 extends MCAChunk { private BiomeMapper biomeIdMapper; private boolean isGenerated; @@ -48,21 +47,21 @@ public class ChunkAnvil115 extends Chunk { private int[] biomes; @SuppressWarnings("unchecked") - public ChunkAnvil115(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) { - super(world, chunkTag); + public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) { + super(chunkTag); - biomeIdMapper = getWorld().getBiomeIdMapper(); + this.biomeIdMapper = biomeIdMapper; CompoundTag levelData = chunkTag.getCompoundTag("Level"); String status = levelData.getString("Status"); - isGenerated = status.equals("full") || status.equals("spawn"); // full is normal fully generated and spawn seems to be converted from old format but not yet loaded if you optimized your world - hasLight = isGenerated; + this.isGenerated = status.equals("full"); + this.hasLight = isGenerated; if (!isGenerated && ignoreMissingLightData) { isGenerated = !status.equals("empty"); } - + sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe? if (levelData.containsKey("Sections")) { for (CompoundTag sectionTag : ((ListTag) levelData.getListTag("Sections"))) { @@ -81,7 +80,7 @@ public ChunkAnvil115(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissing } } else if (tag instanceof IntArrayTag) { - biomes = ((IntArrayTag) tag).getValue(); + biomes = ((IntArrayTag) tag).getValue(); } if (biomes == null || biomes.length == 0) { @@ -121,16 +120,16 @@ public LightData getLightData(Vector3i pos) { } @Override - public Biome getBiome(Vector3i pos) { - int x = (pos.getX() & 0xF) / 4; // Math.floorMod(pos.getX(), 16) - int z = (pos.getZ() & 0xF) / 4; - int y = pos.getY() / 4; + public Biome getBiome(int x, int y, int z) { + x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16) + z = (z & 0xF) / 4; + y = y / 4; int biomeIntIndex = y * 16 + z * 4 + x; return biomeIdMapper.get(biomes[biomeIntIndex]); } - private class Section { + private static class Section { private static final String AIR_ID = "minecraft:air"; private int sectionY; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil116.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil116.java index f16bb66f..08ac3abd 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil116.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil116.java @@ -24,13 +24,7 @@ */ package de.bluecolored.bluemap.core.mca; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper; import de.bluecolored.bluemap.core.world.Biome; @@ -39,7 +33,12 @@ import net.querz.nbt.*; import net.querz.nbt.mca.MCAUtil; -public class ChunkAnvil116 extends Chunk { +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class ChunkAnvil116 extends MCAChunk { private BiomeMapper biomeIdMapper; private boolean isGenerated; @@ -48,22 +47,22 @@ public class ChunkAnvil116 extends Chunk { private int[] biomes; @SuppressWarnings("unchecked") - public ChunkAnvil116(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) { - super(world, chunkTag); + public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) { + super(chunkTag); - biomeIdMapper = getWorld().getBiomeIdMapper(); + this.biomeIdMapper = biomeIdMapper; CompoundTag levelData = chunkTag.getCompoundTag("Level"); String status = levelData.getString("Status"); - isGenerated = status.equals("full") || status.equals("spawn"); // full is normal fully generated and spawn seems to be converted from old format but not yet loaded if you optimized your world - hasLight = isGenerated; + this.isGenerated = status.equals("full"); + this.hasLight = isGenerated; if (!isGenerated && ignoreMissingLightData) { isGenerated = !status.equals("empty"); } - - sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe? + + this.sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe? if (levelData.containsKey("Sections")) { for (CompoundTag sectionTag : ((ListTag) levelData.getListTag("Sections"))) { Section section = new Section(sectionTag); @@ -74,22 +73,22 @@ public ChunkAnvil116(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissing Tag tag = levelData.get("Biomes"); //tag can be byte-array or int-array if (tag instanceof ByteArrayTag) { byte[] bs = ((ByteArrayTag) tag).getValue(); - biomes = new int[bs.length]; + this.biomes = new int[bs.length]; for (int i = 0; i < bs.length; i++) { biomes[i] = bs[i] & 0xFF; } } else if (tag instanceof IntArrayTag) { - biomes = ((IntArrayTag) tag).getValue(); + this.biomes = ((IntArrayTag) tag).getValue(); } if (biomes == null || biomes.length == 0) { - biomes = new int[1024]; + this.biomes = new int[1024]; } if (biomes.length < 1024) { - biomes = Arrays.copyOf(biomes, 1024); + this.biomes = Arrays.copyOf(biomes, 1024); } } @@ -121,16 +120,16 @@ public LightData getLightData(Vector3i pos) { } @Override - public Biome getBiome(Vector3i pos) { - int x = (pos.getX() & 0xF) / 4; // Math.floorMod(pos.getX(), 16) - int z = (pos.getZ() & 0xF) / 4; - int y = pos.getY() / 4; + public Biome getBiome(int x, int y, int z) { + x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16) + z = (z & 0xF) / 4; + y = y / 4; int biomeIntIndex = y * 16 + z * 4 + x; return biomeIdMapper.get(biomes[biomeIntIndex]); } - private class Section { + private static class Section { private static final String AIR_ID = "minecraft:air"; private int sectionY; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/EmptyChunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/EmptyChunk.java index bcfb66ca..4820ae77 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/EmptyChunk.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/EmptyChunk.java @@ -26,16 +26,13 @@ import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.LightData; -public class EmptyChunk extends Chunk { +public class EmptyChunk extends MCAChunk { - protected EmptyChunk(MCAWorld world, Vector2i chunkPos) { - super(world, chunkPos); - } + public static final MCAChunk INSTANCE = new EmptyChunk(); @Override public boolean isGenerated() { @@ -53,7 +50,7 @@ public LightData getLightData(Vector3i pos) { } @Override - public Biome getBiome(Vector3i pos) { + public Biome getBiome(int x, int y, int z) { return Biome.DEFAULT; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/Chunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAChunk.java similarity index 61% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/Chunk.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAChunk.java index 81c9e22d..a2dffa8f 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/Chunk.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAChunk.java @@ -24,53 +24,30 @@ */ package de.bluecolored.bluemap.core.mca; -import java.io.IOException; - -import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.Chunk; import de.bluecolored.bluemap.core.world.LightData; import net.querz.nbt.CompoundTag; -public abstract class Chunk { - - private final MCAWorld world; - private final Vector2i chunkPos; - +import java.io.IOException; + +public abstract class MCAChunk implements Chunk { + private final int dataVersion; - protected Chunk(MCAWorld world, Vector2i chunkPos) { - this.world = world; - this.chunkPos = chunkPos; - + protected MCAChunk() { this.dataVersion = -1; } - protected Chunk(MCAWorld world, CompoundTag chunkTag) { - this.world = world; - - CompoundTag levelData = chunkTag.getCompoundTag("Level"); - - chunkPos = new Vector2i( - levelData.getInt("xPos"), - levelData.getInt("zPos") - ); - + protected MCAChunk(CompoundTag chunkTag) { dataVersion = chunkTag.getInt("DataVersion"); } - + + @Override public abstract boolean isGenerated(); - public Vector2i getChunkPos() { - return chunkPos; - } - - public MCAWorld getWorld() { - return world; - } - public int getDataVersion() { return dataVersion; } @@ -79,19 +56,19 @@ public int getDataVersion() { public abstract LightData getLightData(Vector3i pos); - public abstract Biome getBiome(Vector3i pos); + public abstract Biome getBiome(int x, int y, int z); - public static Chunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) throws IOException { + public static MCAChunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) throws IOException { int version = chunkTag.getInt("DataVersion"); - if (version < 1400) return new ChunkAnvil112(world, chunkTag, ignoreMissingLightData); - if (version < 2200) return new ChunkAnvil113(world, chunkTag, ignoreMissingLightData); - if (version < 2500) return new ChunkAnvil115(world, chunkTag, ignoreMissingLightData); - return new ChunkAnvil116(world, chunkTag, ignoreMissingLightData); + if (version < 1400) return new ChunkAnvil112(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper(), world.getBlockIdMapper(), world::getForgeBlockIdMapping); + if (version < 2200) return new ChunkAnvil113(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper()); + if (version < 2500) return new ChunkAnvil115(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper()); + return new ChunkAnvil116(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper()); } - - public static Chunk empty(MCAWorld world, Vector2i chunkPos) { - return new EmptyChunk(world, chunkPos); + + public static MCAChunk empty() { + return EmptyChunk.INSTANCE; } - + } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCARegion.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCARegion.java new file mode 100644 index 00000000..c2c83e04 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCARegion.java @@ -0,0 +1,108 @@ +package de.bluecolored.bluemap.core.mca; + +import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.world.Region; +import net.querz.nbt.CompoundTag; +import net.querz.nbt.Tag; +import net.querz.nbt.mca.CompressionType; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class MCARegion implements Region { + + private final MCAWorld world; + private final File regionFile; + private final Vector2i regionPos; + + public MCARegion(MCAWorld world, File regionFile) throws IllegalArgumentException { + this.world = world; + this.regionFile = regionFile; + + String[] filenameParts = regionFile.getName().split("\\."); + int rX = Integer.parseInt(filenameParts[1]); + int rZ = Integer.parseInt(filenameParts[2]); + + this.regionPos = new Vector2i(rX, rZ); + } + + @Override + public MCAChunk loadChunk(int chunkX, int chunkZ, boolean ignoreMissingLightData) throws IOException { + try (RandomAccessFile raf = new RandomAccessFile(regionFile, "r")) { + + int xzChunk = Math.floorMod(chunkZ, 32) * 32 + Math.floorMod(chunkX, 32); + + raf.seek(xzChunk * 4); + int offset = raf.read() << 16; + offset |= (raf.read() & 0xFF) << 8; + offset |= raf.read() & 0xFF; + offset *= 4096; + + int size = raf.readByte() * 4096; + if (size == 0) { + return MCAChunk.empty(); + } + + raf.seek(offset + 4); // +4 skip chunk size + + byte compressionTypeByte = raf.readByte(); + CompressionType compressionType = CompressionType.getFromID(compressionTypeByte); + if (compressionType == null) { + throw new IOException("Invalid compression type " + compressionTypeByte); + } + + DataInputStream dis = new DataInputStream(new BufferedInputStream(compressionType.decompress(new FileInputStream(raf.getFD())))); + Tag tag = Tag.deserialize(dis, Tag.DEFAULT_MAX_DEPTH); + if (tag instanceof CompoundTag) { + return MCAChunk.create(world, (CompoundTag) tag, ignoreMissingLightData); + } else { + throw new IOException("Invalid data tag: " + (tag == null ? "null" : tag.getClass().getName())); + } + + } catch (RuntimeException e) { + throw new IOException(e); + } + } + + @Override + public Collection listChunks(long modifiedSince) { + List chunks = new ArrayList<>(1024); //1024 = 32 x 32 chunks per region-file + + try (RandomAccessFile raf = new RandomAccessFile(regionFile, "r")) { + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + Vector2i chunk = new Vector2i(regionPos.getX() * 32 + x, regionPos.getY() * 32 + z); + int xzChunk = z * 32 + x; + + raf.seek(xzChunk * 4 + 3); + int size = raf.readByte() * 4096; + + if (size == 0) continue; + + raf.seek(xzChunk * 4 + 4096); + int timestamp = raf.read() << 24; + timestamp |= (raf.read() & 0xFF) << 16; + timestamp |= (raf.read() & 0xFF) << 8; + timestamp |= raf.read() & 0xFF; + + if (timestamp >= (modifiedSince / 1000)) { + chunks.add(chunk); + } + } + } + } catch (RuntimeException | IOException ex) { + Logger.global.logWarning("Failed to read .mca file: " + regionFile.getAbsolutePath() + " (" + ex.toString() + ")"); + } + + return chunks; + } + + @Override + public File getRegionFile() { + return regionFile; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java index 6f212db0..03c590fc 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java @@ -42,26 +42,28 @@ import net.querz.nbt.ListTag; import net.querz.nbt.NBTUtil; import net.querz.nbt.Tag; -import net.querz.nbt.mca.CompressionType; -import net.querz.nbt.mca.MCAUtil; -import java.io.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.nio.file.Path; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; public class MCAWorld implements World { + private static final Grid CHUNK_GRID = new Grid(16); + private static final Grid REGION_GRID = new Grid(32).multiply(CHUNK_GRID); + private final UUID uuid; private final Path worldFolder; private final MinecraftVersion minecraftVersion; private String name; - private int seaLevel; private Vector3i spawnPoint; - private final LoadingCache chunkCache; - + private final LoadingCache regionCache; + private final LoadingCache chunkCache; + private BlockIdMapper blockIdMapper; private BlockPropertiesMapper blockPropertiesMapper; private BiomeMapper biomeMapper; @@ -70,15 +72,13 @@ public class MCAWorld implements World { private boolean ignoreMissingLightData; - private Map forgeBlockMappings; + private final Map forgeBlockMappings; private MCAWorld( Path worldFolder, UUID uuid, MinecraftVersion minecraftVersion, - String name, - int worldHeight, - int seaLevel, + String name, Vector3i spawnPoint, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, @@ -89,7 +89,6 @@ private MCAWorld( this.worldFolder = worldFolder; this.minecraftVersion = minecraftVersion; this.name = name; - this.seaLevel = seaLevel; this.spawnPoint = spawnPoint; this.blockIdMapper = blockIdMapper; @@ -114,30 +113,33 @@ private MCAWorld( registerBlockStateExtension(new DoublePlantExtension(minecraftVersion)); registerBlockStateExtension(new DoubleChestExtension()); + this.regionCache = Caffeine.newBuilder() + .executor(BlueMap.THREAD_POOL) + .maximumSize(100) + .expireAfterWrite(1, TimeUnit.MINUTES) + .build(this::loadRegion); + this.chunkCache = Caffeine.newBuilder() - .executor(BlueMap.THREAD_POOL) - .maximumSize(500) - .expireAfterWrite(1, TimeUnit.MINUTES) - .build(chunkPos -> this.loadChunkOrEmpty(chunkPos, 2, 1000)); + .executor(BlueMap.THREAD_POOL) + .maximumSize(500) + .expireAfterWrite(1, TimeUnit.MINUTES) + .build(this::loadChunk); } - + public BlockState getBlockState(Vector3i pos) { - Vector2i chunkPos = blockToChunk(pos); - Chunk chunk = getChunk(chunkPos); - return chunk.getBlockState(pos); + return getChunk(blockToChunk(pos)).getBlockState(pos); } @Override - public Biome getBiome(Vector3i pos) { - if (pos.getY() < getMinY()) { - pos = new Vector3i(pos.getX(), getMinY(), pos.getZ()); - } else if (pos.getY() > getMaxY()) { - pos = new Vector3i(pos.getX(), getMaxY(), pos.getZ()); + public Biome getBiome(int x, int y, int z) { + if (y < getMinY()) { + y = getMinY(); + } else if (y > getMaxY()) { + y = getMaxY(); } - - Vector2i chunkPos = blockToChunk(pos); - Chunk chunk = getChunk(chunkPos); - return chunk.getBiome(pos); + + MCAChunk chunk = getChunk(x >> 4, z >> 4); + return chunk.getBiome(x, y, z); } @Override @@ -147,17 +149,16 @@ public Block getBlock(Vector3i pos) { } else if (pos.getY() > getMaxY()) { return new Block(this, BlockState.AIR, LightData.SKY, Biome.DEFAULT, BlockProperties.TRANSPARENT, pos); } - - Vector2i chunkPos = blockToChunk(pos); - Chunk chunk = getChunk(chunkPos); + + MCAChunk chunk = getChunk(blockToChunk(pos)); BlockState blockState = getExtendedBlockState(chunk, pos); LightData lightData = chunk.getLightData(pos); - Biome biome = chunk.getBiome(pos); + Biome biome = chunk.getBiome(pos.getX(), pos.getY(), pos.getZ()); BlockProperties properties = blockPropertiesMapper.get(blockState); return new Block(this, blockState, lightData, biome, properties, pos); } - private BlockState getExtendedBlockState(Chunk chunk, Vector3i pos) { + private BlockState getExtendedBlockState(MCAChunk chunk, Vector3i pos) { BlockState blockState = chunk.getBlockState(pos); if (chunk instanceof ChunkAnvil112) { // only use extensions if old format chunk (1.12) in the new format block-states are saved with extensions @@ -168,137 +169,42 @@ private BlockState getExtendedBlockState(Chunk chunk, Vector3i pos) { return blockState; } - - public Chunk getChunk(Vector2i chunkPos) { - try { - Chunk chunk = chunkCache.get(chunkPos); - return chunk; - } catch (RuntimeException e) { - if (e.getCause() instanceof InterruptedException) Thread.currentThread().interrupt(); - throw e; - } - } - - private Chunk loadChunkOrEmpty(Vector2i chunkPos, int tries, long tryInterval) { - Exception loadException = null; - for (int i = 0; i < tries; i++) { - try { - return loadChunk(chunkPos); - } catch (IOException | RuntimeException e) { - if (loadException != null) e.addSuppressed(loadException); - loadException = e; - - if (tryInterval > 0 && i+1 < tries) { - try { - Thread.sleep(tryInterval); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - break; - } - } - } - } - Logger.global.logDebug("Unexpected exception trying to load chunk (" + chunkPos + "):" + loadException); - return Chunk.empty(this, chunkPos); - } - - private Chunk loadChunk(Vector2i chunkPos) throws IOException { - Vector2i regionPos = chunkToRegion(chunkPos); - Path regionPath = getMCAFilePath(regionPos); - - File regionFile = regionPath.toFile(); - if (!regionFile.exists() || regionFile.length() <= 0) return Chunk.empty(this, chunkPos); - - try (RandomAccessFile raf = new RandomAccessFile(regionFile, "r")) { - - int xzChunk = Math.floorMod(chunkPos.getY(), 32) * 32 + Math.floorMod(chunkPos.getX(), 32); - - raf.seek(xzChunk * 4); - int offset = raf.read() << 16; - offset |= (raf.read() & 0xFF) << 8; - offset |= raf.read() & 0xFF; - offset *= 4096; - - int size = raf.readByte() * 4096; - if (size == 0) { - return Chunk.empty(this, chunkPos); - } - - raf.seek(offset + 4); // +4 skip chunk size - - byte compressionTypeByte = raf.readByte(); - CompressionType compressionType = CompressionType.getFromID(compressionTypeByte); - if (compressionType == null) { - throw new IOException("Invalid compression type " + compressionTypeByte); - } - - DataInputStream dis = new DataInputStream(new BufferedInputStream(compressionType.decompress(new FileInputStream(raf.getFD())))); - Tag tag = Tag.deserialize(dis, Tag.DEFAULT_MAX_DEPTH); - if (tag instanceof CompoundTag) { - return Chunk.create(this, (CompoundTag) tag, ignoreMissingLightData); - } else { - throw new IOException("Invalid data tag: " + (tag == null ? "null" : tag.getClass().getName())); - } - - } catch (RuntimeException e) { - throw new IOException(e); - } - } - @Override - public boolean isChunkGenerated(Vector2i chunkPos) { - Chunk chunk = getChunk(chunkPos); - return chunk.isGenerated(); + public MCAChunk getChunk(int x, int z) { + return getChunk(new Vector2i(x, z)); } - + + public MCAChunk getChunk(Vector2i pos) { + return chunkCache.get(pos); + } + @Override - public Collection getChunkList(long modifiedSinceMillis, Predicate filter){ - List chunks = new ArrayList<>(10000); - - if (!getRegionFolder().toFile().isDirectory()) return Collections.emptyList(); - - for (File file : getRegionFolder().toFile().listFiles()) { + public MCARegion getRegion(int x, int z) { + return regionCache.get(new Vector2i(x, z)); + } + + @Override + public Collection listRegions() { + File[] regionFiles = getRegionFolder().toFile().listFiles(); + if (regionFiles == null) return Collections.emptyList(); + + List regions = new ArrayList<>(regionFiles.length); + + for (File file : regionFiles) { if (!file.getName().endsWith(".mca")) continue; if (file.length() <= 0) continue; - - try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { + try { String[] filenameParts = file.getName().split("\\."); int rX = Integer.parseInt(filenameParts[1]); int rZ = Integer.parseInt(filenameParts[2]); - - for (int x = 0; x < 32; x++) { - for (int z = 0; z < 32; z++) { - Vector2i chunk = new Vector2i(rX * 32 + x, rZ * 32 + z); - if (filter.test(chunk)) { - - int xzChunk = z * 32 + x; - - raf.seek(xzChunk * 4 + 3); - int size = raf.readByte() * 4096; - - if (size == 0) continue; - - raf.seek(xzChunk * 4 + 4096); - int timestamp = raf.read() << 24; - timestamp |= (raf.read() & 0xFF) << 16; - timestamp |= (raf.read() & 0xFF) << 8; - timestamp |= raf.read() & 0xFF; - - if (timestamp >= (modifiedSinceMillis / 1000)) { - chunks.add(chunk); - } - - } - } - } - } catch (RuntimeException | IOException ex) { - Logger.global.logWarning("Failed to read .mca file: " + file.getAbsolutePath() + " (" + ex.toString() + ")"); - } + + regions.add(new Vector2i(rX, rZ)); + } catch (NumberFormatException ignore) {} } - - return chunks; + + return regions; } @Override @@ -318,7 +224,27 @@ public Path getSaveFolder() { @Override public int getSeaLevel() { - return seaLevel; + return 63; + } + + @Override + public int getMinY() { + return 0; + } + + @Override + public int getMaxY() { + return 255; + } + + @Override + public Grid getChunkGrid() { + return CHUNK_GRID; + } + + @Override + public Grid getRegionGrid() { + return REGION_GRID; } @Override @@ -332,8 +258,8 @@ public void invalidateChunkCache() { } @Override - public void invalidateChunkCache(Vector2i chunk) { - chunkCache.invalidate(chunk); + public void invalidateChunkCache(int x, int z) { + chunkCache.invalidate(new Vector2i(x, z)); } @Override @@ -381,8 +307,8 @@ private Path getRegionFolder() { return worldFolder.resolve("region"); } - private Path getMCAFilePath(Vector2i region) { - return getRegionFolder().resolve(MCAUtil.createNameFromRegionLocation(region.getX(), region.getY())); + private File getMCAFile(int regionX, int regionZ) { + return getRegionFolder().resolve("r." + regionX + "." + regionZ + ".mca").toFile(); } private void registerBlockStateExtension(BlockStateExtension extension) { @@ -390,7 +316,48 @@ private void registerBlockStateExtension(BlockStateExtension extension) { this.blockStateExtensions.put(id, extension); } } - + + private MCARegion loadRegion(Vector2i regionPos) { + return loadRegion(regionPos.getX(), regionPos.getY()); + } + + private MCARegion loadRegion(int x, int z) { + File regionPath = getMCAFile(x, z); + return new MCARegion(this, regionPath); + } + + private MCAChunk loadChunk(Vector2i chunkPos) { + return loadChunk(chunkPos.getX(), chunkPos.getY()); + } + + private MCAChunk loadChunk(int x, int z) { + final int tries = 3; + final int tryInterval = 1000; + + Exception loadException = null; + for (int i = 0; i < tries; i++) { + try { + return getRegion(x >> 5, z >> 5) + .loadChunk(x, z, ignoreMissingLightData); + } catch (IOException | RuntimeException e) { + if (loadException != null) e.addSuppressed(loadException); + loadException = e; + + if (i + 1 < tries) { + try { + Thread.sleep(tryInterval); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + break; + } + } + } + } + + Logger.global.logDebug("Unexpected exception trying to load chunk (x:" + x + ", z:" + z + "):" + loadException); + return MCAChunk.empty(); + } + public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion version, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, BiomeMapper biomeIdMapper) throws IOException { return load(worldFolder, uuid, version, blockIdMapper, blockPropertiesMapper, biomeIdMapper, null, false); } @@ -422,9 +389,7 @@ public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion versio if (name == null) { name = levelData.getString("LevelName") + subDimensionName; } - - int worldHeight = 255; - int seaLevel = 63; + Vector3i spawnPoint = new Vector3i( levelData.getInt("SpawnX"), levelData.getInt("SpawnY"), @@ -435,9 +400,7 @@ public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion versio worldFolder, uuid, version, - name, - worldHeight, - seaLevel, + name, spawnPoint, blockIdMapper, blockPropertiesMapper, @@ -469,22 +432,8 @@ public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion versio public static Vector2i blockToChunk(Vector3i pos) { return new Vector2i( - MCAUtil.blockToChunk(pos.getX()), - MCAUtil.blockToChunk(pos.getZ()) - ); - } - - public static Vector2i blockToRegion(Vector3i pos) { - return new Vector2i( - MCAUtil.blockToRegion(pos.getX()), - MCAUtil.blockToRegion(pos.getZ()) - ); - } - - public static Vector2i chunkToRegion(Vector2i pos) { - return new Vector2i( - MCAUtil.chunkToRegion(pos.getX()), - MCAUtil.chunkToRegion(pos.getY()) + pos.getX() >> 4, + pos.getZ() >> 4 ); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/StaticRenderSettings.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/StaticRenderSettings.java deleted file mode 100644 index 62e7d2f0..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/StaticRenderSettings.java +++ /dev/null @@ -1,67 +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.core.render; - -import com.flowpowered.math.vector.Vector3i; - -public class StaticRenderSettings implements RenderSettings { - - private boolean excludeFacesWithoutSunlight; - private Vector3i min, max; - private boolean renderEdges; - - public StaticRenderSettings( - boolean excludeFacesWithoutSunlight, - Vector3i min, - Vector3i max, - boolean renderEdges - ) { - this.excludeFacesWithoutSunlight = excludeFacesWithoutSunlight; - this.min = min; - this.max = max; - this.renderEdges = renderEdges; - } - - @Override - public boolean isExcludeFacesWithoutSunlight() { - return excludeFacesWithoutSunlight; - } - - @Override - public Vector3i getMin() { - return min; - } - - @Override - public Vector3i getMax() { - return max; - } - - @Override - public boolean isRenderEdges() { - return renderEdges; - } - -} \ No newline at end of file diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/TileRenderer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/TileRenderer.java deleted file mode 100644 index 4e876042..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/TileRenderer.java +++ /dev/null @@ -1,68 +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.core.render; - -import de.bluecolored.bluemap.core.render.hires.HiresModel; -import de.bluecolored.bluemap.core.render.hires.HiresModelManager; -import de.bluecolored.bluemap.core.render.lowres.LowresModelManager; -import de.bluecolored.bluemap.core.util.AABB; - -public class TileRenderer { - private HiresModelManager hiresModelManager; - private LowresModelManager lowresModelManager; - - public TileRenderer(HiresModelManager hiresModelManager, LowresModelManager lowresModelManager) { - this.hiresModelManager = hiresModelManager; - this.lowresModelManager = lowresModelManager; - } - - /** - * Renders the provided WorldTile (only) if the world is generated - */ - public void render(WorldTile tile) { - //check if the region is generated before rendering, don't render if it's not generated - AABB area = hiresModelManager.getTileRegion(tile); - if (!tile.getWorld().isAreaGenerated(area)) return; - - HiresModel hiresModel = hiresModelManager.render(tile); - lowresModelManager.render(hiresModel); - } - - /** - * Saves changes to disk - */ - public void save(){ - lowresModelManager.save(); - } - - public HiresModelManager getHiresModelManager() { - return hiresModelManager; - } - - public LowresModelManager getLowresModelManager() { - return lowresModelManager; - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/WorldTile.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/WorldTile.java deleted file mode 100644 index 3393a79c..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/render/WorldTile.java +++ /dev/null @@ -1,69 +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.core.render; - -import java.util.Objects; - -import com.flowpowered.math.vector.Vector2i; - -import de.bluecolored.bluemap.core.world.World; - -public class WorldTile { - - private World world; - private Vector2i tile; - - private int hash; - - public WorldTile(World world, Vector2i tile) { - this.world = world; - this.tile = tile; - - this.hash = Objects.hash(world.getUUID(), tile); - } - - public World getWorld() { - return world; - } - - public Vector2i getTile() { - return tile; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof WorldTile)) return false; - WorldTile that = (WorldTile) obj; - - if (!this.world.getUUID().equals(that.world.getUUID())) return false; - return this.tile.equals(that.tile); - } - - @Override - public int hashCode() { - return hash; - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/AABB.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/AABB.java deleted file mode 100644 index 48e66349..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/AABB.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * This file is part of SpongeAPI, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * 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.util; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.flowpowered.math.GenericMath; -import com.flowpowered.math.vector.Vector3d; -import com.flowpowered.math.vector.Vector3i; - -/** - * An axis aligned bounding box. That is, an un-rotated cuboid. - * It is represented by its minimum and maximum corners. - * - * Using integers, the box has a minimum size of 1 in each direction. - * - *

This class is immutable, all objects returned are either new instances or - * itself.

- */ -public class AABB { - - private final Vector3i min; - private final Vector3i max; - private Vector3i size = null; - private Vector3d center = null; - - /** - * Constructs a new bounding box from two opposite corners. - * Fails the resulting box would be degenerate (a dimension is 0). - * - * @param x1 The first corner x coordinate - * @param y1 The first corner y coordinate - * @param z1 The first corner z coordinate - * @param x2 The second corner x coordinate - * @param y2 The second corner y coordinate - * @param z2 The second corner z coordinate - */ - public AABB(int x1, int y1, int z1, int x2, int y2, int z2) { - this(new Vector3i(x1, y1, z1), new Vector3i(x2, y2, z2)); - } - - /** - * Constructs a new bounding box from two opposite corners. - * Fails the resulting box would be degenerate (a dimension is 0). - * - * @param firstCorner The first corner - * @param secondCorner The second corner - */ - public AABB(Vector3i firstCorner, Vector3i secondCorner) { - checkNotNull(firstCorner, "firstCorner"); - checkNotNull(secondCorner, "secondCorner"); - this.min = firstCorner.min(secondCorner); - this.max = firstCorner.max(secondCorner); - } - - /** - * The minimum corner of the box. - * - * @return The minimum corner - */ - public Vector3i getMin() { - return this.min; - } - - /** - * The maximum corner of the box. - * - * @return The maximum corner - */ - public Vector3i getMax() { - return this.max; - } - - /** - * Returns the center of the box, halfway between each corner. - * - * @return The center - */ - public Vector3d getCenter() { - if (this.center == null) { - this.center = this.min.toDouble().add(getSize().toDouble().div(2)); - } - return this.center; - } - - /** - * Gets the size of the box. - * - * @return The size - */ - public Vector3i getSize() { - if (this.size == null) { - this.size = this.max.sub(this.min).add(Vector3i.ONE); - } - return this.size; - } - - /** - * Checks if the bounding box contains a point. - * - * @param point The point to check - * @return Whether or not the box contains the point - */ - public boolean contains(Vector3i point) { - checkNotNull(point, "point"); - return contains(point.getX(), point.getY(), point.getZ()); - } - - /** - * Checks if the bounding box contains a point. - * - * @param point The point to check - * @return Whether or not the box contains the point - */ - public boolean contains(Vector3d point) { - checkNotNull(point, "point"); - return contains(point.getX(), point.getY(), point.getZ()); - } - - /** - * Checks if the bounding box contains a point. - * - * @param x The x coordinate of the point - * @param y The y coordinate of the point - * @param z The z coordinate of the point - * @return Whether or not the box contains the point - */ - public boolean contains(double x, double y, double z) { - return contains(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); - } - - /** - * Checks if the bounding box contains a point. - * - * @param x The x coordinate of the point - * @param y The y coordinate of the point - * @param z The z coordinate of the point - * @return Whether or not the box contains the point - */ - public boolean contains(int x, int y, int z) { - return this.min.getX() <= x && this.max.getX() >= x - && this.min.getY() <= y && this.max.getY() >= y - && this.min.getZ() <= z && this.max.getZ() >= z; - } - - /** - * Checks if the bounding box intersects another. - * - * @param other The other bounding box to check - * @return Whether this bounding box intersects with the other - */ - public boolean intersects(AABB other) { - checkNotNull(other, "other"); - return this.max.getX() >= other.getMin().getX() && other.getMax().getX() >= this.min.getX() - && this.max.getY() >= other.getMin().getY() && other.getMax().getY() >= this.min.getY() - && this.max.getZ() >= other.getMin().getZ() && other.getMax().getZ() >= this.min.getZ(); - } - - /** - * Offsets this bounding box by a given amount and returns a new box. - * - * @param offset The offset to apply - * @return The new offset box - */ - public AABB offset(Vector3i offset) { - checkNotNull(offset, "offset"); - return offset(offset.getX(), offset.getY(), offset.getZ()); - } - - /** - * Offsets this bounding box by a given amount and returns a new box. - * - * @param x The amount of offset for the x coordinate - * @param y The amount of offset for the y coordinate - * @param z The amount of offset for the z coordinate - * @return The new offset box - */ - public AABB offset(int x, int y, int z) { - return new AABB(this.min.add(x, y, z), this.max.add(x, y, z)); - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof AABB)) { - return false; - } - final AABB aabb = (AABB) other; - return this.min.equals(aabb.min) && this.max.equals(aabb.max); - - } - - @Override - public int hashCode() { - int result = this.min.hashCode(); - result = 31 * result + this.max.hashCode(); - return result; - } - - @Override - public String toString() { - return "AABB(" + this.min + " to " + this.max + ")"; - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpConnection.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpConnection.java index eeca2ad4..fc1b3b5f 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpConnection.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpConnection.java @@ -24,36 +24,39 @@ */ package de.bluecolored.bluemap.core.webserver; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import de.bluecolored.bluemap.core.logger.Logger; + +import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; -import java.util.concurrent.TimeUnit; - -import de.bluecolored.bluemap.core.logger.Logger; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; public class HttpConnection implements Runnable { - private HttpRequestHandler handler; + private final HttpRequestHandler handler; - private ServerSocket server; - private Socket connection; - private InputStream in; - private OutputStream out; + private final ServerSocket server; + private final Socket connection; + private final InputStream in; + private final OutputStream out; - private final boolean verbose; + private final Semaphore processingSemaphore; - public HttpConnection(ServerSocket server, Socket connection, HttpRequestHandler handler, int timeout, TimeUnit timeoutUnit, boolean verbose) throws IOException { + private final boolean verbose; + + public HttpConnection(ServerSocket server, Socket connection, HttpRequestHandler handler, Semaphore processingSemaphore, int timeout, TimeUnit timeoutUnit, boolean verbose) throws IOException { this.server = server; this.connection = connection; this.handler = handler; - this.verbose = verbose; + this.verbose = verbose; + + this.processingSemaphore = processingSemaphore; if (isClosed()){ throw new IOException("Socket already closed!"); @@ -61,8 +64,8 @@ public HttpConnection(ServerSocket server, Socket connection, HttpRequestHandler connection.setSoTimeout((int) timeoutUnit.toMillis(timeout)); - in = this.connection.getInputStream(); - out = this.connection.getOutputStream(); + in = new BufferedInputStream(this.connection.getInputStream()); + out = new BufferedOutputStream(this.connection.getOutputStream()); } @Override @@ -70,25 +73,30 @@ public void run() { while (!isClosed() && !server.isClosed()){ try { HttpRequest request = acceptRequest(); - HttpResponse response = handler.handle(request); - sendResponse(response); - if (verbose) { - log(request, response); + + try { + processingSemaphore.acquire(); + + HttpResponse response = handler.handle(request); + sendResponse(response); + + if (verbose) log(request, response); + } finally { + processingSemaphore.release(); } + } catch (InvalidRequestException e){ try { sendResponse(new HttpResponse(HttpStatusCode.BAD_REQUEST)); - } catch (IOException e1) {} + } catch (IOException ignored) {} break; - } catch (SocketTimeoutException e) { - break; - } catch (SocketException e){ - break; - } catch (ConnectionClosedException e){ + } catch (SocketTimeoutException | ConnectionClosedException | SocketException e) { break; } catch (IOException e) { Logger.global.logError("Unexpected error while processing a HttpRequest!", e); break; + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); } } 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 30df3462..7b3386aa 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 @@ -24,33 +24,22 @@ */ package de.bluecolored.bluemap.core.webserver; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import de.bluecolored.bluemap.core.webserver.HttpConnection.ConnectionClosedException; import de.bluecolored.bluemap.core.webserver.HttpConnection.InvalidRequestException; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class HttpRequest { private static final Pattern REQUEST_PATTERN = Pattern.compile("^(\\w+) (\\S+) (.+)$"); private String method; - private String adress; + private String address; private String version; private Map> header; private Map> headerLC; @@ -60,9 +49,9 @@ public class HttpRequest { private Map getParams = null; private String getParamString = null; - public HttpRequest(String method, String adress, String version, Map> header) { + public HttpRequest(String method, String address, String version, Map> header) { this.method = method; - this.adress = adress; + this.address = address; this.version = version; this.header = header; this.headerLC = new HashMap<>(); @@ -83,8 +72,8 @@ public String getMethod() { return method; } - public String getAdress(){ - return adress; + public String getAddress(){ + return address; } public String getVersion() { @@ -127,7 +116,7 @@ public String getGETParamString() { } private void parseAdress() { - String adress = this.adress; + String adress = this.address; if (adress.isEmpty()) adress = "/"; String[] adressParts = adress.split("\\?", 2); String path = adressParts[0]; @@ -167,8 +156,8 @@ public static HttpRequest read(InputStream in) throws IOException, InvalidReques String method = m.group(1); if (method == null) throw new InvalidRequestException(); - String adress = m.group(2); - if (adress == null) throw new InvalidRequestException(); + String address = m.group(2); + if (address == null) throw new InvalidRequestException(); String version = m.group(3); if (version == null) throw new InvalidRequestException(); @@ -192,7 +181,7 @@ public static HttpRequest read(InputStream in) throws IOException, InvalidReques headerMap.put(kv[0].trim(), values); } - HttpRequest request = new HttpRequest(method, adress, version, headerMap); + HttpRequest request = new HttpRequest(method, address, version, headerMap); if (request.getLowercaseHeader("Transfer-Encoding").contains("chunked")){ try { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpResponse.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpResponse.java index 2442ce71..969e3792 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpResponse.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/HttpResponse.java @@ -24,22 +24,13 @@ */ package de.bluecolored.bluemap.core.webserver; -import java.io.ByteArrayInputStream; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - import org.apache.commons.lang3.StringUtils; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.Map.Entry; + public class HttpResponse implements Closeable { private String version; @@ -57,22 +48,12 @@ public HttpResponse(HttpStatusCode statusCode) { } public void addHeader(String key, String value){ - Set valueSet = header.get(key); - if (valueSet == null){ - valueSet = new HashSet<>(); - header.put(key, valueSet); - } - + Set valueSet = header.computeIfAbsent(key, k -> new HashSet<>()); valueSet.add(value); } public void removeHeader(String key, String value){ - Set valueSet = header.get(key); - if (valueSet == null){ - valueSet = new HashSet<>(); - header.put(key, valueSet); - } - + Set valueSet = header.computeIfAbsent(key, k -> new HashSet<>()); valueSet.remove(value); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/WebServer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/WebServer.java index 6940eae5..4ee2f3b2 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/WebServer.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/webserver/WebServer.java @@ -41,6 +41,7 @@ public class WebServer extends Thread { private final boolean verbose; private final HttpRequestHandler handler; + private final Semaphore processingSemaphore; private ThreadPoolExecutor connectionThreads; @@ -57,6 +58,7 @@ public WebServer(InetAddress bindAddress, int port, int maxConnections, HttpRequ this.verbose = verbose; this.handler = handler; + this.processingSemaphore = new Semaphore(18); connectionThreads = null; } @@ -90,7 +92,7 @@ public void run(){ Socket connection = server.accept(); try { - connectionThreads.execute(new HttpConnection(server, connection, handler, 10, TimeUnit.SECONDS, verbose)); + connectionThreads.execute(new HttpConnection(server, connection, handler, processingSemaphore, 10, TimeUnit.SECONDS, verbose)); } catch (RejectedExecutionException e){ connection.close(); Logger.global.logWarning("Dropped an incoming HttpConnection! (Too many connections?)"); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java index ac829d51..9a525584 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java @@ -42,7 +42,7 @@ */ public class BlockState { - private static Pattern BLOCKSTATE_SERIALIZATION_PATTERN = Pattern.compile("^(.+?)(?:\\[(.*)\\])?$"); + private static final Pattern BLOCKSTATE_SERIALIZATION_PATTERN = Pattern.compile("^(.+?)(?:\\[(.*)\\])?$"); public static final BlockState AIR = new BlockState("minecraft:air", Collections.emptyMap()); public static final BlockState MISSING = new BlockState("bluemap:missing", Collections.emptyMap()); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java new file mode 100644 index 00000000..4112c5ac --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java @@ -0,0 +1,7 @@ +package de.bluecolored.bluemap.core.world; + +public interface Chunk { + + boolean isGenerated(); + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Grid.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Grid.java new file mode 100644 index 00000000..2c8f173b --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Grid.java @@ -0,0 +1,108 @@ +package de.bluecolored.bluemap.core.world; + +import com.flowpowered.math.vector.Vector2i; + +import java.util.Objects; + +public class Grid { + + public static final Grid UNIT = new Grid(Vector2i.ONE); + + private final Vector2i gridSize; + private final Vector2i offset; + + public Grid(int gridSize) { + this(gridSize, 0); + } + + public Grid(int gridSize, int offset) { + this(new Vector2i(gridSize, gridSize), new Vector2i(offset, offset)); + } + + public Grid(Vector2i gridSize) { + this(gridSize, Vector2i.ZERO); + } + + public Grid(Vector2i gridSize, Vector2i offset) { + Objects.requireNonNull(gridSize); + Objects.requireNonNull(offset); + + gridSize = gridSize.max(1,1); + + this.gridSize = gridSize; + this.offset = offset; + } + + public Vector2i getGridSize() { + return gridSize; + } + + public Vector2i getOffset() { + return offset; + } + + public Vector2i getCell(Vector2i pos) { + return new Vector2i( + Math.floorDiv(pos.getX() - offset.getX(), gridSize.getX()), + Math.floorDiv(pos.getY() - offset.getY(), gridSize.getY()) + ); + } + + public Vector2i getCellMin(Vector2i cell) { + return new Vector2i( + cell.getX() * gridSize.getX() + offset.getX(), + cell.getY() * gridSize.getY() + offset.getY() + ); + } + + public Vector2i getCellMax(Vector2i cell) { + return new Vector2i( + (cell.getX() + 1) * gridSize.getX() + offset.getX() - 1, + (cell.getY() + 1) * gridSize.getY() + offset.getY() - 1 + ); + } + + public Vector2i getCellMin(Vector2i cell, Grid targetGrid) { + return targetGrid.getCell(getCellMin(cell)); + } + + public Vector2i getCellMax(Vector2i cell, Grid targetGrid) { + return targetGrid.getCell(getCellMax(cell)); + } + + public Grid multiply(Grid other) { + return new Grid( + this.gridSize.mul(other.gridSize), + this.offset.mul(other.gridSize).add(other.offset) + ); + } + + public Grid divide(Grid other) { + return new Grid( + this.gridSize.div(other.gridSize), + this.offset.sub(other.offset).div(other.gridSize) + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Grid grid = (Grid) o; + return gridSize.equals(grid.gridSize) && offset.equals(grid.offset); + } + + @Override + public int hashCode() { + return Objects.hash(gridSize, offset); + } + + @Override + public String toString() { + return "Grid{" + + "gridSize=" + gridSize + + ", offset=" + offset + + '}'; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Region.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Region.java new file mode 100644 index 00000000..5a6ab65a --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Region.java @@ -0,0 +1,33 @@ +package de.bluecolored.bluemap.core.world; + +import com.flowpowered.math.vector.Vector2i; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; + +public interface Region { + + /** + * Returns a collection of all generated chunks.
+ * (Be aware that the collection is not cached and recollected each time from the world-files!) + */ + default Collection listChunks(){ + return listChunks(0); + } + + /** + * Returns a collection of all chunks that have been modified at or after the specified timestamp.
+ * (Be aware that the collection is not cached and recollected each time from the world-files!) + */ + Collection listChunks(long modifiedSince); + + default Chunk loadChunk(int chunkX, int chunkZ) throws IOException { + return loadChunk(chunkX, chunkZ, false); + } + + Chunk loadChunk(int chunkX, int chunkZ, boolean ignoreMissingLightData) throws IOException; + + File getRegionFile(); + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/SlicedWorld.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/SlicedWorld.java index cb41ba36..97a58231 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/SlicedWorld.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/SlicedWorld.java @@ -24,24 +24,22 @@ */ package de.bluecolored.bluemap.core.world; +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; + import java.nio.file.Path; import java.util.Collection; import java.util.UUID; import java.util.function.Predicate; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; - -import de.bluecolored.bluemap.core.util.AABB; - /** * A sliced view of a world. Everything outside the defined bounds is seen as "not generated" and "air". */ public class SlicedWorld implements World { - private World world; - private Vector3i min; - private Vector3i max; + private final World world; + private final Vector3i min; + private final Vector3i max; public SlicedWorld(World world, Vector3i min, Vector3i max) { this.world = world; @@ -83,7 +81,17 @@ public int getMaxY() { public int getMinY() { return world.getMinY(); } - + + @Override + public Grid getChunkGrid() { + return world.getChunkGrid(); + } + + @Override + public Grid getRegionGrid() { + return world.getRegionGrid(); + } + @Override public Biome getBiome(Vector3i pos) { return world.getBiome(pos); @@ -106,45 +114,6 @@ public Block getBlock(int x, int y, int z) { block.setWorld(this); return block; } - - @Override - public Collection getChunkList(long modifiedSince, Predicate filter) { - return world.getChunkList(modifiedSince, filter.and(chunk -> isInside(chunk))); - } - - @Override - public boolean isChunkGenerated(Vector2i chunkPos) { - if (!isInside(chunkPos)) return false; - - return world.isChunkGenerated(chunkPos); - } - - @Override - public boolean isAreaGenerated(AABB area) { - return isAreaGenerated(area.getMin(), area.getMax()); - } - - @Override - public boolean isAreaGenerated(Vector3i blockMin, Vector3i blockMax) { - return isAreaGenerated(blockPosToChunkPos(blockMin), blockPosToChunkPos(blockMax)); - } - - @Override - public boolean isAreaGenerated(Vector2i chunkMin, Vector2i chunkMax) { - if (!isInside(chunkMin) && - !isInside(chunkMax) && - !isInside(new Vector2i(chunkMin.getX(), chunkMax.getY())) && - !isInside(new Vector2i(chunkMax.getX(), chunkMin.getY())) - ) return false; - - for (int x = chunkMin.getX(); x <= chunkMax.getX(); x++) { - for (int z = chunkMin.getY(); z <= chunkMax.getY(); z++) { - if (!world.isChunkGenerated(new Vector2i(x, z))) return false; - } - } - - return true; - } @Override public void invalidateChunkCache() { @@ -152,8 +121,8 @@ public void invalidateChunkCache() { } @Override - public void invalidateChunkCache(Vector2i chunk) { - world.invalidateChunkCache(chunk); + public void invalidateChunkCache(int x, int z) { + world.invalidateChunkCache(x, z); } @Override @@ -161,11 +130,6 @@ public void cleanUpChunkCache() { world.cleanUpChunkCache(); } - @Override - public Vector2i blockPosToChunkPos(Vector3i block) { - return world.blockPosToChunkPos(block); - } - private boolean isInside(Vector3i blockPos) { return isInside(blockPos.getX(), blockPos.getY(), blockPos.getZ()); } @@ -180,19 +144,6 @@ private boolean isInside(int x, int y, int z) { y <= max.getY(); } - private boolean isInside(Vector2i chunkPos) { - return isInside(chunkPos.getX(), chunkPos.getY()); - } - - private boolean isInside(int chunkX, int chunkZ) { - return - chunkX * 16 <= max.getX() && - chunkX * 16 + 15 >= min.getX() && - chunkZ * 16 <= max.getZ() && - chunkZ * 16 + 15 >= min.getZ(); - - } - private Block createAirBlock(Vector3i pos) { return new Block( this, diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java index 06080a63..aab6a5be 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java @@ -24,17 +24,14 @@ */ package de.bluecolored.bluemap.core.world; -import java.io.IOException; +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; + import java.nio.file.Path; import java.util.Collection; import java.util.UUID; import java.util.function.Predicate; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; - -import de.bluecolored.bluemap.core.util.AABB; - /** * Represents a World on the Server
*
@@ -52,123 +49,60 @@ public interface World { Vector3i getSpawnPoint(); - default int getMaxY() { - return 255; - } + int getMaxY(); - default int getMinY() { - return 0; - } + int getMinY(); + + Grid getChunkGrid(); + + Grid getRegionGrid(); /** - * Returns the Biome on the specified position or the default biome if the block is not generated yet. + * Returns the {@link Biome} on the specified position or the default biome if the block is not generated yet. */ - Biome getBiome(Vector3i pos); + Biome getBiome(int x, int y, int z); /** - * Returns the Block on the specified position or an air-block if the block is not generated yet. + * Returns the {@link Block} on the specified position or an air-block if the block is not generated yet. */ Block getBlock(Vector3i pos); /** - * Returns the Block on the specified position or an air-block if the block is not generated yet. + * Returns the {@link Block} on the specified position or an air-block if the block is not generated yet. */ default Block getBlock(int x, int y, int z) { return getBlock(new Vector3i(x, y, z)); } - - /** - * Returns a collection of all generated chunks.
- * (Be aware that the collection is not cached and recollected each time from the world-files!) - */ - public default Collection getChunkList(){ - return getChunkList(0, c -> true); - } - - /** - * Returns a filtered collection of all generated chunks.
- * (Be aware that the collection is not cached and recollected each time from the world-files!) - */ - public default Collection getChunkList(Predicate filter){ - return getChunkList(0, filter); - } /** - * Returns a collection of all chunks that have been modified at or after the specified timestamp.
- * (Be aware that the collection is not cached and recollected each time from the world-files!) + * Returns the {@link Chunk} on the specified chunk-position */ - public default Collection getChunkList(long modifiedSince){ - return getChunkList(modifiedSince, c -> true); - } - - /** - * Returns a filtered collection of all chunks that have been modified at or after the specified timestamp.
- * (Be aware that the collection is not cached and recollected each time from the world-files!) - */ - public Collection getChunkList(long modifiedSince, Predicate filter); + Chunk getChunk(int x, int z); /** - * Returns true if and only if that chunk is fully generated and no world-generation or lighting has yet to be done. + * Returns the Chunk on the specified chunk-position */ - public boolean isChunkGenerated(Vector2i chunkPos); - - + Region getRegion(int x, int z); + /** - * Returns true if and only if all chunks the given area is intersecting are fully generated and no world-generation or lighting has yet to be done. - * @param area The area to check - * @throws IOException + * Returns a collection of all regions in this world. + * (Be aware that the collection is not cached and recollected each time from the world-files!) */ - public default boolean isAreaGenerated(AABB area) { - return isAreaGenerated(area.getMin(), area.getMax()); - } - - /** - * Returns true if and only if all chunks the given area is intersecting are fully generated and no world-generation or lighting has yet to be done. - * @param area The area to check - * @throws IOException - */ - public default boolean isAreaGenerated(Vector3i blockMin, Vector3i blockMax) { - return isAreaGenerated(blockPosToChunkPos(blockMin), blockPosToChunkPos(blockMax)); - } - - /** - * Returns true if and only if all chunks in the given range are fully generated and no world-generation or lighting has yet to be done. - * @param area The area to check - * @throws IOException - */ - public default boolean isAreaGenerated(Vector2i chunkMin, Vector2i chunkMax) { - for (int x = chunkMin.getX(); x <= chunkMax.getX(); x++) { - for (int z = chunkMin.getY(); z <= chunkMax.getY(); z++) { - if (!isChunkGenerated(new Vector2i(x, z))) return false; - } - } - - return true; - } - + Collection listRegions(); + /** * Invalidates the complete chunk cache (if there is a cache), so that every chunk has to be reloaded from disk */ - public void invalidateChunkCache(); + void invalidateChunkCache(); /** * Invalidates the chunk from the chunk-cache (if there is a cache), so that the chunk has to be reloaded from disk */ - public void invalidateChunkCache(Vector2i chunk); + void invalidateChunkCache(int x, int z); /** * Cleans up invalid cache-entries to free up memory */ - public void cleanUpChunkCache(); - - /** - * Returns the ChunkPosition for a BlockPosition - */ - public default Vector2i blockPosToChunkPos(Vector3i block) { - return new Vector2i( - block.getX() >> 4, - block.getZ() >> 4 - ); - } + void cleanUpChunkCache(); } diff --git a/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/BlockStateTest.java b/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/BlockStateTest.java index e1232223..9a2d8b1a 100644 --- a/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/BlockStateTest.java +++ b/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/BlockStateTest.java @@ -24,10 +24,10 @@ */ package de.bluecolored.bluemap.core.world; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BlockStateTest { diff --git a/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/GridTest.java b/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/GridTest.java new file mode 100644 index 00000000..76deedce --- /dev/null +++ b/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/GridTest.java @@ -0,0 +1,112 @@ +package de.bluecolored.bluemap.core.world; + +import com.flowpowered.math.vector.Vector2i; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GridTest { + + @Test + public void testGetCell() { + Grid grid = new Grid(16, 0); + assertEquals(new Vector2i(0, 0), grid.getCell(new Vector2i(0, 0))); + assertEquals(new Vector2i(0, 0), grid.getCell(new Vector2i(15, 2))); + assertEquals(new Vector2i(1, 1), grid.getCell(new Vector2i(16, 20))); + assertEquals(new Vector2i(-1, -1), grid.getCell(new Vector2i(-1, -16))); + + Grid grid2 = new Grid(16,2); + assertEquals(new Vector2i(-1, -1), grid2.getCell(new Vector2i(0, 0))); + assertEquals(new Vector2i(0, 0), grid2.getCell(new Vector2i(17, 2))); + } + + @Test + public void testCellMin() { + Grid grid = new Grid(16, 0); + assertEquals(new Vector2i(0, 0), grid.getCellMin(new Vector2i(0, 0))); + assertEquals(new Vector2i(16, 32), grid.getCellMin(new Vector2i(1, 2))); + assertEquals(new Vector2i(-32, -16), grid.getCellMin(new Vector2i(-2, -1))); + + Grid grid2 = new Grid(16, 2); + assertEquals(new Vector2i(2, 2), grid2.getCellMin(new Vector2i(0, 0))); + assertEquals(new Vector2i(18, 34), grid2.getCellMin(new Vector2i(1, 2))); + assertEquals(new Vector2i(-30, -14), grid2.getCellMin(new Vector2i(-2, -1))); + } + + @Test + public void testCellMax() { + Grid grid = new Grid(16, 0); + assertEquals(new Vector2i(15, 15), grid.getCellMax(new Vector2i(0, 0))); + assertEquals(new Vector2i(31, 47), grid.getCellMax(new Vector2i(1, 2))); + assertEquals(new Vector2i(-17, -1), grid.getCellMax(new Vector2i(-2, -1))); + + Grid grid2 = new Grid(16, 2); + assertEquals(new Vector2i(17, 17), grid2.getCellMax(new Vector2i(0, 0))); + assertEquals(new Vector2i(33, 49), grid2.getCellMax(new Vector2i(1, 2))); + assertEquals(new Vector2i(-15, 1), grid2.getCellMax(new Vector2i(-2, -1))); + } + + @Test + public void testCellMinWithSmallerTargetGrid() { + Grid grid = new Grid(16, 0); + Grid target = new Grid(2, 1); + + assertEquals(new Vector2i(-1, -1), grid.getCellMin(new Vector2i(0, 0), target)); + assertEquals(new Vector2i(-9, 7), grid.getCellMin(new Vector2i(-1, 1), target)); + } + + @Test + public void testCellMinWithBiggerTargetGrid() { + Grid grid = new Grid(2, 0); + Grid target = new Grid(8, 2); + + assertEquals(new Vector2i(-1, -1), grid.getCellMin(new Vector2i(0, 0), target)); + assertEquals(new Vector2i(-1, 1), grid.getCellMin(new Vector2i(-1, 8), target)); + assertEquals(new Vector2i(-1, 2), grid.getCellMin(new Vector2i(-1, 9), target)); + } + + @Test + public void testCellMaxWithSmallerTargetGrid() { + Grid grid = new Grid(16, 0); + Grid target = new Grid(2, 1); + + assertEquals(new Vector2i(7, 7), grid.getCellMax(new Vector2i(0, 0), target)); + assertEquals(new Vector2i(-1, 15), grid.getCellMax(new Vector2i(-1, 1), target)); + } + + @Test + public void testCellMaxWithBiggerTargetGrid() { + Grid grid = new Grid(2, 0); + Grid target = new Grid(8, 2); + + assertEquals(new Vector2i(-1, -1), grid.getCellMax(new Vector2i(0, 0), target)); + assertEquals(new Vector2i(-1, 1), grid.getCellMax(new Vector2i(-1, 8), target)); + assertEquals(new Vector2i(-1, 2), grid.getCellMax(new Vector2i(-1, 9), target)); + } + + @Test + public void testMultiply() { + Grid grid1 = new Grid(2, 5); + Grid grid2 = new Grid(4, 2); + + Grid result1 = new Grid(8, 22); + Grid result2 = new Grid(8, 9); + + assertEquals(result1, grid1.multiply(grid2)); + assertEquals(result2, grid2.multiply(grid1)); + } + + @Test + public void testDivide() { + Grid grid1 = new Grid(8, 22); + Grid grid2 = new Grid(4, 2); + Grid result1 = new Grid(2, 5); + assertEquals(result1, grid1.divide(grid2)); + + Grid grid3 = new Grid(8, 9); + Grid grid4 = new Grid(2, 5); + Grid result2 = new Grid(4, 2); + assertEquals(result2, grid3.divide(grid4)); + } + +} \ No newline at end of file diff --git a/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java b/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java index afd4dbac..3459aedd 100644 --- a/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java +++ b/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java @@ -24,29 +24,24 @@ */ package de.bluecolored.bluemap.cli; -import com.flowpowered.math.GenericMath; -import com.flowpowered.math.vector.Vector2i; -import de.bluecolored.bluemap.common.*; +import de.bluecolored.bluemap.common.BlueMapService; +import de.bluecolored.bluemap.common.MissingResourcesException; +import de.bluecolored.bluemap.common.rendermanager.RenderManager; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.config.WebServerConfig; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.LoggerLogger; import de.bluecolored.bluemap.core.metrics.Metrics; -import de.bluecolored.bluemap.core.render.hires.HiresModelManager; import de.bluecolored.bluemap.core.util.FileUtils; -import de.bluecolored.bluemap.core.web.FileRequestHandler; -import de.bluecolored.bluemap.core.web.WebSettings; +import de.bluecolored.bluemap.common.web.FileRequestHandler; +import de.bluecolored.bluemap.common.web.WebSettings; import de.bluecolored.bluemap.core.webserver.HttpRequestHandler; import de.bluecolored.bluemap.core.webserver.WebServer; -import de.bluecolored.bluemap.core.world.World; import org.apache.commons.cli.*; -import org.apache.commons.lang3.time.DurationFormatUtils; -import java.io.*; -import java.util.Collection; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; +import java.io.File; +import java.io.IOException; public class BlueMapCLI { @@ -58,162 +53,9 @@ public void renderMaps(BlueMapService blueMap, boolean forceRender, boolean forc blueMap.createOrUpdateWebApp(forceGenerateWebapp); WebSettings webSettings = blueMap.updateWebAppSettings(); - RenderManager renderManager = new RenderManager(blueMap.getCoreConfig().getRenderThreadCount()); - File rmstate = new File(blueMap.getCoreConfig().getDataFolder(), "rmstate"); - - if (!forceRender && rmstate.exists()) { - try ( - InputStream in = new GZIPInputStream(new FileInputStream(rmstate)); - DataInputStream din = new DataInputStream(in); - ){ - renderManager.readState(din, blueMap.getMaps().values()); - Logger.global.logInfo("Found unfinished render, continuing ... (If you want to start a new render, delete the this file: " + rmstate.getCanonicalPath() + " or force a full render using -f)"); - } catch (IOException ex) { - Logger.global.logError("Failed to read saved render-state! Remove the file " + rmstate.getCanonicalPath() + " to start a new render.", ex); - return; - } - } else { - for (MapType map : blueMap.getMaps().values()) { - Logger.global.logInfo("Creating render-task for map '" + map.getId() + "' ..."); - Logger.global.logInfo("Collecting tiles ..."); - - Collection chunks; - if (!forceRender) { - long lastRender = webSettings.getLong("maps", map.getId(), "last-render"); - chunks = map.getWorld().getChunkList(lastRender); - } else { - chunks = map.getWorld().getChunkList(); - } - - HiresModelManager hiresModelManager = map.getTileRenderer().getHiresModelManager(); - Collection tiles = hiresModelManager.getTilesForChunks(chunks); - Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)"); - if (!forceRender && chunks.size() == 0) { - Logger.global.logInfo("(This is normal if nothing has changed in the world since the last render. Use -f on the command-line to force a render of all chunks)"); - } - - if (tiles.isEmpty()) { - continue; - } + RenderManager renderManager = new RenderManager(); - Vector2i renderCenter = map.getWorld().getSpawnPoint().toVector2(true); - - RenderTask task = new RenderTask(map.getId(), map); - task.addTiles(tiles); - task.optimizeQueue(renderCenter); - - renderManager.addRenderTask(task); - } - } - - Logger.global.logInfo("Starting render ..."); - renderManager.start(); - - Thread shutdownHook = new Thread(() -> { - Logger.global.logInfo("Stopping render ..."); - renderManager.stop(); - - Logger.global.logInfo("Saving tiles ..."); - RenderTask currentTask = renderManager.getCurrentRenderTask(); - if (currentTask != null){ - currentTask.getMapType().getTileRenderer().save(); - } - - try { - Logger.global.logInfo("Saving render-state ..."); - FileUtils.createFile(rmstate); - - try ( - OutputStream os = new GZIPOutputStream(new FileOutputStream(rmstate)); - DataOutputStream dos = new DataOutputStream(os); - ) { - renderManager.writeState(dos); - - Logger.global.logInfo("Render saved and stopped! Restart the render (without using -f) to resume."); - } - } catch (IOException ex) { - Logger.global.logError("Failed to save render-state!", ex); - } - }); - Runtime.getRuntime().addShutdownHook(shutdownHook); - - long startTime = System.currentTimeMillis(); - - long lastLogUpdate = startTime; - long lastSave = startTime; - - while(renderManager.getRenderTaskCount() != 0) { - try { - Thread.sleep(200); - } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } - - - long now = System.currentTimeMillis(); - - if (lastLogUpdate < now - 10000) { // print update all 10 seconds - RenderTask currentTask = renderManager.getCurrentRenderTask(); - if (currentTask == null) continue; - - lastLogUpdate = now; - long time = currentTask.getActiveTime(); - - String durationString = DurationFormatUtils.formatDurationWords(time, true, true); - int tileCount = currentTask.getRemainingTileCount() + currentTask.getRenderedTileCount(); - double pct = (double)currentTask.getRenderedTileCount() / (double) tileCount; - - long ert = (long)((time / pct) * (1d - pct)); - String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); - - double tps = currentTask.getRenderedTileCount() / (time / 1000.0); - - Logger.global.logInfo("Rendering map '" + currentTask.getName() + "':"); - Logger.global.logInfo("Rendered " + currentTask.getRenderedTileCount() + " of " + tileCount + " tiles in " + durationString + " | " + GenericMath.round(tps, 3) + " tiles/s"); - Logger.global.logInfo(GenericMath.round(pct * 100, 3) + "% | Estimated remaining time: " + ertDurationString); - } - - if (lastSave < now - 1 * 60000) { // save every minute - RenderTask currentTask = renderManager.getCurrentRenderTask(); - if (currentTask == null) continue; - - lastSave = now; - currentTask.getMapType().getTileRenderer().save(); - - try ( - OutputStream os = new GZIPOutputStream(new FileOutputStream(rmstate)); - DataOutputStream dos = new DataOutputStream(os); - ){ - renderManager.writeState(dos); - } catch (IOException ex) { - Logger.global.logError("Failed to save render-state!", ex); - } - - //clean up caches - for (World world : blueMap.getWorlds().values()) { - world.cleanUpChunkCache(); - } - } - } - - //render finished and saved, so this is no longer needed - Runtime.getRuntime().removeShutdownHook(shutdownHook); - - //stop render-threads - renderManager.stop(); - - //render finished, so remove render state file - FileUtils.delete(rmstate); - - for (MapType map : blueMap.getMaps().values()) { - webSettings.set(startTime, "maps", map.getId(), "last-render"); - } - - try { - webSettings.save(); - } catch (IOException e) { - Logger.global.logError("Failed to update web-settings!", e); - } - - Logger.global.logInfo("Render finished!"); + //TODO } public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOException { diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index c96846e6..5b5d4077 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -24,20 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.UUID; - -import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - 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; @@ -51,6 +42,11 @@ import net.minecraft.util.math.Direction; import net.minecraft.world.World; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + public class FabricEventForwarder { private FabricMod mod; @@ -59,9 +55,7 @@ public class FabricEventForwarder { public FabricEventForwarder(FabricMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); - - WorldSaveCallback.EVENT.register(this::onWorldSave); - ChunkFinalizeCallback.EVENT.register(this::onChunkFinalize); + AttackBlockCallback.EVENT.register(this::onBlockAttack); UseBlockCallback.EVENT.register(this::onBlockUse); @@ -105,24 +99,6 @@ public synchronized void onBlockChange(ServerWorld world, BlockPos blockPos) { } } - public synchronized void onWorldSave(ServerWorld world) { - try { - UUID uuid = mod.getUUIDForWorld(world); - for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(uuid); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - public synchronized void onChunkFinalize(ServerWorld world, Vector2i chunkPos) { - try { - UUID uuid = mod.getUUIDForWorld(world); - for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(uuid, chunkPos); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) { if (this.mod.getServer() != server) return; diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java deleted file mode 100644 index 49de240d..00000000 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java +++ /dev/null @@ -1,43 +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.fabric.events; - -import com.flowpowered.math.vector.Vector2i; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.world.ServerWorld; - -public interface ChunkFinalizeCallback { - Event EVENT = EventFactory.createArrayBacked(ChunkFinalizeCallback.class, - (listeners) -> (world, chunkPos) -> { - for (ChunkFinalizeCallback event : listeners) { - event.onChunkFinalized(world, chunkPos); - } - } - ); - - void onChunkFinalized(ServerWorld world, Vector2i chunkPos); -} diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java deleted file mode 100644 index 25b2ea9f..00000000 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java +++ /dev/null @@ -1,41 +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.fabric.events; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.world.ServerWorld; - -public interface WorldSaveCallback { - Event EVENT = EventFactory.createArrayBacked(WorldSaveCallback.class, - (listeners) -> (world) -> { - for (WorldSaveCallback event : listeners) { - event.onWorldSaved(world); - } - } - ); - - void onWorldSaved(ServerWorld world); -} diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java deleted file mode 100644 index d2647bfa..00000000 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java +++ /dev/null @@ -1,44 +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.fabric.mixin; - -import org.spongepowered.asm.mixin.Mixin; -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.WorldSaveCallback; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.ProgressListener; - -@Mixin(ServerWorld.class) -public abstract class MixinServerWorld { - - @Inject(at = @At("RETURN"), method = "save") - public void save(ProgressListener progressListener, boolean flush, boolean bl, CallbackInfo ci) { - WorldSaveCallback.EVENT.invoker().onWorldSaved((ServerWorld) (Object) this); - } - -} diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java deleted file mode 100644 index efbb5320..00000000 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java +++ /dev/null @@ -1,58 +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.fabric.mixin; - -import java.util.concurrent.CompletableFuture; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import com.flowpowered.math.vector.Vector2i; -import com.mojang.datafixers.util.Either; - -import de.bluecolored.bluemap.fabric.events.ChunkFinalizeCallback; -import net.minecraft.server.world.ChunkHolder; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.server.world.ThreadedAnvilChunkStorage; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.ChunkStatus; - -@Mixin(ThreadedAnvilChunkStorage.class) -public abstract class MixinThreadedAnvilChunkStorage { - - @Accessor("world") - public abstract ServerWorld getWorld(); - - @Inject(at = @At("RETURN"), method = "method_20617") - public void upgradeChunk(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable>> ci) { - if (requiredStatus == ChunkStatus.FULL) { - ChunkFinalizeCallback.EVENT.invoker().onChunkFinalized(getWorld(), new Vector2i(holder.getPos().x, holder.getPos().z)); - } - } - -} diff --git a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index c96846e6..5b5d4077 100644 --- a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -24,20 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.UUID; - -import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - 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; @@ -51,6 +42,11 @@ import net.minecraft.util.math.Direction; import net.minecraft.world.World; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + public class FabricEventForwarder { private FabricMod mod; @@ -59,9 +55,7 @@ public class FabricEventForwarder { public FabricEventForwarder(FabricMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); - - WorldSaveCallback.EVENT.register(this::onWorldSave); - ChunkFinalizeCallback.EVENT.register(this::onChunkFinalize); + AttackBlockCallback.EVENT.register(this::onBlockAttack); UseBlockCallback.EVENT.register(this::onBlockUse); @@ -105,24 +99,6 @@ public synchronized void onBlockChange(ServerWorld world, BlockPos blockPos) { } } - public synchronized void onWorldSave(ServerWorld world) { - try { - UUID uuid = mod.getUUIDForWorld(world); - for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(uuid); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - public synchronized void onChunkFinalize(ServerWorld world, Vector2i chunkPos) { - try { - UUID uuid = mod.getUUIDForWorld(world); - for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(uuid, chunkPos); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) { if (this.mod.getServer() != server) return; diff --git a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java deleted file mode 100644 index 49de240d..00000000 --- a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java +++ /dev/null @@ -1,43 +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.fabric.events; - -import com.flowpowered.math.vector.Vector2i; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.world.ServerWorld; - -public interface ChunkFinalizeCallback { - Event EVENT = EventFactory.createArrayBacked(ChunkFinalizeCallback.class, - (listeners) -> (world, chunkPos) -> { - for (ChunkFinalizeCallback event : listeners) { - event.onChunkFinalized(world, chunkPos); - } - } - ); - - void onChunkFinalized(ServerWorld world, Vector2i chunkPos); -} diff --git a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java deleted file mode 100644 index 25b2ea9f..00000000 --- a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java +++ /dev/null @@ -1,41 +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.fabric.events; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.world.ServerWorld; - -public interface WorldSaveCallback { - Event EVENT = EventFactory.createArrayBacked(WorldSaveCallback.class, - (listeners) -> (world) -> { - for (WorldSaveCallback event : listeners) { - event.onWorldSaved(world); - } - } - ); - - void onWorldSaved(ServerWorld world); -} diff --git a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java deleted file mode 100644 index d2647bfa..00000000 --- a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java +++ /dev/null @@ -1,44 +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.fabric.mixin; - -import org.spongepowered.asm.mixin.Mixin; -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.WorldSaveCallback; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.ProgressListener; - -@Mixin(ServerWorld.class) -public abstract class MixinServerWorld { - - @Inject(at = @At("RETURN"), method = "save") - public void save(ProgressListener progressListener, boolean flush, boolean bl, CallbackInfo ci) { - WorldSaveCallback.EVENT.invoker().onWorldSaved((ServerWorld) (Object) this); - } - -} diff --git a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java b/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java deleted file mode 100644 index 4ffcef51..00000000 --- a/implementations/fabric-1.16.1/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java +++ /dev/null @@ -1,58 +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.fabric.mixin; - -import java.util.concurrent.CompletableFuture; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import com.flowpowered.math.vector.Vector2i; -import com.mojang.datafixers.util.Either; - -import de.bluecolored.bluemap.fabric.events.ChunkFinalizeCallback; -import net.minecraft.server.world.ChunkHolder; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.server.world.ThreadedAnvilChunkStorage; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.ChunkStatus; - -@Mixin(ThreadedAnvilChunkStorage.class) -public abstract class MixinThreadedAnvilChunkStorage { - - @Accessor("world") - public abstract ServerWorld getWorld(); - - @Inject(at = @At("RETURN"), method = "generateChunk") - public void upgradeChunk(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable>> ci) { - if (requiredStatus == ChunkStatus.FULL) { - ChunkFinalizeCallback.EVENT.invoker().onChunkFinalized(getWorld(), new Vector2i(holder.getPos().x, holder.getPos().z)); - } - } - -} diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index c96846e6..8ecaf6d2 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -34,10 +34,8 @@ 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; @@ -59,9 +57,7 @@ public class FabricEventForwarder { public FabricEventForwarder(FabricMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); - - WorldSaveCallback.EVENT.register(this::onWorldSave); - ChunkFinalizeCallback.EVENT.register(this::onChunkFinalize); + AttackBlockCallback.EVENT.register(this::onBlockAttack); UseBlockCallback.EVENT.register(this::onBlockUse); @@ -105,24 +101,6 @@ public synchronized void onBlockChange(ServerWorld world, BlockPos blockPos) { } } - public synchronized void onWorldSave(ServerWorld world) { - try { - UUID uuid = mod.getUUIDForWorld(world); - for (ServerEventListener listener : eventListeners) listener.onWorldSaveToDisk(uuid); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - public synchronized void onChunkFinalize(ServerWorld world, Vector2i chunkPos) { - try { - UUID uuid = mod.getUUIDForWorld(world); - for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(uuid, chunkPos); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) { if (this.mod.getServer() != server) return; diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java deleted file mode 100644 index 49de240d..00000000 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/events/ChunkFinalizeCallback.java +++ /dev/null @@ -1,43 +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.fabric.events; - -import com.flowpowered.math.vector.Vector2i; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.world.ServerWorld; - -public interface ChunkFinalizeCallback { - Event EVENT = EventFactory.createArrayBacked(ChunkFinalizeCallback.class, - (listeners) -> (world, chunkPos) -> { - for (ChunkFinalizeCallback event : listeners) { - event.onChunkFinalized(world, chunkPos); - } - } - ); - - void onChunkFinalized(ServerWorld world, Vector2i chunkPos); -} diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java deleted file mode 100644 index 25b2ea9f..00000000 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/events/WorldSaveCallback.java +++ /dev/null @@ -1,41 +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.fabric.events; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.world.ServerWorld; - -public interface WorldSaveCallback { - Event EVENT = EventFactory.createArrayBacked(WorldSaveCallback.class, - (listeners) -> (world) -> { - for (WorldSaveCallback event : listeners) { - event.onWorldSaved(world); - } - } - ); - - void onWorldSaved(ServerWorld world); -} diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java deleted file mode 100644 index d2647bfa..00000000 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinServerWorld.java +++ /dev/null @@ -1,44 +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.fabric.mixin; - -import org.spongepowered.asm.mixin.Mixin; -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.WorldSaveCallback; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.ProgressListener; - -@Mixin(ServerWorld.class) -public abstract class MixinServerWorld { - - @Inject(at = @At("RETURN"), method = "save") - public void save(ProgressListener progressListener, boolean flush, boolean bl, CallbackInfo ci) { - WorldSaveCallback.EVENT.invoker().onWorldSaved((ServerWorld) (Object) this); - } - -} diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java deleted file mode 100644 index 24c83dfa..00000000 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/mixin/MixinThreadedAnvilChunkStorage.java +++ /dev/null @@ -1,58 +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.fabric.mixin; - -import java.util.concurrent.CompletableFuture; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import com.flowpowered.math.vector.Vector2i; -import com.mojang.datafixers.util.Either; - -import de.bluecolored.bluemap.fabric.events.ChunkFinalizeCallback; -import net.minecraft.server.world.ChunkHolder; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.server.world.ThreadedAnvilChunkStorage; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.ChunkStatus; - -@Mixin(ThreadedAnvilChunkStorage.class) -public abstract class MixinThreadedAnvilChunkStorage { - - @Accessor("world") - public abstract ServerWorld getWorld(); - - @Inject(at = @At("RETURN"), method = "upgradeChunk") - public void upgradeChunk(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable>> ci) { - if (requiredStatus == ChunkStatus.FULL) { - ChunkFinalizeCallback.EVENT.invoker().onChunkFinalized(getWorld(), new Vector2i(holder.getPos().x, holder.getPos().z)); - } - } - -} diff --git a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java index 1ce6b9fc..82f08dc6 100644 --- a/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java +++ b/implementations/forge-1.14.4/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -24,48 +24,31 @@ */ package de.bluecolored.bluemap.forge; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Deque; -import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedDeque; - -import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.world.World; 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.ChunkDataEvent; -import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + public class ForgeEventForwarder { private ForgeMod mod; private Collection eventListeners; - private Deque loadChunkEvents; - private Thread loadChunkEventProcessor; - public ForgeEventForwarder(ForgeMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); - - loadChunkEvents = new ConcurrentLinkedDeque<>(); MinecraftForge.EVENT_BUS.register(this); - - //see processLoadChunkEvents JavaDoc comment - loadChunkEventProcessor = new Thread(this::processLoadChunkEvents); - loadChunkEventProcessor.setDaemon(true); - loadChunkEventProcessor.start(); } public synchronized void addEventListener(ServerEventListener listener) { @@ -103,34 +86,6 @@ private synchronized void onBlockChange(BlockEvent evt) { Logger.global.noFloodError("Failed to get the UUID for a world!", e); } } - - @SubscribeEvent - public synchronized void onChunkSave(ChunkDataEvent.Save evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - Vector2i chunkPos = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); - - try { - UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); - for (ServerEventListener listener : eventListeners) listener.onChunkSaveToDisk(world, chunkPos); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - /* Use ChunkSaveToDisk as it is the preferred event to use and more reliable on the chunk actually saved to disk - @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) { @@ -143,64 +98,5 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { UUID uuid = evt.getPlayer().getUniqueID(); for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); } - - @SubscribeEvent - public void onChunkLoad(ChunkEvent.Load evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - try { - UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); - Vector2i chunk = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); - - synchronized (loadChunkEvents) { - loadChunkEvents.add(new WorldChunk(world, chunk)); - loadChunkEvents.notify(); - } - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - /** - * This is a workaround for forge not providing a way to detect if chunks are newly generated: - * Each time a chunk-load-event occurs, it is (asynchronously) tested if the chunk is already generated on the world files. - * If it is a new chunk it will likely not be saved to the disk right away. - */ - private void processLoadChunkEvents() { - while (!Thread.interrupted()) { - WorldChunk worldChunk; - if (mod.getPlugin().isLoaded() && (worldChunk = loadChunkEvents.poll()) != null) { - try { - World world = mod.getPlugin().getWorld(worldChunk.world); - if (world == null || world.isChunkGenerated(worldChunk.chunk)) continue; - - for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(worldChunk.world, worldChunk.chunk); - - } catch (RuntimeException e) { - Logger.global.noFloodWarning("processLoadChunkEventsError", "Failed to test if a chunk is newly generated:" + e); - } - } else { - synchronized (loadChunkEvents) { - try { - loadChunkEvents.wait(10000); - } catch (InterruptedException e) { - break; - } - } - } - } - - Thread.currentThread().interrupt(); - } - - private class WorldChunk { - final UUID world; - final Vector2i chunk; - - public WorldChunk(UUID world, Vector2i chunk) { - this.world = world; - this.chunk = chunk; - } - } } diff --git a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java index 1ce6b9fc..82f08dc6 100644 --- a/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java +++ b/implementations/forge-1.15.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -24,48 +24,31 @@ */ package de.bluecolored.bluemap.forge; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Deque; -import java.util.UUID; -import java.util.concurrent.ConcurrentLinkedDeque; - -import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; - import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.world.World; 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.ChunkDataEvent; -import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; + public class ForgeEventForwarder { private ForgeMod mod; private Collection eventListeners; - private Deque loadChunkEvents; - private Thread loadChunkEventProcessor; - public ForgeEventForwarder(ForgeMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); - - loadChunkEvents = new ConcurrentLinkedDeque<>(); MinecraftForge.EVENT_BUS.register(this); - - //see processLoadChunkEvents JavaDoc comment - loadChunkEventProcessor = new Thread(this::processLoadChunkEvents); - loadChunkEventProcessor.setDaemon(true); - loadChunkEventProcessor.start(); } public synchronized void addEventListener(ServerEventListener listener) { @@ -103,34 +86,6 @@ private synchronized void onBlockChange(BlockEvent evt) { Logger.global.noFloodError("Failed to get the UUID for a world!", e); } } - - @SubscribeEvent - public synchronized void onChunkSave(ChunkDataEvent.Save evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - Vector2i chunkPos = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); - - try { - UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); - for (ServerEventListener listener : eventListeners) listener.onChunkSaveToDisk(world, chunkPos); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - /* Use ChunkSaveToDisk as it is the preferred event to use and more reliable on the chunk actually saved to disk - @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) { @@ -143,64 +98,5 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { UUID uuid = evt.getPlayer().getUniqueID(); for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); } - - @SubscribeEvent - public void onChunkLoad(ChunkEvent.Load evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - try { - UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); - Vector2i chunk = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); - - synchronized (loadChunkEvents) { - loadChunkEvents.add(new WorldChunk(world, chunk)); - loadChunkEvents.notify(); - } - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - /** - * This is a workaround for forge not providing a way to detect if chunks are newly generated: - * Each time a chunk-load-event occurs, it is (asynchronously) tested if the chunk is already generated on the world files. - * If it is a new chunk it will likely not be saved to the disk right away. - */ - private void processLoadChunkEvents() { - while (!Thread.interrupted()) { - WorldChunk worldChunk; - if (mod.getPlugin().isLoaded() && (worldChunk = loadChunkEvents.poll()) != null) { - try { - World world = mod.getPlugin().getWorld(worldChunk.world); - if (world == null || world.isChunkGenerated(worldChunk.chunk)) continue; - - for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(worldChunk.world, worldChunk.chunk); - - } catch (RuntimeException e) { - Logger.global.noFloodWarning("processLoadChunkEventsError", "Failed to test if a chunk is newly generated:" + e); - } - } else { - synchronized (loadChunkEvents) { - try { - loadChunkEvents.wait(10000); - } catch (InterruptedException e) { - break; - } - } - } - } - - Thread.currentThread().interrupt(); - } - - private class WorldChunk { - final UUID world; - final Vector2i chunk; - - public WorldChunk(UUID world, Vector2i chunk) { - this.world = world; - this.chunk = chunk; - } - } } diff --git a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java index 2b6aeb23..c2a9d23f 100644 --- a/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java +++ b/implementations/forge-1.16.2/src/main/java/de/bluecolored/bluemap/forge/ForgeEventForwarder.java @@ -51,21 +51,11 @@ public class ForgeEventForwarder { private ForgeMod mod; private Collection eventListeners; - private Deque loadChunkEvents; - private Thread loadChunkEventProcessor; - public ForgeEventForwarder(ForgeMod mod) { this.mod = mod; this.eventListeners = new ArrayList<>(1); - - loadChunkEvents = new ConcurrentLinkedDeque<>(); MinecraftForge.EVENT_BUS.register(this); - - //see processLoadChunkEvents JavaDoc comment - loadChunkEventProcessor = new Thread(this::processLoadChunkEvents); - loadChunkEventProcessor.setDaemon(true); - loadChunkEventProcessor.start(); } public synchronized void addEventListener(ServerEventListener listener) { @@ -104,34 +94,6 @@ private synchronized void onBlockChange(BlockEvent evt) { } } - @SubscribeEvent - public synchronized void onChunkSave(ChunkDataEvent.Save evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - Vector2i chunkPos = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); - - try { - UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); - for (ServerEventListener listener : eventListeners) listener.onChunkSaveToDisk(world, chunkPos); - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - /* Use ChunkSaveToDisk as it is the preferred event to use and more reliable on the chunk actually saved to disk - @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(); @@ -143,64 +105,5 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) { UUID uuid = evt.getPlayer().getUniqueID(); for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid); } - - @SubscribeEvent - public void onChunkLoad(ChunkEvent.Load evt) { - if (!(evt.getWorld() instanceof ServerWorld)) return; - - try { - UUID world = mod.getUUIDForWorld((ServerWorld) evt.getWorld()); - Vector2i chunk = new Vector2i(evt.getChunk().getPos().x, evt.getChunk().getPos().z); - - synchronized (loadChunkEvents) { - loadChunkEvents.add(new WorldChunk(world, chunk)); - loadChunkEvents.notify(); - } - } catch (IOException e) { - Logger.global.noFloodError("Failed to get the UUID for a world!", e); - } - } - - /** - * This is a workaround for forge not providing a way to detect if chunks are newly generated: - * Each time a chunk-load-event occurs, it is (asynchronously) tested if the chunk is already generated on the world files. - * If it is a new chunk it will likely not be saved to the disk right away. - */ - private void processLoadChunkEvents() { - while (!Thread.interrupted()) { - WorldChunk worldChunk; - if (mod.getPlugin().isLoaded() && (worldChunk = loadChunkEvents.poll()) != null) { - try { - World world = mod.getPlugin().getWorld(worldChunk.world); - if (world == null || world.isChunkGenerated(worldChunk.chunk)) continue; - - for (ServerEventListener listener : eventListeners) listener.onChunkFinishedGeneration(worldChunk.world, worldChunk.chunk); - - } catch (RuntimeException e) { - Logger.global.noFloodWarning("processLoadChunkEventsError", "Failed to test if a chunk is newly generated:" + e); - } - } else { - synchronized (loadChunkEvents) { - try { - loadChunkEvents.wait(10000); - } catch (InterruptedException e) { - break; - } - } - } - } - - Thread.currentThread().interrupt(); - } - - private class WorldChunk { - final UUID world; - final Vector2i chunk; - - public WorldChunk(UUID world, Vector2i chunk) { - this.world = world; - this.chunk = chunk; - } - } } diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index 8013011f..452f37ae 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -24,23 +24,13 @@ */ package de.bluecolored.bluemap.bukkit; -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; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import de.bluecolored.bluemap.common.plugin.Plugin; +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.MinecraftVersion; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; import org.bstats.bukkit.MetricsLite; import org.bukkit.Bukkit; import org.bukkit.World; @@ -53,13 +43,16 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.java.JavaPlugin; -import de.bluecolored.bluemap.common.plugin.Plugin; -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.MinecraftVersion; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.resourcepack.ParseResourceException; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listener { diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java index c3a46ca8..c10eb18b 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/EventForwarder.java @@ -24,35 +24,21 @@ */ package de.bluecolored.bluemap.bukkit; -import java.util.ArrayList; -import java.util.Collection; -import java.util.UUID; - -import org.bukkit.Chunk; +import com.flowpowered.math.vector.Vector3i; +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.plugin.text.Text; import org.bukkit.Location; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockBurnEvent; -import org.bukkit.event.block.BlockExplodeEvent; -import org.bukkit.event.block.BlockFadeEvent; -import org.bukkit.event.block.BlockFertilizeEvent; -import org.bukkit.event.block.BlockFormEvent; -import org.bukkit.event.block.BlockGrowEvent; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.block.*; 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.ChunkUnloadEvent; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; - -import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.plugin.text.Text; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; public class EventForwarder implements Listener { @@ -70,19 +56,6 @@ public synchronized void removeAllListeners() { listeners.clear(); } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public synchronized void onChunkSaveToDisk(ChunkUnloadEvent evt) { - Vector2i chunkPos = new Vector2i(evt.getChunk().getX(), evt.getChunk().getZ()); - for (ServerEventListener listener : listeners) listener.onChunkSaveToDisk(evt.getWorld().getUID(), chunkPos); - } - - /* Use ChunkSaveToDisk as it is the preferred event to use and more reliable on the chunk actually saved to disk - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public synchronized void onWorldSaveToDisk(WorldSaveEvent evt) { - for (ServerEventListener listener : listeners) listener.onWorldSaveToDisk(evt.getWorld().getUID()); - } - */ - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockChange(BlockPlaceEvent evt) { onBlockChange(evt.getBlock().getLocation()); @@ -133,14 +106,6 @@ private synchronized void onBlockChange(Location loc) { Vector3i pos = new Vector3i(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); for (ServerEventListener listener : listeners) listener.onBlockChange(world, pos); } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public synchronized void onChunkFinishedGeneration(ChunkPopulateEvent evt) { - Chunk chunk = evt.getChunk(); - UUID world = chunk.getWorld().getUID(); - Vector2i chunkPos = new Vector2i(chunk.getX(), chunk.getZ()); - for (ServerEventListener listener : listeners) listener.onChunkFinishedGeneration(world, chunkPos); - } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public synchronized void onPlayerJoin(PlayerJoinEvent evt) { diff --git a/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java b/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java index eee76e76..b09fd7ee 100644 --- a/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java +++ b/implementations/sponge-7.2.0/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java @@ -24,8 +24,8 @@ */ package de.bluecolored.bluemap.sponge; -import java.util.Optional; - +import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; +import de.bluecolored.bluemap.common.plugin.text.Text; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.data.Transaction; import org.spongepowered.api.event.Listener; @@ -34,15 +34,9 @@ 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.chunk.PopulateChunkEvent; -import org.spongepowered.api.event.world.chunk.SaveChunkEvent; import org.spongepowered.api.world.Location; -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; - -import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.plugin.text.Text; +import java.util.Optional; public class EventForwarder { @@ -51,18 +45,6 @@ public class EventForwarder { public EventForwarder(ServerEventListener listener) { this.listener = listener; } - - /* Use ChunkSaveToDisk as it is the preferred event to use and more reliable on the chunk actually saved to disk - @Listener(order = Order.POST) - public void onWorldSaveToDisk(SaveWorldEvent evt) { - listener.onWorldSaveToDisk(evt.getTargetWorld().getUniqueId()); - } - */ - - @Listener(order = Order.POST) - public void onChunkSaveToDisk(SaveChunkEvent.Pre evt) { - listener.onChunkSaveToDisk(evt.getTargetChunk().getWorld().getUniqueId(), evt.getTargetChunk().getPosition().toVector2(true)); - } @Listener(order = Order.POST) @Exclude({ChangeBlockEvent.Post.class, ChangeBlockEvent.Pre.class, ChangeBlockEvent.Modify.class}) @@ -71,18 +53,10 @@ public void onBlockChange(ChangeBlockEvent evt) { if(!tr.isValid()) continue; Optional> ow = tr.getFinal().getLocation(); - if (ow.isPresent()) { - listener.onBlockChange(ow.get().getExtent().getUniqueId(), ow.get().getPosition().toInt()); - } + ow.ifPresent(worldLocation -> listener.onBlockChange(worldLocation.getExtent().getUniqueId(), worldLocation.getPosition().toInt())); } } - @Listener(order = Order.POST) - public void onChunkFinishedGeneration(PopulateChunkEvent.Post evt) { - Vector3i chunkPos = evt.getTargetChunk().getPosition(); - 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());