mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-22 10:35:16 +01:00
Intermediate commit: Lots of redesigning with the RenderManager and Render-Tasks
This commit is contained in:
parent
5095f0df41
commit
27295cb988
@ -1 +1 @@
|
|||||||
Subproject commit 4bef101c6ee6ddef23049287cc1a3736d954979e
|
Subproject commit 514659e5dd4c0ad3f51ecb98cc70e5c1248d994d
|
@ -24,20 +24,17 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.common;
|
package de.bluecolored.bluemap.common;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
|
||||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||||
import de.bluecolored.bluemap.core.config.*;
|
import de.bluecolored.bluemap.core.config.*;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
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.mca.MCAWorld;
|
||||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
import de.bluecolored.bluemap.core.map.hires.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.resourcepack.ParseResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
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.SlicedWorld;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
import org.apache.commons.io.FileUtils;
|
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.
|
* 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 {
|
public class BlueMapService {
|
||||||
private MinecraftVersion minecraftVersion;
|
private final MinecraftVersion minecraftVersion;
|
||||||
private File configFolder;
|
private final File configFolder;
|
||||||
private ThrowingFunction<File, UUID, IOException> worldUUIDProvider;
|
private final ThrowingFunction<File, UUID, IOException> worldUUIDProvider;
|
||||||
private ThrowingFunction<UUID, String, IOException> worldNameProvider;
|
private final ThrowingFunction<UUID, String, IOException> worldNameProvider;
|
||||||
|
|
||||||
private ConfigManager configManager;
|
private final ConfigManager configManager;
|
||||||
|
|
||||||
private CoreConfig coreConfig;
|
private CoreConfig coreConfig;
|
||||||
private RenderConfig renderConfig;
|
private RenderConfig renderConfig;
|
||||||
@ -65,7 +62,7 @@ public class BlueMapService {
|
|||||||
private ResourcePack resourcePack;
|
private ResourcePack resourcePack;
|
||||||
|
|
||||||
private Map<UUID, World> worlds;
|
private Map<UUID, World> worlds;
|
||||||
private Map<String, MapType> maps;
|
private Map<String, BmMap> maps;
|
||||||
|
|
||||||
public BlueMapService(MinecraftVersion minecraftVersion, File configFolder) {
|
public BlueMapService(MinecraftVersion minecraftVersion, File configFolder) {
|
||||||
this.minecraftVersion = minecraftVersion;
|
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 webSettings = new WebSettings(new File(getRenderConfig().getWebRoot(), "data" + File.separator + "settings.json"));
|
||||||
webSettings.set(getRenderConfig().isUseCookies(), "useCookies");
|
webSettings.set(getRenderConfig().isUseCookies(), "useCookies");
|
||||||
webSettings.setAllMapsEnabled(false);
|
webSettings.setAllMapsEnabled(false);
|
||||||
for (MapType map : getMaps().values()) {
|
for (BmMap map : getMaps().values()) {
|
||||||
webSettings.setMapEnabled(true, map.getId());
|
webSettings.setMapEnabled(true, map.getId());
|
||||||
webSettings.setFrom(map.getTileRenderer(), map.getId());
|
webSettings.setFrom(map);
|
||||||
webSettings.setFrom(map.getWorld(), map.getId());
|
|
||||||
}
|
}
|
||||||
int ordinal = 0;
|
int ordinal = 0;
|
||||||
for (MapConfig map : getRenderConfig().getMapConfigs()) {
|
for (MapConfig map : getRenderConfig().getMapConfigs()) {
|
||||||
if (!getMaps().containsKey(map.getId())) continue; //don't add not loaded maps
|
if (!getMaps().containsKey(map.getId())) continue; //don't add not loaded maps
|
||||||
webSettings.setOrdinal(ordinal++, map.getId());
|
webSettings.setOrdinal(ordinal++, map.getId());
|
||||||
webSettings.setFrom(map, map.getId());
|
webSettings.setFrom(map);
|
||||||
}
|
}
|
||||||
webSettings.save();
|
webSettings.save();
|
||||||
|
|
||||||
@ -127,7 +123,7 @@ public synchronized Map<UUID, World> getWorlds() throws IOException, Interrupted
|
|||||||
return worlds;
|
return worlds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Map<String, MapType> getMaps() throws IOException, InterruptedException {
|
public synchronized Map<String, BmMap> getMaps() throws IOException, InterruptedException {
|
||||||
if (maps == null) loadWorldsAndMaps();
|
if (maps == null) loadWorldsAndMaps();
|
||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
@ -135,6 +131,9 @@ public synchronized Map<String, MapType> getMaps() throws IOException, Interrupt
|
|||||||
private synchronized void loadWorldsAndMaps() throws IOException, InterruptedException {
|
private synchronized void loadWorldsAndMaps() throws IOException, InterruptedException {
|
||||||
maps = new HashMap<>();
|
maps = new HashMap<>();
|
||||||
worlds = new HashMap<>();
|
worlds = new HashMap<>();
|
||||||
|
|
||||||
|
ConfigManager configManager = getConfigManager();
|
||||||
|
configManager.loadResourceConfigs(configFolder, getResourcePack());
|
||||||
|
|
||||||
for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) {
|
for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) {
|
||||||
String id = mapConfig.getId();
|
String id = mapConfig.getId();
|
||||||
@ -154,9 +153,6 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigManager configManager = getConfigManager();
|
|
||||||
configManager.loadResourceConfigs(configFolder, getResourcePack());
|
|
||||||
|
|
||||||
World world = worlds.get(worldUUID);
|
World world = worlds.get(worldUUID);
|
||||||
if (world == null) {
|
if (world == null) {
|
||||||
try {
|
try {
|
||||||
@ -165,7 +161,7 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
|
|||||||
} catch (MissingResourcesException e) {
|
} catch (MissingResourcesException e) {
|
||||||
throw e; // rethrow this to stop loading and display resource-missing message
|
throw e; // rethrow this to stop loading and display resource-missing message
|
||||||
} catch (IOException e) {
|
} 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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,28 +175,20 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
|
|||||||
world,
|
world,
|
||||||
mapConfig.getMin().min(mapConfig.getMin().sub(2, 2, 2)), // protect from int-overflow
|
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
|
mapConfig.getMax().max(mapConfig.getMax().add(2, 2, 2)) // protect from int-overflow
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HiresModelManager hiresModelManager = new HiresModelManager(
|
BmMap map = new BmMap(
|
||||||
getRenderConfig().getWebRoot().toPath().resolve("data").resolve(id).resolve("hires"),
|
id,
|
||||||
|
name,
|
||||||
|
world,
|
||||||
|
getRenderConfig().getWebRoot().toPath().resolve("data").resolve(id),
|
||||||
getResourcePack(),
|
getResourcePack(),
|
||||||
mapConfig,
|
mapConfig
|
||||||
new Vector2i(mapConfig.getHiresTileSize(), mapConfig.getHiresTileSize())
|
);
|
||||||
);
|
|
||||||
|
maps.put(id, map);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
worlds = Collections.unmodifiableMap(worlds);
|
worlds = Collections.unmodifiableMap(worlds);
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,287 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<RenderTicket> renderTickets;
|
|
||||||
private Map<RenderTicket, RenderTicket> renderTicketMap;
|
|
||||||
private Deque<RenderTask> 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<RenderTicket> createTickets(MapType mapType, Collection<Vector2i> tiles) {
|
|
||||||
if (tiles.size() < 0) return Collections.emptyList();
|
|
||||||
|
|
||||||
Collection<RenderTicket> 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<MapType, Vector2i> tileMap = MultimapBuilder.hashKeys().arrayListValues().<MapType, Vector2i>build();
|
|
||||||
synchronized (renderTickets) {
|
|
||||||
for (RenderTicket ticket : renderTickets) {
|
|
||||||
tileMap.put(ticket.getMapType(), ticket.getTile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//write renderTickets
|
|
||||||
Set<MapType> maps = tileMap.keySet();
|
|
||||||
out.writeInt(maps.size());
|
|
||||||
for (MapType map : maps) {
|
|
||||||
List<Vector2i> 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<MapType> 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<Vector2i> 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() + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,217 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<Vector2i> 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<Vector2i> 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<Vector2i> 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<MapType> 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<Vector2i> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -27,7 +27,7 @@
|
|||||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||||
import de.bluecolored.bluemap.api.BlueMapWorld;
|
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.marker.MarkerAPIImpl;
|
||||||
import de.bluecolored.bluemap.common.api.render.RenderAPIImpl;
|
import de.bluecolored.bluemap.common.api.render.RenderAPIImpl;
|
||||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||||
@ -68,7 +68,7 @@ public BlueMapAPIImpl(Plugin plugin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
maps = new HashMap<>();
|
maps = new HashMap<>();
|
||||||
for (MapType map : plugin.getMapTypes()) {
|
for (BmMap map : plugin.getMapTypes()) {
|
||||||
BlueMapMapImpl m = new BlueMapMapImpl(this, map);
|
BlueMapMapImpl m = new BlueMapMapImpl(this, map);
|
||||||
maps.put(m.getId(), m);
|
maps.put(m.getId(), m);
|
||||||
}
|
}
|
||||||
|
@ -27,14 +27,14 @@
|
|||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||||
import de.bluecolored.bluemap.common.MapType;
|
import de.bluecolored.bluemap.core.map.BmMap;
|
||||||
|
|
||||||
public class BlueMapMapImpl implements BlueMapMap {
|
public class BlueMapMapImpl implements BlueMapMap {
|
||||||
|
|
||||||
private BlueMapAPIImpl api;
|
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.api = api;
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
}
|
}
|
||||||
@ -56,15 +56,15 @@ public BlueMapWorldImpl getWorld() {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vector2i getTileSize() {
|
public Vector2i getTileSize() {
|
||||||
return delegate.getTileRenderer().getHiresModelManager().getTileSize();
|
return delegate.getHiresModelManager().getTileGrid().getGridSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vector2i getTileOffset() {
|
public Vector2i getTileOffset() {
|
||||||
return delegate.getTileRenderer().getHiresModelManager().getGridOrigin();
|
return delegate.getHiresModelManager().getTileGrid().getOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapType getMapType() {
|
public BmMap getMapType() {
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,16 +24,15 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.common.api.render;
|
package de.bluecolored.bluemap.common.api.render;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||||
import de.bluecolored.bluemap.api.renderer.RenderAPI;
|
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.BlueMapAPIImpl;
|
||||||
import de.bluecolored.bluemap.common.api.BlueMapMapImpl;
|
import de.bluecolored.bluemap.common.api.BlueMapMapImpl;
|
||||||
|
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class RenderAPIImpl implements RenderAPI {
|
public class RenderAPIImpl implements RenderAPI {
|
||||||
|
|
||||||
@ -68,18 +67,19 @@ public void render(BlueMapMap map, Vector2i tile) {
|
|||||||
} else {
|
} else {
|
||||||
cmap = api.getMapForId(map.getId());
|
cmap = api.getMapForId(map.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
renderManager.createTicket(cmap.getMapType(), tile);
|
//TODO
|
||||||
|
//renderManager.createTicket(cmap.getMapType(), tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int renderQueueSize() {
|
public int renderQueueSize() {
|
||||||
return renderManager.getQueueSize();
|
return renderManager.getScheduledRenderTasks().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int renderThreadCount() {
|
public int renderThreadCount() {
|
||||||
return renderManager.getRenderThreadCount();
|
return renderManager.getWorkerThreadCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -89,7 +89,7 @@ public boolean isRunning() {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
if (!isRunning()) renderManager.start();
|
if (!isRunning()) renderManager.start(api.plugin.getCoreConfig().getRenderThreadCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,220 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<UUID, Vector2i> 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<Vector2i> chunks = updateBuffer.get(map.getWorld().getUUID());
|
|
||||||
Collection<Vector2i> 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<Vector2i> chunks = updateBuffer.get(world);
|
|
||||||
Collection<Vector2i> 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<Vector2i> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -24,30 +24,33 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.common.plugin;
|
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.api.BlueMapAPIImpl;
|
||||||
import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler;
|
import de.bluecolored.bluemap.common.live.LiveAPIRequestHandler;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||||
import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater;
|
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.BlueMap;
|
||||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||||
import de.bluecolored.bluemap.core.config.CoreConfig;
|
import de.bluecolored.bluemap.core.config.CoreConfig;
|
||||||
import de.bluecolored.bluemap.core.config.RenderConfig;
|
import de.bluecolored.bluemap.core.config.RenderConfig;
|
||||||
import de.bluecolored.bluemap.core.config.WebServerConfig;
|
import de.bluecolored.bluemap.core.config.WebServerConfig;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
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.metrics.Metrics;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
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.HttpRequestHandler;
|
||||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
public class Plugin {
|
public class Plugin {
|
||||||
|
|
||||||
@ -58,23 +61,26 @@ public class Plugin {
|
|||||||
|
|
||||||
private final MinecraftVersion minecraftVersion;
|
private final MinecraftVersion minecraftVersion;
|
||||||
private final String implementationType;
|
private final String implementationType;
|
||||||
private ServerInterface serverInterface;
|
private final ServerInterface serverInterface;
|
||||||
|
|
||||||
private BlueMapService blueMap;
|
private BlueMapService blueMap;
|
||||||
private BlueMapAPIImpl api;
|
private BlueMapAPIImpl api;
|
||||||
|
|
||||||
|
private CoreConfig coreConfig;
|
||||||
|
private RenderConfig renderConfig;
|
||||||
|
private WebServerConfig webServerConfig;
|
||||||
|
private PluginConfig pluginConfig;
|
||||||
|
|
||||||
private Map<UUID, World> worlds;
|
private Map<UUID, World> worlds;
|
||||||
private Map<String, MapType> maps;
|
private Map<String, BmMap> maps;
|
||||||
|
|
||||||
private RenderManager renderManager;
|
private RenderManager renderManager;
|
||||||
private WebServer webServer;
|
private WebServer webServer;
|
||||||
|
|
||||||
private final Timer daemonTimer;
|
private final Timer daemonTimer;
|
||||||
private TimerTask periodicalSaveTask;
|
private TimerTask saveTask;
|
||||||
private TimerTask metricsTask;
|
private TimerTask metricsTask;
|
||||||
|
|
||||||
private PluginConfig pluginConfig;
|
|
||||||
private MapUpdateHandler updateHandler;
|
|
||||||
private PlayerSkinUpdater skinUpdater;
|
private PlayerSkinUpdater skinUpdater;
|
||||||
|
|
||||||
private boolean loaded = false;
|
private boolean loaded = false;
|
||||||
@ -98,9 +104,9 @@ public void load() throws IOException, ParseResourceException {
|
|||||||
blueMap = new BlueMapService(minecraftVersion, serverInterface);
|
blueMap = new BlueMapService(minecraftVersion, serverInterface);
|
||||||
|
|
||||||
//load configs
|
//load configs
|
||||||
CoreConfig coreConfig = blueMap.getCoreConfig();
|
coreConfig = blueMap.getCoreConfig();
|
||||||
RenderConfig renderConfig = blueMap.getRenderConfig();
|
renderConfig = blueMap.getRenderConfig();
|
||||||
WebServerConfig webServerConfig = blueMap.getWebServerConfig();
|
webServerConfig = blueMap.getWebServerConfig();
|
||||||
|
|
||||||
//load plugin config
|
//load plugin config
|
||||||
pluginConfig = new PluginConfig(blueMap.getConfigManager().loadOrCreate(
|
pluginConfig = new PluginConfig(blueMap.getConfigManager().loadOrCreate(
|
||||||
@ -154,45 +160,8 @@ public void load() throws IOException, ParseResourceException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//initialize render manager
|
//initialize render manager
|
||||||
renderManager = new RenderManager(coreConfig.getRenderThreadCount());
|
renderManager = new RenderManager();
|
||||||
renderManager.start();
|
renderManager.start(coreConfig.getRenderThreadCount());
|
||||||
|
|
||||||
//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);
|
|
||||||
|
|
||||||
//update webapp and settings
|
//update webapp and settings
|
||||||
blueMap.createOrUpdateWebApp(false);
|
blueMap.createOrUpdateWebApp(false);
|
||||||
@ -206,6 +175,20 @@ public void run() {
|
|||||||
);
|
);
|
||||||
serverInterface.registerListener(skinUpdater);
|
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
|
//metrics
|
||||||
metricsTask = new TimerTask() {
|
metricsTask = new TimerTask() {
|
||||||
@ -222,6 +205,8 @@ public void run() {
|
|||||||
//enable api
|
//enable api
|
||||||
this.api = new BlueMapAPIImpl(this);
|
this.api = new BlueMapAPIImpl(this);
|
||||||
this.api.register();
|
this.api.register();
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@ -246,26 +231,15 @@ public void unload() {
|
|||||||
|
|
||||||
//stop scheduled threads
|
//stop scheduled threads
|
||||||
metricsTask.cancel();
|
metricsTask.cancel();
|
||||||
periodicalSaveTask.cancel();
|
|
||||||
|
|
||||||
//stop services
|
//stop services
|
||||||
if (renderManager != null) renderManager.stop();
|
if (renderManager != null) renderManager.stop();
|
||||||
if (webServer != null) webServer.close();
|
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
|
//save renders
|
||||||
if (maps != null) {
|
if (maps != null) {
|
||||||
for (MapType map : maps.values()) {
|
for (BmMap map : maps.values()) {
|
||||||
map.getTileRenderer().save();
|
map.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +249,8 @@ public void unload() {
|
|||||||
maps = null;
|
maps = null;
|
||||||
renderManager = null;
|
renderManager = null;
|
||||||
webServer = null;
|
webServer = null;
|
||||||
updateHandler = null;
|
|
||||||
|
|
||||||
pluginConfig = null;
|
pluginConfig = null;
|
||||||
|
|
||||||
loaded = false;
|
loaded = false;
|
||||||
@ -285,39 +260,32 @@ public void unload() {
|
|||||||
loadingLock.unlock();
|
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 {
|
public void reload() throws IOException, ParseResourceException {
|
||||||
unload();
|
unload();
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean flushWorldUpdates(UUID worldUUID) throws IOException {
|
||||||
|
return serverInterface.persistWorldChanges(worldUUID);
|
||||||
|
}
|
||||||
|
|
||||||
public ServerInterface getServerInterface() {
|
public ServerInterface getServerInterface() {
|
||||||
return serverInterface;
|
return serverInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CoreConfig getCoreConfig() throws IOException {
|
public CoreConfig getCoreConfig() {
|
||||||
return blueMap.getCoreConfig();
|
return coreConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderConfig getRenderConfig() throws IOException {
|
public RenderConfig getRenderConfig() {
|
||||||
return blueMap.getRenderConfig();
|
return renderConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebServerConfig getWebServerConfig() throws IOException {
|
public WebServerConfig getWebServerConfig() {
|
||||||
return blueMap.getWebServerConfig();
|
return webServerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PluginConfig getPluginConfig() {
|
public PluginConfig getPluginConfig() {
|
||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
}
|
}
|
||||||
@ -330,7 +298,7 @@ public Collection<World> getWorlds(){
|
|||||||
return worlds.values();
|
return worlds.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<MapType> getMapTypes(){
|
public Collection<BmMap> getMapTypes(){
|
||||||
return maps.values();
|
return maps.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,24 +306,6 @@ public RenderManager getRenderManager() {
|
|||||||
return renderManager;
|
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() {
|
public WebServer getWebServer() {
|
||||||
return webServer;
|
return webServer;
|
||||||
}
|
}
|
||||||
|
@ -24,29 +24,16 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.common.plugin.commands;
|
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.Plugin;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource;
|
|
||||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
import de.bluecolored.bluemap.common.plugin.text.TextColor;
|
import de.bluecolored.bluemap.common.plugin.text.TextColor;
|
||||||
import de.bluecolored.bluemap.common.plugin.text.TextFormat;
|
import de.bluecolored.bluemap.core.map.BmMap;
|
||||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
public class CommandHelper {
|
public class CommandHelper {
|
||||||
|
|
||||||
private Plugin plugin;
|
private Plugin plugin;
|
||||||
@ -57,119 +44,12 @@ public CommandHelper(Plugin plugin) {
|
|||||||
|
|
||||||
public List<Text> createStatusMessage(){
|
public List<Text> createStatusMessage(){
|
||||||
List<Text> lines = new ArrayList<>();
|
List<Text> lines = new ArrayList<>();
|
||||||
|
|
||||||
RenderManager renderer = plugin.getRenderManager();
|
|
||||||
|
|
||||||
lines.add(Text.of());
|
|
||||||
lines.add(Text.of(TextColor.BLUE, "Tile-Updates:"));
|
|
||||||
|
|
||||||
if (renderer.isRunning()) {
|
//TODO
|
||||||
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)"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines;
|
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<Vector2i> 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<Vector2i> 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<Vector2i> 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() {
|
public Text worldHelperHover() {
|
||||||
StringJoiner joiner = new StringJoiner("\n");
|
StringJoiner joiner = new StringJoiner("\n");
|
||||||
for (World world : plugin.getWorlds()) {
|
for (World world : plugin.getWorlds()) {
|
||||||
@ -181,27 +61,10 @@ public Text worldHelperHover() {
|
|||||||
|
|
||||||
public Text mapHelperHover() {
|
public Text mapHelperHover() {
|
||||||
StringJoiner joiner = new StringJoiner("\n");
|
StringJoiner joiner = new StringJoiner("\n");
|
||||||
for (MapType map : plugin.getMapTypes()) {
|
for (BmMap map : plugin.getMapTypes()) {
|
||||||
joiner.add(map.getId());
|
joiner.add(map.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Text.of("map").setHoverText(Text.of(TextColor.WHITE, "Available maps: \n", TextColor.GRAY, joiner.toString()));
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,18 +24,6 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.common.plugin.commands;
|
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.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3d;
|
import com.flowpowered.math.vector.Vector3d;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
@ -49,25 +37,34 @@
|
|||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.api.BlueMapAPI;
|
import de.bluecolored.bluemap.api.BlueMapAPI;
|
||||||
import de.bluecolored.bluemap.api.BlueMapMap;
|
import de.bluecolored.bluemap.api.BlueMapMap;
|
||||||
import de.bluecolored.bluemap.api.marker.MarkerAPI;
|
import de.bluecolored.bluemap.api.marker.MarkerAPI;
|
||||||
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
import de.bluecolored.bluemap.api.marker.MarkerSet;
|
||||||
import de.bluecolored.bluemap.api.marker.POIMarker;
|
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.Plugin;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.CommandSource;
|
||||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
import de.bluecolored.bluemap.common.plugin.text.TextColor;
|
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.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.ChunkAnvil112;
|
||||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
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<S> {
|
public class Commands<S> {
|
||||||
|
|
||||||
@ -172,23 +169,6 @@ public void init() {
|
|||||||
.then(argument("radius", IntegerArgumentType.integer())
|
.then(argument("radius", IntegerArgumentType.integer())
|
||||||
.executes(this::renderCommand))))) // /bluemap render <world|map> <x> <z> <radius>
|
.executes(this::renderCommand))))) // /bluemap render <world|map> <x> <z> <radius>
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
LiteralCommandNode<S> prioRenderCommand =
|
|
||||||
literal("prioritize")
|
|
||||||
.requires(requirements("bluemap.render"))
|
|
||||||
.then(argument("uuid", StringArgumentType.string())
|
|
||||||
.executes(this::prioritizeRenderTaskCommand))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
LiteralCommandNode<S> cancelRenderCommand =
|
|
||||||
literal("cancel")
|
|
||||||
.requires(requirements("bluemap.render"))
|
|
||||||
.executes(this::cancelLastRenderTaskCommand)
|
|
||||||
|
|
||||||
.then(argument("uuid", StringArgumentType.string())
|
|
||||||
.executes(this::cancelRenderTaskCommand))
|
|
||||||
|
|
||||||
.build();
|
|
||||||
|
|
||||||
LiteralCommandNode<S> purgeCommand =
|
LiteralCommandNode<S> purgeCommand =
|
||||||
literal("purge")
|
literal("purge")
|
||||||
@ -247,8 +227,6 @@ public void init() {
|
|||||||
baseCommand.addChild(resumeCommand);
|
baseCommand.addChild(resumeCommand);
|
||||||
baseCommand.addChild(renderCommand);
|
baseCommand.addChild(renderCommand);
|
||||||
baseCommand.addChild(purgeCommand);
|
baseCommand.addChild(purgeCommand);
|
||||||
renderCommand.addChild(prioRenderCommand);
|
|
||||||
renderCommand.addChild(cancelRenderCommand);
|
|
||||||
baseCommand.addChild(worldsCommand);
|
baseCommand.addChild(worldsCommand);
|
||||||
baseCommand.addChild(mapsCommand);
|
baseCommand.addChild(mapsCommand);
|
||||||
baseCommand.addChild(markerCommand);
|
baseCommand.addChild(markerCommand);
|
||||||
@ -296,8 +274,8 @@ private Optional<World> parseWorld(String worldName) {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<MapType> parseMap(String mapId) {
|
private Optional<BmMap> parseMap(String mapId) {
|
||||||
for (MapType map : plugin.getMapTypes()) {
|
for (BmMap map : plugin.getMapTypes()) {
|
||||||
if (map.getId().equalsIgnoreCase(mapId)) {
|
if (map.getId().equalsIgnoreCase(mapId)) {
|
||||||
return Optional.of(map);
|
return Optional.of(map);
|
||||||
}
|
}
|
||||||
@ -334,7 +312,7 @@ public int versionCommand(CommandContext<S> context) {
|
|||||||
|
|
||||||
int renderThreadCount = 0;
|
int renderThreadCount = 0;
|
||||||
if (plugin.isLoaded()) {
|
if (plugin.isLoaded()) {
|
||||||
renderThreadCount = plugin.getRenderManager().getRenderThreadCount();
|
renderThreadCount = plugin.getRenderManager().getWorkerThreadCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
source.sendMessage(Text.of(TextFormat.BOLD, TextColor.BLUE, "Version: ", TextColor.WHITE, BlueMap.VERSION));
|
source.sendMessage(Text.of(TextFormat.BOLD, TextColor.BLUE, "Version: ", TextColor.WHITE, BlueMap.VERSION));
|
||||||
@ -504,7 +482,7 @@ public int debugBlockCommand(CommandContext<S> context) {
|
|||||||
String blockBelowIdMeta = "";
|
String blockBelowIdMeta = "";
|
||||||
|
|
||||||
if (world instanceof MCAWorld) {
|
if (world instanceof MCAWorld) {
|
||||||
Chunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(blockPos));
|
MCAChunk chunk = ((MCAWorld) world).getChunk(MCAWorld.blockToChunk(blockPos));
|
||||||
if (chunk instanceof ChunkAnvil112) {
|
if (chunk instanceof ChunkAnvil112) {
|
||||||
blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos) + ")";
|
blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos) + ")";
|
||||||
blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos.add(0, -1, 0)) + ")";
|
blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos.add(0, -1, 0)) + ")";
|
||||||
@ -512,7 +490,6 @@ public int debugBlockCommand(CommandContext<S> context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
source.sendMessages(Lists.newArrayList(
|
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 at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta),
|
||||||
Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta)
|
Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta)
|
||||||
));
|
));
|
||||||
@ -538,7 +515,7 @@ public int resumeCommand(CommandContext<S> context) {
|
|||||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||||
|
|
||||||
if (!plugin.getRenderManager().isRunning()) {
|
if (!plugin.getRenderManager().isRunning()) {
|
||||||
plugin.getRenderManager().start();
|
plugin.getRenderManager().start(plugin.getCoreConfig().getRenderThreadCount());
|
||||||
source.sendMessage(Text.of(TextColor.GREEN, "BlueMap renders resumed!"));
|
source.sendMessage(Text.of(TextColor.GREEN, "BlueMap renders resumed!"));
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
@ -554,7 +531,7 @@ public int renderCommand(CommandContext<S> context) {
|
|||||||
Optional<String> worldOrMap = getOptionalArgument(context, "world|map", String.class);
|
Optional<String> worldOrMap = getOptionalArgument(context, "world|map", String.class);
|
||||||
|
|
||||||
final World world;
|
final World world;
|
||||||
final MapType map;
|
final BmMap map;
|
||||||
if (worldOrMap.isPresent()) {
|
if (worldOrMap.isPresent()) {
|
||||||
world = parseWorld(worldOrMap.get()).orElse(null);
|
world = parseWorld(worldOrMap.get()).orElse(null);
|
||||||
|
|
||||||
@ -605,10 +582,10 @@ public int renderCommand(CommandContext<S> context) {
|
|||||||
try {
|
try {
|
||||||
if (world != null) {
|
if (world != null) {
|
||||||
plugin.getServerInterface().persistWorldChanges(world.getUUID());
|
plugin.getServerInterface().persistWorldChanges(world.getUUID());
|
||||||
helper.createWorldRenderTask(source, world, center, radius);
|
//TODO: helper.createWorldRenderTask(source, world, center, radius);
|
||||||
} else {
|
} else {
|
||||||
plugin.getServerInterface().persistWorldChanges(map.getWorld().getUUID());
|
plugin.getServerInterface().persistWorldChanges(map.getWorld().getUUID());
|
||||||
helper.createMapRenderTask(source, map, center, radius);
|
//TODO: helper.createMapRenderTask(source, map, center, radius);
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} 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..."));
|
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<S> context) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int prioritizeRenderTaskCommand(CommandContext<S> context) {
|
|
||||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
|
||||||
|
|
||||||
String uuidString = context.getArgument("uuid", String.class);
|
|
||||||
Optional<UUID> 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<S> 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<S> context) {
|
|
||||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
|
||||||
|
|
||||||
String uuidString = context.getArgument("uuid", String.class);
|
|
||||||
Optional<UUID> 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<S> context) {
|
public int worldsCommand(CommandContext<S> context) {
|
||||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||||
|
|
||||||
@ -721,7 +636,7 @@ public int mapsCommand(CommandContext<S> context) {
|
|||||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||||
|
|
||||||
source.sendMessage(Text.of(TextColor.BLUE, "Maps loaded by BlueMap:"));
|
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())));
|
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<S> context) {
|
|||||||
|
|
||||||
// parse world/map argument
|
// parse world/map argument
|
||||||
String mapString = context.getArgument("map", String.class);
|
String mapString = context.getArgument("map", String.class);
|
||||||
MapType map = parseMap(mapString).orElse(null);
|
BmMap map = parseMap(mapString).orElse(null);
|
||||||
|
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString));
|
source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString));
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
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.common.plugin.Plugin;
|
||||||
|
|
||||||
public class MapSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
|
public class MapSuggestionProvider<S> extends AbstractSuggestionProvider<S> {
|
||||||
@ -42,7 +42,7 @@ public MapSuggestionProvider(Plugin plugin) {
|
|||||||
public Collection<String> getPossibleValues() {
|
public Collection<String> getPossibleValues() {
|
||||||
Collection<String> values = new HashSet<>();
|
Collection<String> values = new HashSet<>();
|
||||||
|
|
||||||
for (MapType map : plugin.getMapTypes()) {
|
for (BmMap map : plugin.getMapTypes()) {
|
||||||
values.add(map.getId());
|
values.add(map.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
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.common.plugin.Plugin;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ public Collection<String> getPossibleValues() {
|
|||||||
values.add(world.getName());
|
values.add(world.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (MapType map : plugin.getMapTypes()) {
|
for (BmMap map : plugin.getMapTypes()) {
|
||||||
values.add(map.getId());
|
values.add(map.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,23 +24,15 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
package de.bluecolored.bluemap.common.plugin.serverinterface;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface ServerEventListener {
|
public interface ServerEventListener {
|
||||||
|
|
||||||
default void onWorldSaveToDisk(UUID world) {};
|
|
||||||
|
|
||||||
default void onChunkSaveToDisk(UUID world, Vector2i chunkPos) {};
|
|
||||||
|
|
||||||
default void onBlockChange(UUID world, Vector3i blockPos) {};
|
default void onBlockChange(UUID world, Vector3i blockPos) {};
|
||||||
|
|
||||||
default void onChunkFinishedGeneration(UUID world, Vector2i chunkPos) {};
|
|
||||||
|
|
||||||
default void onPlayerJoin(UUID playerUuid) {};
|
default void onPlayerJoin(UUID playerUuid) {};
|
||||||
|
|
||||||
default void onPlayerLeave(UUID playerUuid) {};
|
default void onPlayerLeave(UUID playerUuid) {};
|
||||||
|
@ -95,7 +95,7 @@ default boolean isMetricsEnabled(boolean configValue) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the state of the player with that UUID if present<br>
|
* Returns the state of the player with that UUID if present<br>
|
||||||
* 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<Player> getPlayer(UUID uuid);
|
Optional<Player> getPlayer(UUID uuid);
|
||||||
|
|
||||||
|
@ -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<T extends RenderTask> implements RenderTask {
|
||||||
|
|
||||||
|
private final List<T> tasks;
|
||||||
|
private int currentTaskIndex;
|
||||||
|
|
||||||
|
public CombinedRenderTask(Collection<T> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||||
|
*
|
||||||
|
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||||
|
* 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<WorkerThread> workerThreads;
|
||||||
|
private final AtomicInteger busyCount;
|
||||||
|
|
||||||
|
private final LinkedList<RenderTask> renderTasks;
|
||||||
|
private final Set<RenderTask> 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<RenderTask> 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<RenderTask> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.<br>
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
}
|
@ -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<Vector2i> 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<Vector2i> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,7 +22,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.HttpRequest;
|
||||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
@ -22,15 +22,14 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3f;
|
import com.flowpowered.math.vector.Vector3f;
|
||||||
import de.bluecolored.bluemap.core.config.MapConfig;
|
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.FileUtils;
|
||||||
import de.bluecolored.bluemap.core.util.MathUtils;
|
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
|
||||||
import ninja.leaping.configurate.ConfigurationNode;
|
import ninja.leaping.configurate.ConfigurationNode;
|
||||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||||
@ -92,7 +91,7 @@ public double getDouble(Object... path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getMapIds() {
|
public Collection<String> 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) {
|
public void setAllMapsEnabled(boolean enabled) {
|
||||||
@ -105,51 +104,49 @@ public void setMapEnabled(boolean enabled, String mapId) {
|
|||||||
set(enabled, "maps", mapId, "enabled");
|
set(enabled, "maps", mapId, "enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFrom(TileRenderer tileRenderer, String mapId) {
|
public void setFrom(BmMap map) {
|
||||||
Vector2i hiresTileSize = tileRenderer.getHiresModelManager().getTileSize();
|
Vector2i hiresTileSize = map.getHiresModelManager().getTileGrid().getGridSize();
|
||||||
Vector2i gridOrigin = tileRenderer.getHiresModelManager().getGridOrigin();
|
Vector2i gridOrigin = map.getHiresModelManager().getTileGrid().getOffset();
|
||||||
Vector2i lowresTileSize = tileRenderer.getLowresModelManager().getTileSize();
|
Vector2i lowresTileSize = map.getLowresModelManager().getTileSize();
|
||||||
Vector2i lowresPointsPerHiresTile = tileRenderer.getLowresModelManager().getPointsPerHiresTile();
|
Vector2i lowresPointsPerHiresTile = map.getLowresModelManager().getPointsPerHiresTile();
|
||||||
|
|
||||||
set(hiresTileSize.getX(), "maps", mapId, "hires", "tileSize", "x");
|
set(hiresTileSize.getX(), "maps", map.getId(), "hires", "tileSize", "x");
|
||||||
set(hiresTileSize.getY(), "maps", mapId, "hires", "tileSize", "z");
|
set(hiresTileSize.getY(), "maps", map.getId(), "hires", "tileSize", "z");
|
||||||
set(1, "maps", mapId, "hires", "scale", "x");
|
set(1, "maps", map.getId(), "hires", "scale", "x");
|
||||||
set(1, "maps", mapId, "hires", "scale", "z");
|
set(1, "maps", map.getId(), "hires", "scale", "z");
|
||||||
set(gridOrigin.getX(), "maps", mapId, "hires", "translate", "x");
|
set(gridOrigin.getX(), "maps", map.getId(), "hires", "translate", "x");
|
||||||
set(gridOrigin.getY(), "maps", mapId, "hires", "translate", "z");
|
set(gridOrigin.getY(), "maps", map.getId(), "hires", "translate", "z");
|
||||||
|
|
||||||
Vector2i pointSize = hiresTileSize.div(lowresPointsPerHiresTile);
|
Vector2i pointSize = hiresTileSize.div(lowresPointsPerHiresTile);
|
||||||
Vector2i tileSize = pointSize.mul(lowresTileSize);
|
Vector2i tileSize = pointSize.mul(lowresTileSize);
|
||||||
|
|
||||||
set(tileSize.getX(), "maps", mapId, "lowres", "tileSize", "x");
|
set(tileSize.getX(), "maps", map.getId(), "lowres", "tileSize", "x");
|
||||||
set(tileSize.getY(), "maps", mapId, "lowres", "tileSize", "z");
|
set(tileSize.getY(), "maps", map.getId(), "lowres", "tileSize", "z");
|
||||||
set(pointSize.getX(), "maps", mapId, "lowres", "scale", "x");
|
set(pointSize.getX(), "maps", map.getId(), "lowres", "scale", "x");
|
||||||
set(pointSize.getY(), "maps", mapId, "lowres", "scale", "z");
|
set(pointSize.getY(), "maps", map.getId(), "lowres", "scale", "z");
|
||||||
set(pointSize.getX() / 2, "maps", mapId, "lowres", "translate", "x");
|
set(pointSize.getX() / 2, "maps", map.getId(), "lowres", "translate", "x");
|
||||||
set(pointSize.getY() / 2, "maps", mapId, "lowres", "translate", "z");
|
set(pointSize.getY() / 2, "maps", map.getId(), "lowres", "translate", "z");
|
||||||
}
|
|
||||||
|
|
||||||
public void setFrom(World world, String mapId) {
|
set(map.getWorld().getSpawnPoint().getX(), "maps", map.getId(), "startPos", "x");
|
||||||
set(world.getSpawnPoint().getX(), "maps", mapId, "startPos", "x");
|
set(map.getWorld().getSpawnPoint().getZ(), "maps", map.getId(), "startPos", "z");
|
||||||
set(world.getSpawnPoint().getZ(), "maps", mapId, "startPos", "z");
|
set(map.getWorld().getUUID().toString(), "maps", map.getId(), "world");
|
||||||
set(world.getUUID().toString(), "maps", mapId, "world");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFrom(MapConfig mapConfig, String mapId) {
|
public void setFrom(MapConfig mapConfig) {
|
||||||
Vector2i startPos = mapConfig.getStartPos();
|
Vector2i startPos = mapConfig.getStartPos();
|
||||||
if (startPos != null) {
|
if (startPos != null) {
|
||||||
set(startPos.getX(), "maps", mapId, "startPos", "x");
|
set(startPos.getX(), "maps", mapConfig.getId(), "startPos", "x");
|
||||||
set(startPos.getY(), "maps", mapId, "startPos", "z");
|
set(startPos.getY(), "maps", mapConfig.getId(), "startPos", "z");
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3f skyColor = MathUtils.color3FromInt(mapConfig.getSkyColor());
|
Vector3f skyColor = MathUtils.color3FromInt(mapConfig.getSkyColor());
|
||||||
set(skyColor.getX(), "maps", mapId, "skyColor", "r");
|
set(skyColor.getX(), "maps", mapConfig.getId(), "skyColor", "r");
|
||||||
set(skyColor.getY(), "maps", mapId, "skyColor", "g");
|
set(skyColor.getY(), "maps", mapConfig.getId(), "skyColor", "g");
|
||||||
set(skyColor.getZ(), "maps", mapId, "skyColor", "b");
|
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) {
|
public void setOrdinal(int ordinal, String mapId) {
|
@ -8,7 +8,11 @@ dependencies {
|
|||||||
compile 'org.spongepowered:configurate-gson:3.7.1'
|
compile 'org.spongepowered:configurate-gson:3.7.1'
|
||||||
compile 'com.github.Querz:NBT:4.0'
|
compile 'com.github.Querz:NBT:4.0'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
|
@ -24,17 +24,17 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.config;
|
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.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
import de.bluecolored.bluemap.core.map.MapSettings;
|
||||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
|
||||||
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
||||||
import ninja.leaping.configurate.ConfigurationNode;
|
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 static final Pattern VALID_ID_PATTERN = Pattern.compile("[a-zA-Z0-9_]+");
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
@ -86,12 +86,12 @@ public MapConfig(ConfigurationNode node) throws IOException {
|
|||||||
this.renderCaves = node.getNode("renderCaves").getBoolean(false);
|
this.renderCaves = node.getNode("renderCaves").getBoolean(false);
|
||||||
|
|
||||||
//bounds
|
//bounds
|
||||||
int minX = node.getNode("minX").getInt(RenderSettings.super.getMin().getX());
|
int minX = node.getNode("minX").getInt(MapSettings.super.getMin().getX());
|
||||||
int maxX = node.getNode("maxX").getInt(RenderSettings.super.getMax().getX());
|
int maxX = node.getNode("maxX").getInt(MapSettings.super.getMax().getX());
|
||||||
int minZ = node.getNode("minZ").getInt(RenderSettings.super.getMin().getZ());
|
int minZ = node.getNode("minZ").getInt(MapSettings.super.getMin().getZ());
|
||||||
int maxZ = node.getNode("maxZ").getInt(RenderSettings.super.getMax().getZ());
|
int maxZ = node.getNode("maxZ").getInt(MapSettings.super.getMax().getZ());
|
||||||
int minY = node.getNode("minY").getInt(RenderSettings.super.getMin().getY());
|
int minY = node.getNode("minY").getInt(MapSettings.super.getMin().getY());
|
||||||
int maxY = node.getNode("maxY").getInt(RenderSettings.super.getMax().getY());
|
int maxY = node.getNode("maxY").getInt(MapSettings.super.getMax().getY());
|
||||||
this.min = new Vector3i(minX, minY, minZ);
|
this.min = new Vector3i(minX, minY, minZ);
|
||||||
this.max = new Vector3i(maxX, maxY, maxZ);
|
this.max = new Vector3i(maxX, maxY, maxZ);
|
||||||
|
|
||||||
@ -146,15 +146,18 @@ public boolean isRenderCaves() {
|
|||||||
public boolean isIgnoreMissingLightData() {
|
public boolean isIgnoreMissingLightData() {
|
||||||
return ignoreMissingLightData;
|
return ignoreMissingLightData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getHiresTileSize() {
|
public int getHiresTileSize() {
|
||||||
return hiresTileSize;
|
return hiresTileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getLowresPointsPerHiresTile() {
|
public int getLowresPointsPerHiresTile() {
|
||||||
return lowresPointsPerHiresTile;
|
return lowresPointsPerHiresTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getLowresPointsPerLowresTile() {
|
public int getLowresPointsPerLowresTile() {
|
||||||
return lowresPointsPerLowresTile;
|
return lowresPointsPerLowresTile;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||||
|
*
|
||||||
|
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<Vector2i, Long> 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<Vector2i, Long> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
|
||||||
|
}
|
@ -22,31 +22,27 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.Vector3i;
|
||||||
import com.flowpowered.math.vector.Vector4f;
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.model.ExtendedModel;
|
import de.bluecolored.bluemap.core.model.ExtendedModel;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A model, containing additional information about the tile it represents
|
* A model, containing additional information about the tile it represents
|
||||||
*/
|
*/
|
||||||
public class HiresModel extends ExtendedModel {
|
public class HiresModel extends ExtendedModel {
|
||||||
|
|
||||||
private UUID world;
|
private UUID world;
|
||||||
private Vector2i tile;
|
|
||||||
private Vector3i blockMin, blockMax, blockSize;
|
private Vector3i blockMin, blockMax, blockSize;
|
||||||
|
|
||||||
private int[][] heights;
|
private int[][] heights;
|
||||||
private Vector4f[][] colors;
|
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.world = world;
|
||||||
this.tile = tile;
|
|
||||||
this.blockMin = blockMin;
|
this.blockMin = blockMin;
|
||||||
this.blockMax = blockMax;
|
this.blockMax = blockMax;
|
||||||
this.blockSize = blockMax.sub(blockMin).add(Vector3i.ONE);
|
this.blockSize = blockMax.sub(blockMin).add(Vector3i.ONE);
|
||||||
@ -89,8 +85,4 @@ public Vector3i getBlockSize(){
|
|||||||
return blockSize;
|
return blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2i getTile(){
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -22,17 +22,16 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3d;
|
import com.flowpowered.math.vector.Vector3d;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
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.resourcepack.ResourcePack;
|
||||||
import de.bluecolored.bluemap.core.util.AABB;
|
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
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.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -44,24 +43,20 @@
|
|||||||
|
|
||||||
public class HiresModelManager {
|
public class HiresModelManager {
|
||||||
|
|
||||||
private Path fileRoot;
|
private final Path fileRoot;
|
||||||
private HiresModelRenderer renderer;
|
private final HiresModelRenderer renderer;
|
||||||
|
private final Grid tileGrid;
|
||||||
private Vector2i tileSize;
|
private final boolean useGzip;
|
||||||
private Vector2i gridOrigin;
|
|
||||||
|
public HiresModelManager(Path fileRoot, ResourcePack resourcePack, RenderSettings renderSettings, Grid tileGrid) {
|
||||||
private boolean useGzip;
|
this(fileRoot, new HiresModelRenderer(resourcePack, renderSettings), tileGrid, renderSettings.useGzipCompression());
|
||||||
|
|
||||||
public HiresModelManager(Path fileRoot, ResourcePack resourcePack, RenderSettings renderSettings, Vector2i tileSize) {
|
|
||||||
this(fileRoot, new HiresModelRenderer(resourcePack, renderSettings), tileSize, new Vector2i(2, 2), 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.fileRoot = fileRoot;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
|
|
||||||
this.tileSize = tileSize;
|
this.tileGrid = tileGrid;
|
||||||
this.gridOrigin = gridOrigin;
|
|
||||||
|
|
||||||
this.useGzip = useGzip;
|
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
|
* Renders the given world tile with the provided render-settings
|
||||||
*/
|
*/
|
||||||
public HiresModel render(WorldTile tile) {
|
public HiresModel render(World world, Vector2i tile) {
|
||||||
HiresModel model = renderer.render(tile, getTileRegion(tile));
|
Vector2i tileMin = tileGrid.getCellMin(tile);
|
||||||
save(model);
|
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;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save(final HiresModel model) {
|
private void save(final HiresModel model, Vector2i tile) {
|
||||||
final String modelJson = model.toBufferGeometry().toJson();
|
final String modelJson = model.toBufferGeometry().toJson();
|
||||||
save(model, modelJson);
|
save(modelJson, tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save(HiresModel model, String modelJson){
|
private void save(String modelJson, Vector2i tile){
|
||||||
File file = getFile(model.getTile(), useGzip);
|
File file = getFile(tile, useGzip);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileUtils.createFile(file);
|
FileUtils.createFile(file);
|
||||||
@ -104,14 +105,15 @@ private void save(HiresModel model, String modelJson){
|
|||||||
/**
|
/**
|
||||||
* Returns all tiles that the provided chunks are intersecting
|
* Returns all tiles that the provided chunks are intersecting
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Collection<Vector2i> getTilesForChunks(Iterable<Vector2i> chunks){
|
public Collection<Vector2i> getTilesForChunks(Iterable<Vector2i> chunks){
|
||||||
Set<Vector2i> tiles = new HashSet<>();
|
Set<Vector2i> tiles = new HashSet<>();
|
||||||
for (Vector2i chunk : chunks) {
|
for (Vector2i chunk : chunks) {
|
||||||
Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16);
|
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)
|
//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 x = 0; x < 15; x += tileGrid.getGridSize().getX()) {
|
||||||
for (int z = 0; z < 15; z += getTileSize().getY()) {
|
for (int z = 0; z < 15; z += tileGrid.getGridSize().getY()) {
|
||||||
tiles.add(posToTile(minBlockPos.add(x, 0, z)));
|
tiles.add(posToTile(minBlockPos.add(x, 0, z)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,52 +127,24 @@ public Collection<Vector2i> getTilesForChunks(Iterable<Vector2i> chunks){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the region of blocks that a tile includes
|
* Returns the tile-grid
|
||||||
*/
|
*/
|
||||||
public AABB getTileRegion(WorldTile tile) {
|
public Grid getTileGrid() {
|
||||||
Vector3i min = new Vector3i(
|
return tileGrid;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a block-position to a map-tile-coordinate
|
* Converts a block-position to a map-tile-coordinate
|
||||||
*/
|
*/
|
||||||
public Vector2i posToTile(Vector3i pos){
|
public Vector2i posToTile(Vector3i pos){
|
||||||
return posToTile(pos.toDouble());
|
return tileGrid.getCell(pos.toVector2(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a block-position to a map-tile-coordinate
|
* Converts a block-position to a map-tile-coordinate
|
||||||
*/
|
*/
|
||||||
public Vector2i posToTile(Vector3d pos){
|
public Vector2i posToTile(Vector3d pos){
|
||||||
pos = pos.sub(new Vector3d(gridOrigin.getX(), 0.0, gridOrigin.getY()));
|
return tileGrid.getCell(new Vector2i(pos.getFloorX(), pos.getFloorZ()));
|
||||||
return Vector2i.from(
|
|
||||||
(int) Math.floor(pos.getX() / getTileSize().getX()),
|
|
||||||
(int) Math.floor(pos.getZ() / getTileSize().getY())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
@ -22,19 +22,15 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.Vector3f;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import com.flowpowered.math.vector.Vector4f;
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
|
import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModel;
|
||||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModelFactory;
|
||||||
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.resourcepack.NoSuchResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
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.util.MathUtils;
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
@ -61,16 +57,11 @@ public HiresModelRenderer(ResourcePack resourcePack, RenderSettings renderSettin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HiresModel render(WorldTile tile, AABB region) {
|
public HiresModel render(World world, Vector3i modelMin, Vector3i modelMax) {
|
||||||
Vector3i modelMin = region.getMin();
|
|
||||||
Vector3i modelMax = region.getMax();
|
|
||||||
|
|
||||||
Vector3i min = modelMin.max(renderSettings.getMin());
|
Vector3i min = modelMin.max(renderSettings.getMin());
|
||||||
Vector3i max = modelMax.min(renderSettings.getMax());
|
Vector3i max = modelMax.min(renderSettings.getMax());
|
||||||
|
|
||||||
World world = tile.getWorld();
|
HiresModel model = new HiresModel(world.getUUID(), modelMin, modelMax);
|
||||||
|
|
||||||
HiresModel model = new HiresModel(tile.getWorld().getUUID(), tile.getTile(), modelMin, modelMax);
|
|
||||||
|
|
||||||
for (int x = min.getX(); x <= max.getX(); x++){
|
for (int x = min.getX(); x <= max.getX(); x++){
|
||||||
for (int z = min.getZ(); z <= max.getZ(); z++){
|
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);
|
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)){
|
if (block.getBlockState().getFullId().equals(grassId)){
|
||||||
float dx = (MathUtils.hashToFloat(x, y, z, 123984) - 0.5f) * 0.75f;
|
float dx = (MathUtils.hashToFloat(x, y, z, 123984) - 0.5f) * 0.75f;
|
||||||
float dz = (MathUtils.hashToFloat(x, y, z, 345542) - 0.5f) * 0.75f;
|
float dz = (MathUtils.hashToFloat(x, y, z, 345542) - 0.5f) * 0.75f;
|
@ -22,14 +22,14 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.render;
|
package de.bluecolored.bluemap.core.map.hires;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
public interface RenderSettings {
|
public interface RenderSettings {
|
||||||
|
|
||||||
static final Vector3i DEFAULT_MIN = Vector3i.from(Integer.MIN_VALUE);
|
Vector3i DEFAULT_MIN = Vector3i.from(Integer.MIN_VALUE);
|
||||||
static final Vector3i DEFAULT_MAX = Vector3i.from(Integer.MAX_VALUE);
|
Vector3i DEFAULT_MAX = Vector3i.from(Integer.MAX_VALUE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether faces that have a sky-light-value of 0 will be rendered or not.
|
* Whether faces that have a sky-light-value of 0 will be rendered or not.
|
||||||
@ -68,13 +68,4 @@ default boolean useGzipCompression() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default RenderSettings copy() {
|
|
||||||
return new StaticRenderSettings(
|
|
||||||
isExcludeFacesWithoutSunlight(),
|
|
||||||
getMin(),
|
|
||||||
getMax(),
|
|
||||||
isRenderEdges()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -22,7 +22,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.render.hires.blockmodel;
|
package de.bluecolored.bluemap.core.map.hires.blockmodel;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector4f;
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
|
|
@ -22,9 +22,9 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.BlockColorCalculator;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource;
|
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
@ -22,7 +22,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.render.hires.blockmodel;
|
package de.bluecolored.bluemap.core.map.hires.blockmodel;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
@ -35,7 +35,7 @@
|
|||||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||||
import de.bluecolored.bluemap.core.model.ExtendedFace;
|
import de.bluecolored.bluemap.core.model.ExtendedFace;
|
||||||
import de.bluecolored.bluemap.core.model.ExtendedModel;
|
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.BlockColorCalculator;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.Texture;
|
import de.bluecolored.bluemap.core.resourcepack.Texture;
|
@ -22,7 +22,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.TrigMath;
|
||||||
import com.flowpowered.math.imaginary.Complexf;
|
import com.flowpowered.math.imaginary.Complexf;
|
||||||
@ -34,7 +34,7 @@
|
|||||||
import com.flowpowered.math.vector.Vector4f;
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.model.ExtendedFace;
|
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.BlockColorCalculator;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource.Element.Rotation;
|
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);
|
Quaternionf rot = Quaternionf.fromAxesAnglesDeg(rotation.getX(), rotation.getY(), 0);
|
||||||
uvLockAngle = (int) rot.getAxesAnglesDeg().dot(faceDir.toVector().toFloat());
|
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;
|
if (rotation.getX() >= 180 && rotation.getY() != 90 && rotation.getY() != 270) uvLockAngle += 180;
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* 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.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3f;
|
import com.flowpowered.math.vector.Vector3f;
|
||||||
@ -33,21 +33,15 @@
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
public class LowresModel {
|
public class LowresModel {
|
||||||
|
|
||||||
private UUID world;
|
private final BufferGeometry model;
|
||||||
private Vector2i tilePos;
|
|
||||||
private BufferGeometry model;
|
|
||||||
|
|
||||||
private Map<Vector2i, LowresPoint> changes;
|
private Map<Vector2i, LowresPoint> changes;
|
||||||
|
|
||||||
private boolean hasUnsavedChanges;
|
private boolean hasUnsavedChanges;
|
||||||
@ -56,17 +50,13 @@ public class LowresModel {
|
|||||||
fileLock = new Object(),
|
fileLock = new Object(),
|
||||||
modelLock = new Object();
|
modelLock = new Object();
|
||||||
|
|
||||||
public LowresModel(UUID world, Vector2i tilePos, Vector2i gridSize) {
|
public LowresModel(Vector2i gridSize) {
|
||||||
this(
|
this(
|
||||||
world,
|
|
||||||
tilePos,
|
|
||||||
ModelUtils.makeGrid(gridSize).toBufferGeometry()
|
ModelUtils.makeGrid(gridSize).toBufferGeometry()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LowresModel(UUID world, Vector2i tilePos, BufferGeometry model) {
|
public LowresModel(BufferGeometry model) {
|
||||||
this.world = world;
|
|
||||||
this.tilePos = tilePos;
|
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
|
||||||
this.changes = new ConcurrentHashMap<>();
|
this.changes = new ConcurrentHashMap<>();
|
||||||
@ -134,7 +124,7 @@ public void flush(){
|
|||||||
if (changes.isEmpty()) return;
|
if (changes.isEmpty()) return;
|
||||||
|
|
||||||
Map<Vector2i, LowresPoint> points = changes;
|
Map<Vector2i, LowresPoint> points = changes;
|
||||||
changes = new HashMap<>();
|
changes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
float[] position = model.attributes.get("position").values();
|
float[] position = model.attributes.get("position").values();
|
||||||
float[] color = model.attributes.get("color").values();
|
float[] color = model.attributes.get("color").values();
|
||||||
@ -178,36 +168,12 @@ public BufferGeometry getBufferGeometry(){
|
|||||||
return model;
|
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
|
* a point on this lowres-model-grid
|
||||||
*/
|
*/
|
||||||
public class LowresPoint {
|
public static class LowresPoint {
|
||||||
private float height;
|
private final float height;
|
||||||
private Vector3f color;
|
private final Vector3f color;
|
||||||
|
|
||||||
public LowresPoint(float height, Vector3f color) {
|
public LowresPoint(float height, Vector3f color) {
|
||||||
this.height = height;
|
this.height = height;
|
@ -22,11 +22,11 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.render.lowres;
|
package de.bluecolored.bluemap.core.map.lowres;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.*;
|
import com.flowpowered.math.vector.*;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
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.threejs.BufferGeometry;
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
@ -47,19 +47,17 @@
|
|||||||
|
|
||||||
public class LowresModelManager {
|
public class LowresModelManager {
|
||||||
|
|
||||||
private Path fileRoot;
|
private final Path fileRoot;
|
||||||
|
private final Vector2i pointsPerLowresTile;
|
||||||
private Vector2i gridSize;
|
private final Vector2i pointsPerHiresTile;
|
||||||
private Vector2i pointsPerHiresTile;
|
private final boolean useGzip;
|
||||||
|
|
||||||
private Map<File, CachedModel> models;
|
private final Map<File, CachedModel> models;
|
||||||
|
|
||||||
private boolean useGzip;
|
|
||||||
|
|
||||||
public LowresModelManager(Path fileRoot, Vector2i gridSize, Vector2i pointsPerHiresTile, boolean useGzip) {
|
public LowresModelManager(Path fileRoot, Vector2i pointsPerLowresTile, Vector2i pointsPerHiresTile, boolean useGzip) {
|
||||||
this.fileRoot = fileRoot;
|
this.fileRoot = fileRoot;
|
||||||
|
|
||||||
this.gridSize = gridSize;
|
this.pointsPerLowresTile = pointsPerLowresTile;
|
||||||
this.pointsPerHiresTile = pointsPerHiresTile;
|
this.pointsPerHiresTile = pointsPerHiresTile;
|
||||||
|
|
||||||
models = new ConcurrentHashMap<>();
|
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) {
|
public void render(HiresModel hiresModel) {
|
||||||
Vector3i min = hiresModel.getBlockMin();
|
Vector3i min = hiresModel.getBlockMin();
|
||||||
@ -124,15 +122,15 @@ public void render(HiresModel hiresModel) {
|
|||||||
* Saves all unsaved changes to the models to disk
|
* Saves all unsaved changes to the models to disk
|
||||||
*/
|
*/
|
||||||
public synchronized void save(){
|
public synchronized void save(){
|
||||||
for (CachedModel model : models.values()){
|
for (Entry<File, CachedModel> entry : models.entrySet()){
|
||||||
saveModel(model);
|
saveModel(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
tidyUpModelCache();
|
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) {
|
public void update(UUID world, Vector2i point, float height, Vector3f color) {
|
||||||
Vector2i tile = pointToTile(point);
|
Vector2i tile = pointToTile(point);
|
||||||
@ -186,7 +184,7 @@ private LowresModel getModel(UUID world, Vector2i tile) {
|
|||||||
|
|
||||||
String json = IOUtils.toString(is, StandardCharsets.UTF_8);
|
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){
|
} catch (IllegalArgumentException | IOException ex){
|
||||||
Logger.global.logError("Failed to load lowres model: " + modelFile, 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){
|
if (model == null){
|
||||||
model = new CachedModel(world, tile, gridSize);
|
model = new CachedModel(pointsPerLowresTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
models.put(modelFile, model);
|
models.put(modelFile, model);
|
||||||
@ -227,18 +225,17 @@ public synchronized void tidyUpModelCache() {
|
|||||||
int size = entries.size();
|
int size = entries.size();
|
||||||
for (Entry<File, CachedModel> e : entries) {
|
for (Entry<File, CachedModel> e : entries) {
|
||||||
if (size > 10) {
|
if (size > 10) {
|
||||||
saveAndRemoveModel(e.getValue());
|
saveAndRemoveModel(e.getKey(), e.getValue());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.getValue().getCacheTime() > 120000) {
|
if (e.getValue().getCacheTime() > 120000) {
|
||||||
saveModel(e.getValue());
|
saveModel(e.getKey(), e.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void saveAndRemoveModel(CachedModel model) {
|
private synchronized void saveAndRemoveModel(File modelFile, CachedModel model) {
|
||||||
File modelFile = getFile(model.getTile(), useGzip);
|
|
||||||
models.remove(modelFile);
|
models.remove(modelFile);
|
||||||
try {
|
try {
|
||||||
model.save(modelFile, false, useGzip);
|
model.save(modelFile, false, useGzip);
|
||||||
@ -248,8 +245,7 @@ private synchronized void saveAndRemoveModel(CachedModel model) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveModel(CachedModel model) {
|
private void saveModel(File modelFile, CachedModel model) {
|
||||||
File modelFile = getFile(model.getTile(), useGzip);
|
|
||||||
try {
|
try {
|
||||||
model.save(modelFile, false, useGzip);
|
model.save(modelFile, false, useGzip);
|
||||||
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
||||||
@ -263,35 +259,35 @@ private void saveModel(CachedModel model) {
|
|||||||
private Vector2i pointToTile(Vector2i point){
|
private Vector2i pointToTile(Vector2i point){
|
||||||
return point
|
return point
|
||||||
.toDouble()
|
.toDouble()
|
||||||
.div(gridSize.toDouble())
|
.div(pointsPerLowresTile.toDouble())
|
||||||
.floor()
|
.floor()
|
||||||
.toInt();
|
.toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2i getPointRelativeToTile(Vector2i tile, Vector2i point){
|
private Vector2i getPointRelativeToTile(Vector2i tile, Vector2i point){
|
||||||
return point.sub(tile.mul(gridSize));
|
return point.sub(tile.mul(pointsPerLowresTile));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2i getTileSize() {
|
public Vector2i getTileSize() {
|
||||||
return gridSize;
|
return pointsPerLowresTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2i getPointsPerHiresTile() {
|
public Vector2i getPointsPerHiresTile() {
|
||||||
return pointsPerHiresTile;
|
return pointsPerHiresTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CachedModel extends LowresModel {
|
private static class CachedModel extends LowresModel {
|
||||||
|
|
||||||
private long cacheTime;
|
private long cacheTime;
|
||||||
|
|
||||||
public CachedModel(UUID world, Vector2i tilePos, BufferGeometry model) {
|
public CachedModel(BufferGeometry model) {
|
||||||
super(world, tilePos, model);
|
super(model);
|
||||||
|
|
||||||
cacheTime = System.currentTimeMillis();
|
cacheTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedModel(UUID world, Vector2i tilePos, Vector2i gridSize) {
|
public CachedModel(Vector2i gridSize) {
|
||||||
super(world, tilePos, gridSize);
|
super(gridSize);
|
||||||
|
|
||||||
cacheTime = System.currentTimeMillis();
|
cacheTime = System.currentTimeMillis();
|
||||||
}
|
}
|
@ -24,10 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.mca;
|
package de.bluecolored.bluemap.core.mca;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
||||||
import de.bluecolored.bluemap.core.world.Biome;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
@ -38,9 +35,13 @@
|
|||||||
import net.querz.nbt.NumberTag;
|
import net.querz.nbt.NumberTag;
|
||||||
import net.querz.nbt.mca.MCAUtil;
|
import net.querz.nbt.mca.MCAUtil;
|
||||||
|
|
||||||
public class ChunkAnvil112 extends Chunk {
|
import java.util.Arrays;
|
||||||
private BlockIdMapper blockIdMapper;
|
import java.util.function.IntFunction;
|
||||||
private BiomeMapper biomeIdMapper;
|
|
||||||
|
public class ChunkAnvil112 extends MCAChunk {
|
||||||
|
private final BiomeMapper biomeIdMapper;
|
||||||
|
private final BlockIdMapper blockIdMapper;
|
||||||
|
private final IntFunction<String> forgeBlockIdMapper;
|
||||||
|
|
||||||
private boolean isGenerated;
|
private boolean isGenerated;
|
||||||
private boolean hasLight;
|
private boolean hasLight;
|
||||||
@ -48,11 +49,12 @@ public class ChunkAnvil112 extends Chunk {
|
|||||||
private byte[] biomes;
|
private byte[] biomes;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ChunkAnvil112(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) {
|
public ChunkAnvil112(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper, BlockIdMapper blockIdMapper, IntFunction<String> forgeBlockIdMapper) {
|
||||||
super(world, chunkTag);
|
super(chunkTag);
|
||||||
|
|
||||||
blockIdMapper = getWorld().getBlockIdMapper();
|
this.blockIdMapper = blockIdMapper;
|
||||||
biomeIdMapper = getWorld().getBiomeIdMapper();
|
this.biomeIdMapper = biomeIdMapper;
|
||||||
|
this.forgeBlockIdMapper = forgeBlockIdMapper;
|
||||||
|
|
||||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||||
|
|
||||||
@ -118,9 +120,9 @@ public LightData getLightData(Vector3i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Biome getBiome(Vector3i pos) {
|
public Biome getBiome(int x, int y, int z) {
|
||||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
x = x & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||||
int z = pos.getZ() & 0xF;
|
z = z & 0xF;
|
||||||
int biomeByteIndex = z * 16 + x;
|
int biomeByteIndex = z * 16 + x;
|
||||||
|
|
||||||
return biomeIdMapper.get(biomes[biomeByteIndex] & 0xFF);
|
return biomeIdMapper.get(biomes[biomeByteIndex] & 0xFF);
|
||||||
@ -168,7 +170,7 @@ public BlockState getBlockState(Vector3i pos) {
|
|||||||
|
|
||||||
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
|
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
|
||||||
|
|
||||||
String forgeIdMapping = getWorld().getForgeBlockIdMapping(blockId);
|
String forgeIdMapping = forgeBlockIdMapper.apply(blockId);
|
||||||
if (forgeIdMapping != null) {
|
if (forgeIdMapping != null) {
|
||||||
return blockIdMapper.get(forgeIdMapping, blockId, blockData);
|
return blockIdMapper.get(forgeIdMapping, blockId, blockData);
|
||||||
} else {
|
} else {
|
||||||
@ -191,7 +193,7 @@ public String getBlockIdMeta(Vector3i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
|
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
|
||||||
String forgeIdMapping = getWorld().getForgeBlockIdMapping(blockId);
|
String forgeIdMapping = forgeBlockIdMapper.apply(blockId);
|
||||||
|
|
||||||
return blockId + ":" + blockData + " " + forgeIdMapping;
|
return blockId + ":" + blockData + " " + forgeIdMapping;
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.mca;
|
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 com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
||||||
import de.bluecolored.bluemap.core.world.Biome;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
@ -39,7 +33,12 @@
|
|||||||
import net.querz.nbt.*;
|
import net.querz.nbt.*;
|
||||||
import net.querz.nbt.mca.MCAUtil;
|
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 BiomeMapper biomeIdMapper;
|
||||||
|
|
||||||
private boolean isGenerated;
|
private boolean isGenerated;
|
||||||
@ -48,16 +47,16 @@ public class ChunkAnvil113 extends Chunk {
|
|||||||
private int[] biomes;
|
private int[] biomes;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ChunkAnvil113(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) {
|
public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) {
|
||||||
super(world, chunkTag);
|
super(chunkTag);
|
||||||
|
|
||||||
biomeIdMapper = getWorld().getBiomeIdMapper();
|
this.biomeIdMapper = biomeIdMapper;
|
||||||
|
|
||||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||||
|
|
||||||
String status = levelData.getString("Status");
|
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
|
this.isGenerated = status.equals("full");
|
||||||
hasLight = isGenerated;
|
this.hasLight = isGenerated;
|
||||||
|
|
||||||
if (!isGenerated && ignoreMissingLightData) {
|
if (!isGenerated && ignoreMissingLightData) {
|
||||||
isGenerated = !status.equals("empty");
|
isGenerated = !status.equals("empty");
|
||||||
@ -121,9 +120,9 @@ public LightData getLightData(Vector3i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Biome getBiome(Vector3i pos) {
|
public Biome getBiome(int x, int y, int z) {
|
||||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
x = x & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||||
int z = pos.getZ() & 0xF;
|
z = z & 0xF;
|
||||||
int biomeIntIndex = z * 16 + x;
|
int biomeIntIndex = z * 16 + x;
|
||||||
|
|
||||||
return biomeIdMapper.get(biomes[biomeIntIndex]);
|
return biomeIdMapper.get(biomes[biomeIntIndex]);
|
||||||
|
@ -24,13 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.mca;
|
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 com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
||||||
import de.bluecolored.bluemap.core.world.Biome;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
@ -39,7 +33,12 @@
|
|||||||
import net.querz.nbt.*;
|
import net.querz.nbt.*;
|
||||||
import net.querz.nbt.mca.MCAUtil;
|
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 BiomeMapper biomeIdMapper;
|
||||||
|
|
||||||
private boolean isGenerated;
|
private boolean isGenerated;
|
||||||
@ -48,21 +47,21 @@ public class ChunkAnvil115 extends Chunk {
|
|||||||
private int[] biomes;
|
private int[] biomes;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ChunkAnvil115(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) {
|
public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) {
|
||||||
super(world, chunkTag);
|
super(chunkTag);
|
||||||
|
|
||||||
biomeIdMapper = getWorld().getBiomeIdMapper();
|
this.biomeIdMapper = biomeIdMapper;
|
||||||
|
|
||||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||||
|
|
||||||
String status = levelData.getString("Status");
|
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
|
this.isGenerated = status.equals("full");
|
||||||
hasLight = isGenerated;
|
this.hasLight = isGenerated;
|
||||||
|
|
||||||
if (!isGenerated && ignoreMissingLightData) {
|
if (!isGenerated && ignoreMissingLightData) {
|
||||||
isGenerated = !status.equals("empty");
|
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?
|
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")) {
|
if (levelData.containsKey("Sections")) {
|
||||||
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
|
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
|
||||||
@ -81,7 +80,7 @@ public ChunkAnvil115(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tag instanceof IntArrayTag) {
|
else if (tag instanceof IntArrayTag) {
|
||||||
biomes = ((IntArrayTag) tag).getValue();
|
biomes = ((IntArrayTag) tag).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (biomes == null || biomes.length == 0) {
|
if (biomes == null || biomes.length == 0) {
|
||||||
@ -121,16 +120,16 @@ public LightData getLightData(Vector3i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Biome getBiome(Vector3i pos) {
|
public Biome getBiome(int x, int y, int z) {
|
||||||
int x = (pos.getX() & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
|
x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
|
||||||
int z = (pos.getZ() & 0xF) / 4;
|
z = (z & 0xF) / 4;
|
||||||
int y = pos.getY() / 4;
|
y = y / 4;
|
||||||
int biomeIntIndex = y * 16 + z * 4 + x;
|
int biomeIntIndex = y * 16 + z * 4 + x;
|
||||||
|
|
||||||
return biomeIdMapper.get(biomes[biomeIntIndex]);
|
return biomeIdMapper.get(biomes[biomeIntIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Section {
|
private static class Section {
|
||||||
private static final String AIR_ID = "minecraft:air";
|
private static final String AIR_ID = "minecraft:air";
|
||||||
|
|
||||||
private int sectionY;
|
private int sectionY;
|
||||||
|
@ -24,13 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.mca;
|
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 com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
||||||
import de.bluecolored.bluemap.core.world.Biome;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
@ -39,7 +33,12 @@
|
|||||||
import net.querz.nbt.*;
|
import net.querz.nbt.*;
|
||||||
import net.querz.nbt.mca.MCAUtil;
|
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 BiomeMapper biomeIdMapper;
|
||||||
|
|
||||||
private boolean isGenerated;
|
private boolean isGenerated;
|
||||||
@ -48,22 +47,22 @@ public class ChunkAnvil116 extends Chunk {
|
|||||||
private int[] biomes;
|
private int[] biomes;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ChunkAnvil116(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) {
|
public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) {
|
||||||
super(world, chunkTag);
|
super(chunkTag);
|
||||||
|
|
||||||
biomeIdMapper = getWorld().getBiomeIdMapper();
|
this.biomeIdMapper = biomeIdMapper;
|
||||||
|
|
||||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||||
|
|
||||||
String status = levelData.getString("Status");
|
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
|
this.isGenerated = status.equals("full");
|
||||||
hasLight = isGenerated;
|
this.hasLight = isGenerated;
|
||||||
|
|
||||||
if (!isGenerated && ignoreMissingLightData) {
|
if (!isGenerated && ignoreMissingLightData) {
|
||||||
isGenerated = !status.equals("empty");
|
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")) {
|
if (levelData.containsKey("Sections")) {
|
||||||
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
|
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
|
||||||
Section section = new Section(sectionTag);
|
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
|
Tag<?> tag = levelData.get("Biomes"); //tag can be byte-array or int-array
|
||||||
if (tag instanceof ByteArrayTag) {
|
if (tag instanceof ByteArrayTag) {
|
||||||
byte[] bs = ((ByteArrayTag) tag).getValue();
|
byte[] bs = ((ByteArrayTag) tag).getValue();
|
||||||
biomes = new int[bs.length];
|
this.biomes = new int[bs.length];
|
||||||
|
|
||||||
for (int i = 0; i < bs.length; i++) {
|
for (int i = 0; i < bs.length; i++) {
|
||||||
biomes[i] = bs[i] & 0xFF;
|
biomes[i] = bs[i] & 0xFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tag instanceof IntArrayTag) {
|
else if (tag instanceof IntArrayTag) {
|
||||||
biomes = ((IntArrayTag) tag).getValue();
|
this.biomes = ((IntArrayTag) tag).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (biomes == null || biomes.length == 0) {
|
if (biomes == null || biomes.length == 0) {
|
||||||
biomes = new int[1024];
|
this.biomes = new int[1024];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (biomes.length < 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
|
@Override
|
||||||
public Biome getBiome(Vector3i pos) {
|
public Biome getBiome(int x, int y, int z) {
|
||||||
int x = (pos.getX() & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
|
x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
|
||||||
int z = (pos.getZ() & 0xF) / 4;
|
z = (z & 0xF) / 4;
|
||||||
int y = pos.getY() / 4;
|
y = y / 4;
|
||||||
int biomeIntIndex = y * 16 + z * 4 + x;
|
int biomeIntIndex = y * 16 + z * 4 + x;
|
||||||
|
|
||||||
return biomeIdMapper.get(biomes[biomeIntIndex]);
|
return biomeIdMapper.get(biomes[biomeIntIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Section {
|
private static class Section {
|
||||||
private static final String AIR_ID = "minecraft:air";
|
private static final String AIR_ID = "minecraft:air";
|
||||||
|
|
||||||
private int sectionY;
|
private int sectionY;
|
||||||
|
@ -26,16 +26,13 @@
|
|||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.world.Biome;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
import de.bluecolored.bluemap.core.world.LightData;
|
import de.bluecolored.bluemap.core.world.LightData;
|
||||||
|
|
||||||
public class EmptyChunk extends Chunk {
|
public class EmptyChunk extends MCAChunk {
|
||||||
|
|
||||||
protected EmptyChunk(MCAWorld world, Vector2i chunkPos) {
|
public static final MCAChunk INSTANCE = new EmptyChunk();
|
||||||
super(world, chunkPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isGenerated() {
|
public boolean isGenerated() {
|
||||||
@ -53,7 +50,7 @@ public LightData getLightData(Vector3i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Biome getBiome(Vector3i pos) {
|
public Biome getBiome(int x, int y, int z) {
|
||||||
return Biome.DEFAULT;
|
return Biome.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,53 +24,30 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.mca;
|
package de.bluecolored.bluemap.core.mca;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.world.Biome;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
import de.bluecolored.bluemap.core.world.Chunk;
|
||||||
import de.bluecolored.bluemap.core.world.LightData;
|
import de.bluecolored.bluemap.core.world.LightData;
|
||||||
import net.querz.nbt.CompoundTag;
|
import net.querz.nbt.CompoundTag;
|
||||||
|
|
||||||
public abstract class Chunk {
|
import java.io.IOException;
|
||||||
|
|
||||||
private final MCAWorld world;
|
public abstract class MCAChunk implements Chunk {
|
||||||
private final Vector2i chunkPos;
|
|
||||||
|
|
||||||
private final int dataVersion;
|
private final int dataVersion;
|
||||||
|
|
||||||
protected Chunk(MCAWorld world, Vector2i chunkPos) {
|
protected MCAChunk() {
|
||||||
this.world = world;
|
|
||||||
this.chunkPos = chunkPos;
|
|
||||||
|
|
||||||
this.dataVersion = -1;
|
this.dataVersion = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Chunk(MCAWorld world, CompoundTag chunkTag) {
|
protected MCAChunk(CompoundTag chunkTag) {
|
||||||
this.world = world;
|
|
||||||
|
|
||||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
|
||||||
|
|
||||||
chunkPos = new Vector2i(
|
|
||||||
levelData.getInt("xPos"),
|
|
||||||
levelData.getInt("zPos")
|
|
||||||
);
|
|
||||||
|
|
||||||
dataVersion = chunkTag.getInt("DataVersion");
|
dataVersion = chunkTag.getInt("DataVersion");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public abstract boolean isGenerated();
|
public abstract boolean isGenerated();
|
||||||
|
|
||||||
public Vector2i getChunkPos() {
|
|
||||||
return chunkPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MCAWorld getWorld() {
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDataVersion() {
|
public int getDataVersion() {
|
||||||
return dataVersion;
|
return dataVersion;
|
||||||
}
|
}
|
||||||
@ -79,19 +56,19 @@ public int getDataVersion() {
|
|||||||
|
|
||||||
public abstract LightData getLightData(Vector3i pos);
|
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");
|
int version = chunkTag.getInt("DataVersion");
|
||||||
|
|
||||||
if (version < 1400) return new ChunkAnvil112(world, chunkTag, ignoreMissingLightData);
|
if (version < 1400) return new ChunkAnvil112(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper(), world.getBlockIdMapper(), world::getForgeBlockIdMapping);
|
||||||
if (version < 2200) return new ChunkAnvil113(world, chunkTag, ignoreMissingLightData);
|
if (version < 2200) return new ChunkAnvil113(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper());
|
||||||
if (version < 2500) return new ChunkAnvil115(world, chunkTag, ignoreMissingLightData);
|
if (version < 2500) return new ChunkAnvil115(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper());
|
||||||
return new ChunkAnvil116(world, chunkTag, ignoreMissingLightData);
|
return new ChunkAnvil116(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Chunk empty(MCAWorld world, Vector2i chunkPos) {
|
public static MCAChunk empty() {
|
||||||
return new EmptyChunk(world, chunkPos);
|
return EmptyChunk.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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<Vector2i> listChunks(long modifiedSince) {
|
||||||
|
List<Vector2i> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -42,26 +42,28 @@
|
|||||||
import net.querz.nbt.ListTag;
|
import net.querz.nbt.ListTag;
|
||||||
import net.querz.nbt.NBTUtil;
|
import net.querz.nbt.NBTUtil;
|
||||||
import net.querz.nbt.Tag;
|
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.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class MCAWorld implements World {
|
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 UUID uuid;
|
||||||
private final Path worldFolder;
|
private final Path worldFolder;
|
||||||
private final MinecraftVersion minecraftVersion;
|
private final MinecraftVersion minecraftVersion;
|
||||||
private String name;
|
private String name;
|
||||||
private int seaLevel;
|
|
||||||
private Vector3i spawnPoint;
|
private Vector3i spawnPoint;
|
||||||
|
|
||||||
private final LoadingCache<Vector2i, Chunk> chunkCache;
|
private final LoadingCache<Vector2i, MCARegion> regionCache;
|
||||||
|
private final LoadingCache<Vector2i, MCAChunk> chunkCache;
|
||||||
|
|
||||||
private BlockIdMapper blockIdMapper;
|
private BlockIdMapper blockIdMapper;
|
||||||
private BlockPropertiesMapper blockPropertiesMapper;
|
private BlockPropertiesMapper blockPropertiesMapper;
|
||||||
private BiomeMapper biomeMapper;
|
private BiomeMapper biomeMapper;
|
||||||
@ -70,15 +72,13 @@ public class MCAWorld implements World {
|
|||||||
|
|
||||||
private boolean ignoreMissingLightData;
|
private boolean ignoreMissingLightData;
|
||||||
|
|
||||||
private Map<Integer, String> forgeBlockMappings;
|
private final Map<Integer, String> forgeBlockMappings;
|
||||||
|
|
||||||
private MCAWorld(
|
private MCAWorld(
|
||||||
Path worldFolder,
|
Path worldFolder,
|
||||||
UUID uuid,
|
UUID uuid,
|
||||||
MinecraftVersion minecraftVersion,
|
MinecraftVersion minecraftVersion,
|
||||||
String name,
|
String name,
|
||||||
int worldHeight,
|
|
||||||
int seaLevel,
|
|
||||||
Vector3i spawnPoint,
|
Vector3i spawnPoint,
|
||||||
BlockIdMapper blockIdMapper,
|
BlockIdMapper blockIdMapper,
|
||||||
BlockPropertiesMapper blockPropertiesMapper,
|
BlockPropertiesMapper blockPropertiesMapper,
|
||||||
@ -89,7 +89,6 @@ private MCAWorld(
|
|||||||
this.worldFolder = worldFolder;
|
this.worldFolder = worldFolder;
|
||||||
this.minecraftVersion = minecraftVersion;
|
this.minecraftVersion = minecraftVersion;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.seaLevel = seaLevel;
|
|
||||||
this.spawnPoint = spawnPoint;
|
this.spawnPoint = spawnPoint;
|
||||||
|
|
||||||
this.blockIdMapper = blockIdMapper;
|
this.blockIdMapper = blockIdMapper;
|
||||||
@ -114,30 +113,33 @@ private MCAWorld(
|
|||||||
registerBlockStateExtension(new DoublePlantExtension(minecraftVersion));
|
registerBlockStateExtension(new DoublePlantExtension(minecraftVersion));
|
||||||
registerBlockStateExtension(new DoubleChestExtension());
|
registerBlockStateExtension(new DoubleChestExtension());
|
||||||
|
|
||||||
|
this.regionCache = Caffeine.newBuilder()
|
||||||
|
.executor(BlueMap.THREAD_POOL)
|
||||||
|
.maximumSize(100)
|
||||||
|
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||||
|
.build(this::loadRegion);
|
||||||
|
|
||||||
this.chunkCache = Caffeine.newBuilder()
|
this.chunkCache = Caffeine.newBuilder()
|
||||||
.executor(BlueMap.THREAD_POOL)
|
.executor(BlueMap.THREAD_POOL)
|
||||||
.maximumSize(500)
|
.maximumSize(500)
|
||||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||||
.build(chunkPos -> this.loadChunkOrEmpty(chunkPos, 2, 1000));
|
.build(this::loadChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockState getBlockState(Vector3i pos) {
|
public BlockState getBlockState(Vector3i pos) {
|
||||||
Vector2i chunkPos = blockToChunk(pos);
|
return getChunk(blockToChunk(pos)).getBlockState(pos);
|
||||||
Chunk chunk = getChunk(chunkPos);
|
|
||||||
return chunk.getBlockState(pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Biome getBiome(Vector3i pos) {
|
public Biome getBiome(int x, int y, int z) {
|
||||||
if (pos.getY() < getMinY()) {
|
if (y < getMinY()) {
|
||||||
pos = new Vector3i(pos.getX(), getMinY(), pos.getZ());
|
y = getMinY();
|
||||||
} else if (pos.getY() > getMaxY()) {
|
} else if (y > getMaxY()) {
|
||||||
pos = new Vector3i(pos.getX(), getMaxY(), pos.getZ());
|
y = getMaxY();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i chunkPos = blockToChunk(pos);
|
MCAChunk chunk = getChunk(x >> 4, z >> 4);
|
||||||
Chunk chunk = getChunk(chunkPos);
|
return chunk.getBiome(x, y, z);
|
||||||
return chunk.getBiome(pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -147,17 +149,16 @@ public Block getBlock(Vector3i pos) {
|
|||||||
} else if (pos.getY() > getMaxY()) {
|
} else if (pos.getY() > getMaxY()) {
|
||||||
return new Block(this, BlockState.AIR, LightData.SKY, Biome.DEFAULT, BlockProperties.TRANSPARENT, pos);
|
return new Block(this, BlockState.AIR, LightData.SKY, Biome.DEFAULT, BlockProperties.TRANSPARENT, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i chunkPos = blockToChunk(pos);
|
MCAChunk chunk = getChunk(blockToChunk(pos));
|
||||||
Chunk chunk = getChunk(chunkPos);
|
|
||||||
BlockState blockState = getExtendedBlockState(chunk, pos);
|
BlockState blockState = getExtendedBlockState(chunk, pos);
|
||||||
LightData lightData = chunk.getLightData(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);
|
BlockProperties properties = blockPropertiesMapper.get(blockState);
|
||||||
return new Block(this, blockState, lightData, biome, properties, pos);
|
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);
|
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
|
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;
|
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
|
@Override
|
||||||
public boolean isChunkGenerated(Vector2i chunkPos) {
|
public MCAChunk getChunk(int x, int z) {
|
||||||
Chunk chunk = getChunk(chunkPos);
|
return getChunk(new Vector2i(x, z));
|
||||||
return chunk.isGenerated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MCAChunk getChunk(Vector2i pos) {
|
||||||
|
return chunkCache.get(pos);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Vector2i> getChunkList(long modifiedSinceMillis, Predicate<Vector2i> filter){
|
public MCARegion getRegion(int x, int z) {
|
||||||
List<Vector2i> chunks = new ArrayList<>(10000);
|
return regionCache.get(new Vector2i(x, z));
|
||||||
|
}
|
||||||
if (!getRegionFolder().toFile().isDirectory()) return Collections.emptyList();
|
|
||||||
|
@Override
|
||||||
for (File file : getRegionFolder().toFile().listFiles()) {
|
public Collection<Vector2i> listRegions() {
|
||||||
|
File[] regionFiles = getRegionFolder().toFile().listFiles();
|
||||||
|
if (regionFiles == null) return Collections.emptyList();
|
||||||
|
|
||||||
|
List<Vector2i> regions = new ArrayList<>(regionFiles.length);
|
||||||
|
|
||||||
|
for (File file : regionFiles) {
|
||||||
if (!file.getName().endsWith(".mca")) continue;
|
if (!file.getName().endsWith(".mca")) continue;
|
||||||
if (file.length() <= 0) continue;
|
if (file.length() <= 0) continue;
|
||||||
|
|
||||||
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
|
|
||||||
|
|
||||||
|
try {
|
||||||
String[] filenameParts = file.getName().split("\\.");
|
String[] filenameParts = file.getName().split("\\.");
|
||||||
int rX = Integer.parseInt(filenameParts[1]);
|
int rX = Integer.parseInt(filenameParts[1]);
|
||||||
int rZ = Integer.parseInt(filenameParts[2]);
|
int rZ = Integer.parseInt(filenameParts[2]);
|
||||||
|
|
||||||
for (int x = 0; x < 32; x++) {
|
regions.add(new Vector2i(rX, rZ));
|
||||||
for (int z = 0; z < 32; z++) {
|
} catch (NumberFormatException ignore) {}
|
||||||
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() + ")");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunks;
|
return regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -318,7 +224,27 @@ public Path getSaveFolder() {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSeaLevel() {
|
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
|
@Override
|
||||||
@ -332,8 +258,8 @@ public void invalidateChunkCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidateChunkCache(Vector2i chunk) {
|
public void invalidateChunkCache(int x, int z) {
|
||||||
chunkCache.invalidate(chunk);
|
chunkCache.invalidate(new Vector2i(x, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -381,8 +307,8 @@ private Path getRegionFolder() {
|
|||||||
return worldFolder.resolve("region");
|
return worldFolder.resolve("region");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path getMCAFilePath(Vector2i region) {
|
private File getMCAFile(int regionX, int regionZ) {
|
||||||
return getRegionFolder().resolve(MCAUtil.createNameFromRegionLocation(region.getX(), region.getY()));
|
return getRegionFolder().resolve("r." + regionX + "." + regionZ + ".mca").toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerBlockStateExtension(BlockStateExtension extension) {
|
private void registerBlockStateExtension(BlockStateExtension extension) {
|
||||||
@ -390,7 +316,48 @@ private void registerBlockStateExtension(BlockStateExtension extension) {
|
|||||||
this.blockStateExtensions.put(id, 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 {
|
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);
|
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) {
|
if (name == null) {
|
||||||
name = levelData.getString("LevelName") + subDimensionName;
|
name = levelData.getString("LevelName") + subDimensionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
int worldHeight = 255;
|
|
||||||
int seaLevel = 63;
|
|
||||||
Vector3i spawnPoint = new Vector3i(
|
Vector3i spawnPoint = new Vector3i(
|
||||||
levelData.getInt("SpawnX"),
|
levelData.getInt("SpawnX"),
|
||||||
levelData.getInt("SpawnY"),
|
levelData.getInt("SpawnY"),
|
||||||
@ -435,9 +400,7 @@ public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion versio
|
|||||||
worldFolder,
|
worldFolder,
|
||||||
uuid,
|
uuid,
|
||||||
version,
|
version,
|
||||||
name,
|
name,
|
||||||
worldHeight,
|
|
||||||
seaLevel,
|
|
||||||
spawnPoint,
|
spawnPoint,
|
||||||
blockIdMapper,
|
blockIdMapper,
|
||||||
blockPropertiesMapper,
|
blockPropertiesMapper,
|
||||||
@ -469,22 +432,8 @@ public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion versio
|
|||||||
|
|
||||||
public static Vector2i blockToChunk(Vector3i pos) {
|
public static Vector2i blockToChunk(Vector3i pos) {
|
||||||
return new Vector2i(
|
return new Vector2i(
|
||||||
MCAUtil.blockToChunk(pos.getX()),
|
pos.getX() >> 4,
|
||||||
MCAUtil.blockToChunk(pos.getZ())
|
pos.getZ() >> 4
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) SpongePowered <https://www.spongepowered.org>
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* <p>This class is immutable, all objects returned are either new instances or
|
|
||||||
* itself.</p>
|
|
||||||
*/
|
|
||||||
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 + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -24,36 +24,39 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.webserver;
|
package de.bluecolored.bluemap.core.webserver;
|
||||||
|
|
||||||
import java.io.IOException;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
import java.io.*;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class HttpConnection implements Runnable {
|
public class HttpConnection implements Runnable {
|
||||||
|
|
||||||
private HttpRequestHandler handler;
|
private final HttpRequestHandler handler;
|
||||||
|
|
||||||
private ServerSocket server;
|
private final ServerSocket server;
|
||||||
private Socket connection;
|
private final Socket connection;
|
||||||
private InputStream in;
|
private final InputStream in;
|
||||||
private OutputStream out;
|
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.server = server;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.verbose = verbose;
|
this.verbose = verbose;
|
||||||
|
|
||||||
|
this.processingSemaphore = processingSemaphore;
|
||||||
|
|
||||||
if (isClosed()){
|
if (isClosed()){
|
||||||
throw new IOException("Socket already closed!");
|
throw new IOException("Socket already closed!");
|
||||||
@ -61,8 +64,8 @@ public HttpConnection(ServerSocket server, Socket connection, HttpRequestHandler
|
|||||||
|
|
||||||
connection.setSoTimeout((int) timeoutUnit.toMillis(timeout));
|
connection.setSoTimeout((int) timeoutUnit.toMillis(timeout));
|
||||||
|
|
||||||
in = this.connection.getInputStream();
|
in = new BufferedInputStream(this.connection.getInputStream());
|
||||||
out = this.connection.getOutputStream();
|
out = new BufferedOutputStream(this.connection.getOutputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -70,25 +73,30 @@ public void run() {
|
|||||||
while (!isClosed() && !server.isClosed()){
|
while (!isClosed() && !server.isClosed()){
|
||||||
try {
|
try {
|
||||||
HttpRequest request = acceptRequest();
|
HttpRequest request = acceptRequest();
|
||||||
HttpResponse response = handler.handle(request);
|
|
||||||
sendResponse(response);
|
try {
|
||||||
if (verbose) {
|
processingSemaphore.acquire();
|
||||||
log(request, response);
|
|
||||||
|
HttpResponse response = handler.handle(request);
|
||||||
|
sendResponse(response);
|
||||||
|
|
||||||
|
if (verbose) log(request, response);
|
||||||
|
} finally {
|
||||||
|
processingSemaphore.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (InvalidRequestException e){
|
} catch (InvalidRequestException e){
|
||||||
try {
|
try {
|
||||||
sendResponse(new HttpResponse(HttpStatusCode.BAD_REQUEST));
|
sendResponse(new HttpResponse(HttpStatusCode.BAD_REQUEST));
|
||||||
} catch (IOException e1) {}
|
} catch (IOException ignored) {}
|
||||||
break;
|
break;
|
||||||
} catch (SocketTimeoutException e) {
|
} catch (SocketTimeoutException | ConnectionClosedException | SocketException e) {
|
||||||
break;
|
|
||||||
} catch (SocketException e){
|
|
||||||
break;
|
|
||||||
} catch (ConnectionClosedException e){
|
|
||||||
break;
|
break;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Logger.global.logError("Unexpected error while processing a HttpRequest!", e);
|
Logger.global.logError("Unexpected error while processing a HttpRequest!", e);
|
||||||
break;
|
break;
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,33 +24,22 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.webserver;
|
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.ConnectionClosedException;
|
||||||
import de.bluecolored.bluemap.core.webserver.HttpConnection.InvalidRequestException;
|
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 {
|
public class HttpRequest {
|
||||||
|
|
||||||
private static final Pattern REQUEST_PATTERN = Pattern.compile("^(\\w+) (\\S+) (.+)$");
|
private static final Pattern REQUEST_PATTERN = Pattern.compile("^(\\w+) (\\S+) (.+)$");
|
||||||
|
|
||||||
private String method;
|
private String method;
|
||||||
private String adress;
|
private String address;
|
||||||
private String version;
|
private String version;
|
||||||
private Map<String, Set<String>> header;
|
private Map<String, Set<String>> header;
|
||||||
private Map<String, Set<String>> headerLC;
|
private Map<String, Set<String>> headerLC;
|
||||||
@ -60,9 +49,9 @@ public class HttpRequest {
|
|||||||
private Map<String, String> getParams = null;
|
private Map<String, String> getParams = null;
|
||||||
private String getParamString = null;
|
private String getParamString = null;
|
||||||
|
|
||||||
public HttpRequest(String method, String adress, String version, Map<String, Set<String>> header) {
|
public HttpRequest(String method, String address, String version, Map<String, Set<String>> header) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.adress = adress;
|
this.address = address;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.header = header;
|
this.header = header;
|
||||||
this.headerLC = new HashMap<>();
|
this.headerLC = new HashMap<>();
|
||||||
@ -83,8 +72,8 @@ public String getMethod() {
|
|||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAdress(){
|
public String getAddress(){
|
||||||
return adress;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVersion() {
|
public String getVersion() {
|
||||||
@ -127,7 +116,7 @@ public String getGETParamString() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void parseAdress() {
|
private void parseAdress() {
|
||||||
String adress = this.adress;
|
String adress = this.address;
|
||||||
if (adress.isEmpty()) adress = "/";
|
if (adress.isEmpty()) adress = "/";
|
||||||
String[] adressParts = adress.split("\\?", 2);
|
String[] adressParts = adress.split("\\?", 2);
|
||||||
String path = adressParts[0];
|
String path = adressParts[0];
|
||||||
@ -167,8 +156,8 @@ public static HttpRequest read(InputStream in) throws IOException, InvalidReques
|
|||||||
String method = m.group(1);
|
String method = m.group(1);
|
||||||
if (method == null) throw new InvalidRequestException();
|
if (method == null) throw new InvalidRequestException();
|
||||||
|
|
||||||
String adress = m.group(2);
|
String address = m.group(2);
|
||||||
if (adress == null) throw new InvalidRequestException();
|
if (address == null) throw new InvalidRequestException();
|
||||||
|
|
||||||
String version = m.group(3);
|
String version = m.group(3);
|
||||||
if (version == null) throw new InvalidRequestException();
|
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);
|
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")){
|
if (request.getLowercaseHeader("Transfer-Encoding").contains("chunked")){
|
||||||
try {
|
try {
|
||||||
|
@ -24,22 +24,13 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.webserver;
|
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 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 {
|
public class HttpResponse implements Closeable {
|
||||||
|
|
||||||
private String version;
|
private String version;
|
||||||
@ -57,22 +48,12 @@ public HttpResponse(HttpStatusCode statusCode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addHeader(String key, String value){
|
public void addHeader(String key, String value){
|
||||||
Set<String> valueSet = header.get(key);
|
Set<String> valueSet = header.computeIfAbsent(key, k -> new HashSet<>());
|
||||||
if (valueSet == null){
|
|
||||||
valueSet = new HashSet<>();
|
|
||||||
header.put(key, valueSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
valueSet.add(value);
|
valueSet.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeHeader(String key, String value){
|
public void removeHeader(String key, String value){
|
||||||
Set<String> valueSet = header.get(key);
|
Set<String> valueSet = header.computeIfAbsent(key, k -> new HashSet<>());
|
||||||
if (valueSet == null){
|
|
||||||
valueSet = new HashSet<>();
|
|
||||||
header.put(key, valueSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
valueSet.remove(value);
|
valueSet.remove(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ public class WebServer extends Thread {
|
|||||||
private final boolean verbose;
|
private final boolean verbose;
|
||||||
|
|
||||||
private final HttpRequestHandler handler;
|
private final HttpRequestHandler handler;
|
||||||
|
private final Semaphore processingSemaphore;
|
||||||
|
|
||||||
private ThreadPoolExecutor connectionThreads;
|
private ThreadPoolExecutor connectionThreads;
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ public WebServer(InetAddress bindAddress, int port, int maxConnections, HttpRequ
|
|||||||
this.verbose = verbose;
|
this.verbose = verbose;
|
||||||
|
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
|
this.processingSemaphore = new Semaphore(18);
|
||||||
|
|
||||||
connectionThreads = null;
|
connectionThreads = null;
|
||||||
}
|
}
|
||||||
@ -90,7 +92,7 @@ public void run(){
|
|||||||
Socket connection = server.accept();
|
Socket connection = server.accept();
|
||||||
|
|
||||||
try {
|
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){
|
} catch (RejectedExecutionException e){
|
||||||
connection.close();
|
connection.close();
|
||||||
Logger.global.logWarning("Dropped an incoming HttpConnection! (Too many connections?)");
|
Logger.global.logWarning("Dropped an incoming HttpConnection! (Too many connections?)");
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
*/
|
*/
|
||||||
public class BlockState {
|
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 AIR = new BlockState("minecraft:air", Collections.emptyMap());
|
||||||
public static final BlockState MISSING = new BlockState("bluemap:missing", Collections.emptyMap());
|
public static final BlockState MISSING = new BlockState("bluemap:missing", Collections.emptyMap());
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package de.bluecolored.bluemap.core.world;
|
||||||
|
|
||||||
|
public interface Chunk {
|
||||||
|
|
||||||
|
boolean isGenerated();
|
||||||
|
|
||||||
|
}
|
@ -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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.<br>
|
||||||
|
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
||||||
|
*/
|
||||||
|
default Collection<Vector2i> listChunks(){
|
||||||
|
return listChunks(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a collection of all chunks that have been modified at or after the specified timestamp.<br>
|
||||||
|
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
||||||
|
*/
|
||||||
|
Collection<Vector2i> 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();
|
||||||
|
|
||||||
|
}
|
@ -24,24 +24,22 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.world;
|
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.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Predicate;
|
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".
|
* A sliced view of a world. Everything outside the defined bounds is seen as "not generated" and "air".
|
||||||
*/
|
*/
|
||||||
public class SlicedWorld implements World {
|
public class SlicedWorld implements World {
|
||||||
|
|
||||||
private World world;
|
private final World world;
|
||||||
private Vector3i min;
|
private final Vector3i min;
|
||||||
private Vector3i max;
|
private final Vector3i max;
|
||||||
|
|
||||||
public SlicedWorld(World world, Vector3i min, Vector3i max) {
|
public SlicedWorld(World world, Vector3i min, Vector3i max) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
@ -83,7 +81,17 @@ public int getMaxY() {
|
|||||||
public int getMinY() {
|
public int getMinY() {
|
||||||
return world.getMinY();
|
return world.getMinY();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Grid getChunkGrid() {
|
||||||
|
return world.getChunkGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Grid getRegionGrid() {
|
||||||
|
return world.getRegionGrid();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Biome getBiome(Vector3i pos) {
|
public Biome getBiome(Vector3i pos) {
|
||||||
return world.getBiome(pos);
|
return world.getBiome(pos);
|
||||||
@ -106,45 +114,6 @@ public Block getBlock(int x, int y, int z) {
|
|||||||
block.setWorld(this);
|
block.setWorld(this);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Vector2i> getChunkList(long modifiedSince, Predicate<Vector2i> 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
|
@Override
|
||||||
public void invalidateChunkCache() {
|
public void invalidateChunkCache() {
|
||||||
@ -152,8 +121,8 @@ public void invalidateChunkCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidateChunkCache(Vector2i chunk) {
|
public void invalidateChunkCache(int x, int z) {
|
||||||
world.invalidateChunkCache(chunk);
|
world.invalidateChunkCache(x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -161,11 +130,6 @@ public void cleanUpChunkCache() {
|
|||||||
world.cleanUpChunkCache();
|
world.cleanUpChunkCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vector2i blockPosToChunkPos(Vector3i block) {
|
|
||||||
return world.blockPosToChunkPos(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInside(Vector3i blockPos) {
|
private boolean isInside(Vector3i blockPos) {
|
||||||
return isInside(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
return isInside(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||||
}
|
}
|
||||||
@ -180,19 +144,6 @@ private boolean isInside(int x, int y, int z) {
|
|||||||
y <= max.getY();
|
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) {
|
private Block createAirBlock(Vector3i pos) {
|
||||||
return new Block(
|
return new Block(
|
||||||
this,
|
this,
|
||||||
|
@ -24,17 +24,14 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.world;
|
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.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Predicate;
|
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<br>
|
* Represents a World on the Server<br>
|
||||||
* <br>
|
* <br>
|
||||||
@ -52,123 +49,60 @@ public interface World {
|
|||||||
|
|
||||||
Vector3i getSpawnPoint();
|
Vector3i getSpawnPoint();
|
||||||
|
|
||||||
default int getMaxY() {
|
int getMaxY();
|
||||||
return 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
default int getMinY() {
|
int getMinY();
|
||||||
return 0;
|
|
||||||
}
|
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);
|
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) {
|
default Block getBlock(int x, int y, int z) {
|
||||||
return getBlock(new Vector3i(x, y, z));
|
return getBlock(new Vector3i(x, y, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a collection of all generated chunks.<br>
|
|
||||||
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
|
||||||
*/
|
|
||||||
public default Collection<Vector2i> getChunkList(){
|
|
||||||
return getChunkList(0, c -> true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a filtered collection of all generated chunks.<br>
|
|
||||||
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
|
||||||
*/
|
|
||||||
public default Collection<Vector2i> getChunkList(Predicate<Vector2i> filter){
|
|
||||||
return getChunkList(0, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a collection of all chunks that have been modified at or after the specified timestamp.<br>
|
* Returns the {@link Chunk} on the specified chunk-position
|
||||||
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
|
||||||
*/
|
*/
|
||||||
public default Collection<Vector2i> getChunkList(long modifiedSince){
|
Chunk getChunk(int x, int z);
|
||||||
return getChunkList(modifiedSince, c -> true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a filtered collection of all chunks that have been modified at or after the specified timestamp.<br>
|
|
||||||
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
|
||||||
*/
|
|
||||||
public Collection<Vector2i> getChunkList(long modifiedSince, Predicate<Vector2i> filter);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* Returns a collection of all regions in this world.
|
||||||
* @param area The area to check
|
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public default boolean isAreaGenerated(AABB area) {
|
Collection<Vector2i> listRegions();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidates the complete chunk cache (if there is a cache), so that every chunk has to be reloaded from disk
|
* 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
|
* 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
|
* Cleans up invalid cache-entries to free up memory
|
||||||
*/
|
*/
|
||||||
public void cleanUpChunkCache();
|
void cleanUpChunkCache();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ChunkPosition for a BlockPosition
|
|
||||||
*/
|
|
||||||
public default Vector2i blockPosToChunkPos(Vector3i block) {
|
|
||||||
return new Vector2i(
|
|
||||||
block.getX() >> 4,
|
|
||||||
block.getZ() >> 4
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,10 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.world;
|
package de.bluecolored.bluemap.core.world;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class BlockStateTest {
|
public class BlockStateTest {
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,29 +24,24 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.cli;
|
package de.bluecolored.bluemap.cli;
|
||||||
|
|
||||||
import com.flowpowered.math.GenericMath;
|
import de.bluecolored.bluemap.common.BlueMapService;
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import de.bluecolored.bluemap.common.MissingResourcesException;
|
||||||
import de.bluecolored.bluemap.common.*;
|
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||||
import de.bluecolored.bluemap.core.BlueMap;
|
import de.bluecolored.bluemap.core.BlueMap;
|
||||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||||
import de.bluecolored.bluemap.core.config.WebServerConfig;
|
import de.bluecolored.bluemap.core.config.WebServerConfig;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.logger.LoggerLogger;
|
import de.bluecolored.bluemap.core.logger.LoggerLogger;
|
||||||
import de.bluecolored.bluemap.core.metrics.Metrics;
|
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.util.FileUtils;
|
||||||
import de.bluecolored.bluemap.core.web.FileRequestHandler;
|
import de.bluecolored.bluemap.common.web.FileRequestHandler;
|
||||||
import de.bluecolored.bluemap.core.web.WebSettings;
|
import de.bluecolored.bluemap.common.web.WebSettings;
|
||||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
|
||||||
import org.apache.commons.cli.*;
|
import org.apache.commons.cli.*;
|
||||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
import java.util.Collection;
|
import java.io.IOException;
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
public class BlueMapCLI {
|
public class BlueMapCLI {
|
||||||
|
|
||||||
@ -58,162 +53,9 @@ public void renderMaps(BlueMapService blueMap, boolean forceRender, boolean forc
|
|||||||
blueMap.createOrUpdateWebApp(forceGenerateWebapp);
|
blueMap.createOrUpdateWebApp(forceGenerateWebapp);
|
||||||
WebSettings webSettings = blueMap.updateWebAppSettings();
|
WebSettings webSettings = blueMap.updateWebAppSettings();
|
||||||
|
|
||||||
RenderManager renderManager = new RenderManager(blueMap.getCoreConfig().getRenderThreadCount());
|
RenderManager renderManager = new RenderManager();
|
||||||
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<Vector2i> 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<Vector2i> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2i renderCenter = map.getWorld().getSpawnPoint().toVector2(true);
|
//TODO
|
||||||
|
|
||||||
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!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOException {
|
public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOException {
|
||||||
|
@ -24,20 +24,11 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.fabric;
|
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 com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
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.PlayerJoinCallback;
|
||||||
import de.bluecolored.bluemap.fabric.events.PlayerLeaveCallback;
|
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.AttackBlockCallback;
|
||||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
@ -51,6 +42,11 @@
|
|||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class FabricEventForwarder {
|
public class FabricEventForwarder {
|
||||||
|
|
||||||
private FabricMod mod;
|
private FabricMod mod;
|
||||||
@ -59,9 +55,7 @@ public class FabricEventForwarder {
|
|||||||
public FabricEventForwarder(FabricMod mod) {
|
public FabricEventForwarder(FabricMod mod) {
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
this.eventListeners = new ArrayList<>(1);
|
this.eventListeners = new ArrayList<>(1);
|
||||||
|
|
||||||
WorldSaveCallback.EVENT.register(this::onWorldSave);
|
|
||||||
ChunkFinalizeCallback.EVENT.register(this::onChunkFinalize);
|
|
||||||
AttackBlockCallback.EVENT.register(this::onBlockAttack);
|
AttackBlockCallback.EVENT.register(this::onBlockAttack);
|
||||||
UseBlockCallback.EVENT.register(this::onBlockUse);
|
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) {
|
public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) {
|
||||||
if (this.mod.getServer() != server) return;
|
if (this.mod.getServer() != server) return;
|
||||||
|
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<ChunkFinalizeCallback> EVENT = EventFactory.createArrayBacked(ChunkFinalizeCallback.class,
|
|
||||||
(listeners) -> (world, chunkPos) -> {
|
|
||||||
for (ChunkFinalizeCallback event : listeners) {
|
|
||||||
event.onChunkFinalized(world, chunkPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
void onChunkFinalized(ServerWorld world, Vector2i chunkPos);
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<WorldSaveCallback> EVENT = EventFactory.createArrayBacked(WorldSaveCallback.class,
|
|
||||||
(listeners) -> (world) -> {
|
|
||||||
for (WorldSaveCallback event : listeners) {
|
|
||||||
event.onWorldSaved(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
void onWorldSaved(ServerWorld world);
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>>> ci) {
|
|
||||||
if (requiredStatus == ChunkStatus.FULL) {
|
|
||||||
ChunkFinalizeCallback.EVENT.invoker().onChunkFinalized(getWorld(), new Vector2i(holder.getPos().x, holder.getPos().z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -24,20 +24,11 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.fabric;
|
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 com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
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.PlayerJoinCallback;
|
||||||
import de.bluecolored.bluemap.fabric.events.PlayerLeaveCallback;
|
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.AttackBlockCallback;
|
||||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
@ -51,6 +42,11 @@
|
|||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class FabricEventForwarder {
|
public class FabricEventForwarder {
|
||||||
|
|
||||||
private FabricMod mod;
|
private FabricMod mod;
|
||||||
@ -59,9 +55,7 @@ public class FabricEventForwarder {
|
|||||||
public FabricEventForwarder(FabricMod mod) {
|
public FabricEventForwarder(FabricMod mod) {
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
this.eventListeners = new ArrayList<>(1);
|
this.eventListeners = new ArrayList<>(1);
|
||||||
|
|
||||||
WorldSaveCallback.EVENT.register(this::onWorldSave);
|
|
||||||
ChunkFinalizeCallback.EVENT.register(this::onChunkFinalize);
|
|
||||||
AttackBlockCallback.EVENT.register(this::onBlockAttack);
|
AttackBlockCallback.EVENT.register(this::onBlockAttack);
|
||||||
UseBlockCallback.EVENT.register(this::onBlockUse);
|
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) {
|
public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) {
|
||||||
if (this.mod.getServer() != server) return;
|
if (this.mod.getServer() != server) return;
|
||||||
|
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<ChunkFinalizeCallback> EVENT = EventFactory.createArrayBacked(ChunkFinalizeCallback.class,
|
|
||||||
(listeners) -> (world, chunkPos) -> {
|
|
||||||
for (ChunkFinalizeCallback event : listeners) {
|
|
||||||
event.onChunkFinalized(world, chunkPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
void onChunkFinalized(ServerWorld world, Vector2i chunkPos);
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<WorldSaveCallback> EVENT = EventFactory.createArrayBacked(WorldSaveCallback.class,
|
|
||||||
(listeners) -> (world) -> {
|
|
||||||
for (WorldSaveCallback event : listeners) {
|
|
||||||
event.onWorldSaved(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
void onWorldSaved(ServerWorld world);
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>>> ci) {
|
|
||||||
if (requiredStatus == ChunkStatus.FULL) {
|
|
||||||
ChunkFinalizeCallback.EVENT.invoker().onChunkFinalized(getWorld(), new Vector2i(holder.getPos().x, holder.getPos().z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -34,10 +34,8 @@
|
|||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
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.PlayerJoinCallback;
|
||||||
import de.bluecolored.bluemap.fabric.events.PlayerLeaveCallback;
|
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.AttackBlockCallback;
|
||||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
@ -59,9 +57,7 @@ public class FabricEventForwarder {
|
|||||||
public FabricEventForwarder(FabricMod mod) {
|
public FabricEventForwarder(FabricMod mod) {
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
this.eventListeners = new ArrayList<>(1);
|
this.eventListeners = new ArrayList<>(1);
|
||||||
|
|
||||||
WorldSaveCallback.EVENT.register(this::onWorldSave);
|
|
||||||
ChunkFinalizeCallback.EVENT.register(this::onChunkFinalize);
|
|
||||||
AttackBlockCallback.EVENT.register(this::onBlockAttack);
|
AttackBlockCallback.EVENT.register(this::onBlockAttack);
|
||||||
UseBlockCallback.EVENT.register(this::onBlockUse);
|
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) {
|
public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) {
|
||||||
if (this.mod.getServer() != server) return;
|
if (this.mod.getServer() != server) return;
|
||||||
|
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<ChunkFinalizeCallback> EVENT = EventFactory.createArrayBacked(ChunkFinalizeCallback.class,
|
|
||||||
(listeners) -> (world, chunkPos) -> {
|
|
||||||
for (ChunkFinalizeCallback event : listeners) {
|
|
||||||
event.onChunkFinalized(world, chunkPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
void onChunkFinalized(ServerWorld world, Vector2i chunkPos);
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<WorldSaveCallback> EVENT = EventFactory.createArrayBacked(WorldSaveCallback.class,
|
|
||||||
(listeners) -> (world) -> {
|
|
||||||
for (WorldSaveCallback event : listeners) {
|
|
||||||
event.onWorldSaved(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
void onWorldSaved(ServerWorld world);
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
|
||||||
* 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<CompletableFuture<Either<Chunk, ChunkHolder.Unloaded>>> ci) {
|
|
||||||
if (requiredStatus == ChunkStatus.FULL) {
|
|
||||||
ChunkFinalizeCallback.EVENT.invoker().onChunkFinalized(getWorld(), new Vector2i(holder.getPos().x, holder.getPos().z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -24,48 +24,31 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.forge;
|
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 com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
|
||||||
import net.minecraft.world.server.ServerWorld;
|
import net.minecraft.world.server.ServerWorld;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
|
||||||
import net.minecraftforge.event.world.BlockEvent;
|
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 net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ForgeEventForwarder {
|
public class ForgeEventForwarder {
|
||||||
|
|
||||||
private ForgeMod mod;
|
private ForgeMod mod;
|
||||||
private Collection<ServerEventListener> eventListeners;
|
private Collection<ServerEventListener> eventListeners;
|
||||||
|
|
||||||
private Deque<WorldChunk> loadChunkEvents;
|
|
||||||
private Thread loadChunkEventProcessor;
|
|
||||||
|
|
||||||
public ForgeEventForwarder(ForgeMod mod) {
|
public ForgeEventForwarder(ForgeMod mod) {
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
this.eventListeners = new ArrayList<>(1);
|
this.eventListeners = new ArrayList<>(1);
|
||||||
|
|
||||||
loadChunkEvents = new ConcurrentLinkedDeque<>();
|
|
||||||
|
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
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) {
|
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);
|
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
|
@SubscribeEvent
|
||||||
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
|
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||||
@ -143,64 +98,5 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) {
|
|||||||
UUID uuid = evt.getPlayer().getUniqueID();
|
UUID uuid = evt.getPlayer().getUniqueID();
|
||||||
for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,48 +24,31 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.forge;
|
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 com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
|
||||||
import net.minecraft.world.server.ServerWorld;
|
import net.minecraft.world.server.ServerWorld;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
|
||||||
import net.minecraftforge.event.world.BlockEvent;
|
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 net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ForgeEventForwarder {
|
public class ForgeEventForwarder {
|
||||||
|
|
||||||
private ForgeMod mod;
|
private ForgeMod mod;
|
||||||
private Collection<ServerEventListener> eventListeners;
|
private Collection<ServerEventListener> eventListeners;
|
||||||
|
|
||||||
private Deque<WorldChunk> loadChunkEvents;
|
|
||||||
private Thread loadChunkEventProcessor;
|
|
||||||
|
|
||||||
public ForgeEventForwarder(ForgeMod mod) {
|
public ForgeEventForwarder(ForgeMod mod) {
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
this.eventListeners = new ArrayList<>(1);
|
this.eventListeners = new ArrayList<>(1);
|
||||||
|
|
||||||
loadChunkEvents = new ConcurrentLinkedDeque<>();
|
|
||||||
|
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
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) {
|
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);
|
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
|
@SubscribeEvent
|
||||||
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
|
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||||
@ -143,64 +98,5 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) {
|
|||||||
UUID uuid = evt.getPlayer().getUniqueID();
|
UUID uuid = evt.getPlayer().getUniqueID();
|
||||||
for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,21 +51,11 @@ public class ForgeEventForwarder {
|
|||||||
private ForgeMod mod;
|
private ForgeMod mod;
|
||||||
private Collection<ServerEventListener> eventListeners;
|
private Collection<ServerEventListener> eventListeners;
|
||||||
|
|
||||||
private Deque<WorldChunk> loadChunkEvents;
|
|
||||||
private Thread loadChunkEventProcessor;
|
|
||||||
|
|
||||||
public ForgeEventForwarder(ForgeMod mod) {
|
public ForgeEventForwarder(ForgeMod mod) {
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
this.eventListeners = new ArrayList<>(1);
|
this.eventListeners = new ArrayList<>(1);
|
||||||
|
|
||||||
loadChunkEvents = new ConcurrentLinkedDeque<>();
|
|
||||||
|
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
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) {
|
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
|
@SubscribeEvent
|
||||||
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
|
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
|
||||||
UUID uuid = evt.getPlayer().getUniqueID();
|
UUID uuid = evt.getPlayer().getUniqueID();
|
||||||
@ -143,64 +105,5 @@ public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) {
|
|||||||
UUID uuid = evt.getPlayer().getUniqueID();
|
UUID uuid = evt.getPlayer().getUniqueID();
|
||||||
for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,23 +24,13 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.bukkit;
|
package de.bluecolored.bluemap.bukkit;
|
||||||
|
|
||||||
import java.io.File;
|
import de.bluecolored.bluemap.common.plugin.Plugin;
|
||||||
import java.io.IOException;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
||||||
import java.lang.reflect.Field;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import java.util.ArrayList;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
||||||
import java.util.Collection;
|
import de.bluecolored.bluemap.core.MinecraftVersion;
|
||||||
import java.util.Collections;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import java.util.List;
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||||
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 org.bstats.bukkit.MetricsLite;
|
import org.bstats.bukkit.MetricsLite;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
@ -53,13 +43,16 @@
|
|||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.Plugin;
|
import java.io.File;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.Player;
|
import java.io.IOException;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
import java.lang.reflect.Field;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerInterface;
|
import java.util.*;
|
||||||
import de.bluecolored.bluemap.core.MinecraftVersion;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
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 {
|
public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listener {
|
||||||
|
|
||||||
|
@ -24,35 +24,21 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.bukkit;
|
package de.bluecolored.bluemap.bukkit;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import java.util.Collection;
|
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
||||||
import java.util.UUID;
|
import de.bluecolored.bluemap.common.plugin.text.Text;
|
||||||
|
|
||||||
import org.bukkit.Chunk;
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.block.BlockBreakEvent;
|
import org.bukkit.event.block.*;
|
||||||
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.player.AsyncPlayerChatEvent;
|
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
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 java.util.ArrayList;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import java.util.Collection;
|
||||||
|
import java.util.UUID;
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
|
||||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
|
||||||
|
|
||||||
public class EventForwarder implements Listener {
|
public class EventForwarder implements Listener {
|
||||||
|
|
||||||
@ -70,19 +56,6 @@ public synchronized void removeAllListeners() {
|
|||||||
listeners.clear();
|
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)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public void onBlockChange(BlockPlaceEvent evt) {
|
public void onBlockChange(BlockPlaceEvent evt) {
|
||||||
onBlockChange(evt.getBlock().getLocation());
|
onBlockChange(evt.getBlock().getLocation());
|
||||||
@ -133,14 +106,6 @@ private synchronized void onBlockChange(Location loc) {
|
|||||||
Vector3i pos = new Vector3i(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
|
Vector3i pos = new Vector3i(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
|
||||||
for (ServerEventListener listener : listeners) listener.onBlockChange(world, pos);
|
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)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public synchronized void onPlayerJoin(PlayerJoinEvent evt) {
|
public synchronized void onPlayerJoin(PlayerJoinEvent evt) {
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.sponge;
|
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.block.BlockSnapshot;
|
||||||
import org.spongepowered.api.data.Transaction;
|
import org.spongepowered.api.data.Transaction;
|
||||||
import org.spongepowered.api.event.Listener;
|
import org.spongepowered.api.event.Listener;
|
||||||
@ -34,15 +34,9 @@
|
|||||||
import org.spongepowered.api.event.filter.type.Exclude;
|
import org.spongepowered.api.event.filter.type.Exclude;
|
||||||
import org.spongepowered.api.event.message.MessageChannelEvent;
|
import org.spongepowered.api.event.message.MessageChannelEvent;
|
||||||
import org.spongepowered.api.event.network.ClientConnectionEvent;
|
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 org.spongepowered.api.world.Location;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import java.util.Optional;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
|
||||||
|
|
||||||
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
|
|
||||||
import de.bluecolored.bluemap.common.plugin.text.Text;
|
|
||||||
|
|
||||||
public class EventForwarder {
|
public class EventForwarder {
|
||||||
|
|
||||||
@ -51,18 +45,6 @@ public class EventForwarder {
|
|||||||
public EventForwarder(ServerEventListener listener) {
|
public EventForwarder(ServerEventListener listener) {
|
||||||
this.listener = 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)
|
@Listener(order = Order.POST)
|
||||||
@Exclude({ChangeBlockEvent.Post.class, ChangeBlockEvent.Pre.class, ChangeBlockEvent.Modify.class})
|
@Exclude({ChangeBlockEvent.Post.class, ChangeBlockEvent.Pre.class, ChangeBlockEvent.Modify.class})
|
||||||
@ -71,18 +53,10 @@ public void onBlockChange(ChangeBlockEvent evt) {
|
|||||||
if(!tr.isValid()) continue;
|
if(!tr.isValid()) continue;
|
||||||
|
|
||||||
Optional<Location<org.spongepowered.api.world.World>> ow = tr.getFinal().getLocation();
|
Optional<Location<org.spongepowered.api.world.World>> ow = tr.getFinal().getLocation();
|
||||||
if (ow.isPresent()) {
|
ow.ifPresent(worldLocation -> listener.onBlockChange(worldLocation.getExtent().getUniqueId(), worldLocation.getPosition().toInt()));
|
||||||
listener.onBlockChange(ow.get().getExtent().getUniqueId(), ow.get().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)
|
@Listener(order = Order.POST)
|
||||||
public void onPlayerJoin(ClientConnectionEvent.Join evt) {
|
public void onPlayerJoin(ClientConnectionEvent.Join evt) {
|
||||||
listener.onPlayerJoin(evt.getTargetEntity().getUniqueId());
|
listener.onPlayerJoin(evt.getTargetEntity().getUniqueId());
|
||||||
|
Loading…
Reference in New Issue
Block a user