mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-12-18 06:58:15 +01:00
Merge branch 'master' into mc/1.12
This commit is contained in:
commit
9f79488e75
@ -104,7 +104,7 @@ private UUID getUUIDForWorldSync (File worldFolder) throws IOException {
|
|||||||
if (worldFolder.equals(world.getWorldFolder().getCanonicalFile())) return world.getUID();
|
if (worldFolder.equals(world.getWorldFolder().getCanonicalFile())) return world.getUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IOException("There is no world with this folder loaded: " + worldFolder.getCanonicalPath());
|
throw new IOException("There is no world with this folder loaded: " + worldFolder.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -24,7 +24,7 @@ renderThreadCount: -2
|
|||||||
|
|
||||||
# If this is true, BlueMap might send really basic metrics reports containg only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
# If this is true, BlueMap might send really basic metrics reports containg only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
||||||
# This allows me to track the basic usage of BlueMap and helps me stay motivated to further develop this tool! Please leave it on :)
|
# This allows me to track the basic usage of BlueMap and helps me stay motivated to further develop this tool! Please leave it on :)
|
||||||
# An example report looks like this: {"implementation":"CLI","version":"%version%"}
|
# An example report looks like this: {"implementation":"bukkit","version":"%version%"}
|
||||||
metrics: true
|
metrics: true
|
||||||
|
|
||||||
# The folder where bluemap saves data-files it needs during runtime or to save e.g. the render-progress to resume it later.
|
# The folder where bluemap saves data-files it needs during runtime or to save e.g. the render-progress to resume it later.
|
||||||
@ -75,6 +75,10 @@ maps: [
|
|||||||
# The path to the save-folder of the world to render
|
# The path to the save-folder of the world to render
|
||||||
world: "world"
|
world: "world"
|
||||||
|
|
||||||
|
# The position on the world where the map will be centered if you open it.
|
||||||
|
# This defaults to the world-spawn if you don't set it.
|
||||||
|
#startPos: [500, -820]
|
||||||
|
|
||||||
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
||||||
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||||
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
||||||
|
@ -24,8 +24,14 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.cli;
|
package de.bluecolored.bluemap.cli;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -36,6 +42,8 @@
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.CommandLineParser;
|
import org.apache.commons.cli.CommandLineParser;
|
||||||
@ -136,15 +144,14 @@ public void renderMaps() throws IOException {
|
|||||||
webSettings.setAllEnabled(false);
|
webSettings.setAllEnabled(false);
|
||||||
for (MapType map : maps.values()) {
|
for (MapType map : maps.values()) {
|
||||||
webSettings.setEnabled(true, map.getId());
|
webSettings.setEnabled(true, map.getId());
|
||||||
webSettings.setName(map.getName(), map.getId());
|
|
||||||
webSettings.setFrom(map.getTileRenderer(), map.getId());
|
webSettings.setFrom(map.getTileRenderer(), map.getId());
|
||||||
|
webSettings.setFrom(map.getWorld(), map.getId());
|
||||||
}
|
}
|
||||||
int ordinal = 0;
|
int ordinal = 0;
|
||||||
for (MapConfig map : config.getMapConfigs()) {
|
for (MapConfig map : config.getMapConfigs()) {
|
||||||
if (!maps.containsKey(map.getId())) continue; //don't add not loaded maps
|
if (!maps.containsKey(map.getId())) continue; //don't add not loaded maps
|
||||||
webSettings.setOrdinal(ordinal++, map.getId());
|
webSettings.setOrdinal(ordinal++, map.getId());
|
||||||
webSettings.setHiresViewDistance(map.getHiresViewDistance(), map.getId());
|
webSettings.setFrom(map, map.getId());
|
||||||
webSettings.setLowresViewDistance(map.getLowresViewDistance(), map.getId());
|
|
||||||
}
|
}
|
||||||
webSettings.save();
|
webSettings.save();
|
||||||
|
|
||||||
@ -153,11 +160,22 @@ public void renderMaps() throws IOException {
|
|||||||
resourcePack.saveTextureFile(textureExportFile);
|
resourcePack.saveTextureFile(textureExportFile);
|
||||||
|
|
||||||
RenderManager renderManager = new RenderManager(config.getRenderThreadCount());
|
RenderManager renderManager = new RenderManager(config.getRenderThreadCount());
|
||||||
renderManager.start();
|
File rmstate = new File(configFolder, "rmstate");
|
||||||
|
|
||||||
|
if (rmstate.exists()) {
|
||||||
|
try (
|
||||||
|
InputStream in = new GZIPInputStream(new FileInputStream(rmstate));
|
||||||
|
DataInputStream din = new DataInputStream(in);
|
||||||
|
){
|
||||||
|
renderManager.readState(din, maps.values());
|
||||||
|
Logger.global.logInfo("Found unfinished render, continuing ... (If you want to start a new render, delete the this file: " + rmstate.getCanonicalPath());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.global.logError("Failed to read saved render-state! Remove the file " + rmstate.getCanonicalPath() + " to start a new render.", ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (MapType map : maps.values()) {
|
for (MapType map : maps.values()) {
|
||||||
Logger.global.logInfo("Rendering map '" + map.getId() + "' ...");
|
Logger.global.logInfo("Creating render-task for map '" + map.getId() + "' ...");
|
||||||
Logger.global.logInfo("Collecting tiles to render...");
|
Logger.global.logInfo("Collecting tiles ...");
|
||||||
|
|
||||||
Collection<Vector2i> chunks;
|
Collection<Vector2i> chunks;
|
||||||
if (!forceRender) {
|
if (!forceRender) {
|
||||||
@ -178,60 +196,81 @@ public void renderMaps() throws IOException {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.global.logInfo("Starting Render...");
|
RenderTask task = new RenderTask(map.getName(), map);
|
||||||
long starttime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
RenderTask task = new RenderTask("Map-Render: " + map.getName(), map);
|
|
||||||
task.addTiles(tiles);
|
task.addTiles(tiles);
|
||||||
task.optimizeQueue();
|
task.optimizeQueue();
|
||||||
|
|
||||||
renderManager.addRenderTask(task);
|
renderManager.addRenderTask(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
long lastLogUpdate = System.currentTimeMillis();
|
Logger.global.logInfo("Starting render ...");
|
||||||
long lastSave = lastLogUpdate;
|
renderManager.start();
|
||||||
|
|
||||||
while(!task.isFinished()) {
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long lastLogUpdate = startTime;
|
||||||
|
long lastSave = startTime;
|
||||||
|
|
||||||
|
while(renderManager.getRenderTaskCount() != 0) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
} catch (InterruptedException e) {}
|
} catch (InterruptedException e) {}
|
||||||
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
if (lastLogUpdate < now - 10000) { // print update all 10 seconds
|
if (lastLogUpdate < now - 10000) { // print update all 10 seconds
|
||||||
|
RenderTask currentTask = renderManager.getCurrentRenderTask();
|
||||||
lastLogUpdate = now;
|
lastLogUpdate = now;
|
||||||
long time = task.getActiveTime();
|
long time = currentTask.getActiveTime();
|
||||||
|
|
||||||
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
||||||
int tileCount = task.getRemainingTileCount() + task.getRenderedTileCount();
|
int tileCount = currentTask.getRemainingTileCount() + currentTask.getRenderedTileCount();
|
||||||
double pct = (double)task.getRenderedTileCount() / (double) tileCount;
|
double pct = (double)currentTask.getRenderedTileCount() / (double) tileCount;
|
||||||
|
|
||||||
long ert = (long)((time / pct) * (1d - pct));
|
long ert = (long)((time / pct) * (1d - pct));
|
||||||
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
||||||
|
|
||||||
double tps = task.getRenderedTileCount() / (time / 1000.0);
|
double tps = currentTask.getRenderedTileCount() / (time / 1000.0);
|
||||||
|
|
||||||
Logger.global.logInfo("Rendered " + task.getRenderedTileCount() + " of " + tileCount + " tiles in " + durationString + " | " + GenericMath.round(tps, 3) + " tiles/s");
|
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);
|
Logger.global.logInfo(GenericMath.round(pct * 100, 3) + "% | Estimated remaining time: " + ertDurationString);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastSave < now - 5 * 60000) { // save every 5 minutes
|
if (lastSave < now - 1 * 60000) { // save every minute
|
||||||
|
RenderTask currentTask = renderManager.getCurrentRenderTask();
|
||||||
|
|
||||||
lastSave = now;
|
lastSave = now;
|
||||||
map.getTileRenderer().save();
|
currentTask.getMapType().getTileRenderer().save();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
map.getTileRenderer().save();
|
try (
|
||||||
|
OutputStream os = new GZIPOutputStream(new FileOutputStream(rmstate));
|
||||||
try {
|
DataOutputStream dos = new DataOutputStream(os);
|
||||||
webSettings.set(starttime, map.getId(), "last-render");
|
){
|
||||||
webSettings.save();
|
renderManager.writeState(dos);
|
||||||
} catch (IOException e) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logError("Failed to update web-settings!", e);
|
Logger.global.logError("Failed to save render-state!", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderManager.stop();
|
renderManager.stop();
|
||||||
|
|
||||||
|
//render finished, so remove render state file
|
||||||
|
rmstate.delete();
|
||||||
|
|
||||||
|
for (MapType map : maps.values()) {
|
||||||
|
webSettings.set(startTime, map.getId(), "last-render");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
webSettings.save();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.global.logError("Failed to update web-settings!", e);
|
||||||
|
}
|
||||||
|
|
||||||
Logger.global.logInfo("Waiting for all threads to quit ...");
|
Logger.global.logInfo("Waiting for all threads to quit ...");
|
||||||
if (!ForkJoinPool.commonPool().awaitQuiescence(30, TimeUnit.SECONDS)) {
|
if (!ForkJoinPool.commonPool().awaitQuiescence(30, TimeUnit.SECONDS)) {
|
||||||
Logger.global.logWarning("Some save-threads are taking very long to exit (>30s), they will be ignored.");
|
Logger.global.logWarning("Some save-threads are taking very long to exit (>30s), they will be ignored.");
|
||||||
|
@ -24,7 +24,7 @@ renderThreadCount: 0
|
|||||||
|
|
||||||
# If this is true, BlueMap might send really basic metrics reports containg only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
# If this is true, BlueMap might send really basic metrics reports containg only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
||||||
# This allows me to track the basic usage of BlueMap and helps me stay motivated to further develop this tool! Please leave it on :)
|
# This allows me to track the basic usage of BlueMap and helps me stay motivated to further develop this tool! Please leave it on :)
|
||||||
# An example report looks like this: {"implementation":"CLI","version":"%version%"}
|
# An example report looks like this: {"implementation":"cli","version":"%version%"}
|
||||||
metrics: true
|
metrics: true
|
||||||
|
|
||||||
# The folder where bluemap saves data-files it needs during runtime
|
# The folder where bluemap saves data-files it needs during runtime
|
||||||
@ -74,6 +74,10 @@ maps: [
|
|||||||
# The path to the save-folder of the world to render
|
# The path to the save-folder of the world to render
|
||||||
world: "world"
|
world: "world"
|
||||||
|
|
||||||
|
# The position on the world where the map will be centered if you open it.
|
||||||
|
# This defaults to the world-spawn if you don't set it.
|
||||||
|
#startPos: [500, -820]
|
||||||
|
|
||||||
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
||||||
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||||
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
||||||
|
@ -127,11 +127,13 @@ private void renderThread() {
|
|||||||
RenderTask task = renderTasks.peek();
|
RenderTask task = renderTasks.peek();
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
ticket = task.poll();
|
ticket = task.poll();
|
||||||
if (task.isFinished()) renderTasks.poll();
|
if (task.isFinished()) {
|
||||||
|
renderTasks.poll();
|
||||||
task.getMapType().getTileRenderer().save();
|
task.getMapType().getTileRenderer().save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ticket != null) {
|
if (ticket != null) {
|
||||||
try {
|
try {
|
||||||
@ -155,8 +157,18 @@ public int getQueueSize() {
|
|||||||
* Returns a copy of the deque with the render tasks in order as array
|
* Returns a copy of the deque with the render tasks in order as array
|
||||||
*/
|
*/
|
||||||
public RenderTask[] getRenderTasks(){
|
public RenderTask[] getRenderTasks(){
|
||||||
|
synchronized (renderTasks) {
|
||||||
return renderTasks.toArray(new RenderTask[renderTasks.size()]);
|
return renderTasks.toArray(new RenderTask[renderTasks.size()]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRenderTaskCount(){
|
||||||
|
return renderTasks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderTask getCurrentRenderTask() {
|
||||||
|
return renderTasks.peek();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRunning() {
|
public boolean isRunning() {
|
||||||
return running;
|
return running;
|
||||||
|
@ -69,6 +69,7 @@ public class Plugin {
|
|||||||
private RenderManager renderManager;
|
private RenderManager renderManager;
|
||||||
private BlueMapWebServer webServer;
|
private BlueMapWebServer webServer;
|
||||||
|
|
||||||
|
private Thread periodicalSaveThread;
|
||||||
private Thread metricsThread;
|
private Thread metricsThread;
|
||||||
|
|
||||||
private boolean loaded = false;
|
private boolean loaded = false;
|
||||||
@ -218,6 +219,23 @@ public synchronized void load() throws IOException, ParseResourceException {
|
|||||||
Logger.global.logError("Failed to load render-manager state!", ex);
|
Logger.global.logError("Failed to load render-manager state!", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//create periodical-save thread
|
||||||
|
periodicalSaveThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
Thread.sleep(TimeUnit.MINUTES.toMillis(5));
|
||||||
|
try {
|
||||||
|
saveRenderManagerState();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.global.logError("Failed to save render-manager state!", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
periodicalSaveThread.start();
|
||||||
|
|
||||||
//start map updater
|
//start map updater
|
||||||
this.updateHandler = new MapUpdateHandler();
|
this.updateHandler = new MapUpdateHandler();
|
||||||
serverInterface.registerListener(updateHandler);
|
serverInterface.registerListener(updateHandler);
|
||||||
@ -232,15 +250,14 @@ public synchronized void load() throws IOException, ParseResourceException {
|
|||||||
webSettings.setAllEnabled(false);
|
webSettings.setAllEnabled(false);
|
||||||
for (MapType map : maps.values()) {
|
for (MapType map : maps.values()) {
|
||||||
webSettings.setEnabled(true, map.getId());
|
webSettings.setEnabled(true, map.getId());
|
||||||
webSettings.setName(map.getName(), map.getId());
|
|
||||||
webSettings.setFrom(map.getTileRenderer(), map.getId());
|
webSettings.setFrom(map.getTileRenderer(), map.getId());
|
||||||
|
webSettings.setFrom(map.getWorld(), map.getId());
|
||||||
}
|
}
|
||||||
int ordinal = 0;
|
int ordinal = 0;
|
||||||
for (MapConfig map : config.getMapConfigs()) {
|
for (MapConfig map : config.getMapConfigs()) {
|
||||||
if (!maps.containsKey(map.getId())) continue; //don't add not loaded maps
|
if (!maps.containsKey(map.getId())) continue; //don't add not loaded maps
|
||||||
webSettings.setOrdinal(ordinal++, map.getId());
|
webSettings.setOrdinal(ordinal++, map.getId());
|
||||||
webSettings.setHiresViewDistance(map.getHiresViewDistance(), map.getId());
|
webSettings.setFrom(map, map.getId());
|
||||||
webSettings.setLowresViewDistance(map.getLowresViewDistance(), map.getId());
|
|
||||||
}
|
}
|
||||||
webSettings.save();
|
webSettings.save();
|
||||||
|
|
||||||
@ -278,6 +295,9 @@ public synchronized void unload() {
|
|||||||
if (metricsThread != null) metricsThread.interrupt();
|
if (metricsThread != null) metricsThread.interrupt();
|
||||||
metricsThread = null;
|
metricsThread = null;
|
||||||
|
|
||||||
|
if (periodicalSaveThread != null) periodicalSaveThread.interrupt();
|
||||||
|
periodicalSaveThread = null;
|
||||||
|
|
||||||
//stop services
|
//stop services
|
||||||
if (renderManager != null) renderManager.stop();
|
if (renderManager != null) renderManager.stop();
|
||||||
if (webServer != null) webServer.close();
|
if (webServer != null) webServer.close();
|
||||||
@ -286,14 +306,7 @@ public synchronized void unload() {
|
|||||||
if (updateHandler != null) updateHandler.flushTileBuffer(); //first write all buffered tiles to the render manager to save them too
|
if (updateHandler != null) updateHandler.flushTileBuffer(); //first write all buffered tiles to the render manager to save them too
|
||||||
if (renderManager != null) {
|
if (renderManager != null) {
|
||||||
try {
|
try {
|
||||||
File saveFile = config.getDataPath().resolve("rmstate").toFile();
|
saveRenderManagerState();
|
||||||
saveFile.getParentFile().mkdirs();
|
|
||||||
if (saveFile.exists()) saveFile.delete();
|
|
||||||
saveFile.createNewFile();
|
|
||||||
|
|
||||||
try (DataOutputStream out = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(saveFile)))) {
|
|
||||||
renderManager.writeState(out);
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logError("Failed to save render-manager state!", ex);
|
Logger.global.logError("Failed to save render-manager state!", ex);
|
||||||
}
|
}
|
||||||
@ -316,6 +329,17 @@ public synchronized void unload() {
|
|||||||
loaded = false;
|
loaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveRenderManagerState() throws IOException {
|
||||||
|
File saveFile = config.getDataPath().resolve("rmstate").toFile();
|
||||||
|
saveFile.getParentFile().mkdirs();
|
||||||
|
if (saveFile.exists()) saveFile.delete();
|
||||||
|
saveFile.createNewFile();
|
||||||
|
|
||||||
|
try (DataOutputStream out = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(saveFile)))) {
|
||||||
|
renderManager.writeState(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void reload() throws IOException, ParseResourceException {
|
public synchronized void reload() throws IOException, ParseResourceException {
|
||||||
unload();
|
unload();
|
||||||
load();
|
load();
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
@ -194,6 +195,8 @@ public class MapConfig implements RenderSettings {
|
|||||||
private String name;
|
private String name;
|
||||||
private String world;
|
private String world;
|
||||||
|
|
||||||
|
private Vector2i startPos;
|
||||||
|
|
||||||
private boolean renderCaves;
|
private boolean renderCaves;
|
||||||
private float ambientOcclusion;
|
private float ambientOcclusion;
|
||||||
private float lighting;
|
private float lighting;
|
||||||
@ -219,6 +222,8 @@ private MapConfig(ConfigurationNode node) throws IOException {
|
|||||||
this.world = node.getNode("world").getString("");
|
this.world = node.getNode("world").getString("");
|
||||||
if (world.isEmpty()) throw new IOException("Invalid configuration: Node maps[?].world is not defined");
|
if (world.isEmpty()) throw new IOException("Invalid configuration: Node maps[?].world is not defined");
|
||||||
|
|
||||||
|
if (!node.getNode("startPos").isVirtual()) this.startPos = ConfigUtils.readVector2i(node.getNode("startPos"));
|
||||||
|
|
||||||
this.renderCaves = node.getNode("renderCaves").getBoolean(false);
|
this.renderCaves = node.getNode("renderCaves").getBoolean(false);
|
||||||
this.ambientOcclusion = node.getNode("ambientOcclusion").getFloat(0.25f);
|
this.ambientOcclusion = node.getNode("ambientOcclusion").getFloat(0.25f);
|
||||||
this.lighting = node.getNode("lighting").getFloat(0.8f);
|
this.lighting = node.getNode("lighting").getFloat(0.8f);
|
||||||
@ -234,7 +239,7 @@ private MapConfig(ConfigurationNode node) throws IOException {
|
|||||||
|
|
||||||
this.renderEdges = node.getNode("renderEdges").getBoolean(true);
|
this.renderEdges = node.getNode("renderEdges").getBoolean(true);
|
||||||
|
|
||||||
this.renderEdges = node.getNode("useCompression").getBoolean(true);
|
this.useGzip = node.getNode("useCompression").getBoolean(true);
|
||||||
|
|
||||||
this.hiresTileSize = node.getNode("hires", "tileSize").getInt(32);
|
this.hiresTileSize = node.getNode("hires", "tileSize").getInt(32);
|
||||||
this.hiresViewDistance = node.getNode("hires", "viewDistance").getFloat(4.5f);
|
this.hiresViewDistance = node.getNode("hires", "viewDistance").getFloat(4.5f);
|
||||||
@ -260,6 +265,10 @@ public String getWorldPath() {
|
|||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector2i getStartPos() {
|
||||||
|
return startPos;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRenderCaves() {
|
public boolean isRenderCaves() {
|
||||||
return renderCaves;
|
return renderCaves;
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,7 @@ public HiresModel render(WorldTile tile, AABB region) {
|
|||||||
e.addSuppressed(e2);
|
e.addSuppressed(e2);
|
||||||
blockModel = new BlockStateModel();
|
blockModel = new BlockStateModel();
|
||||||
}
|
}
|
||||||
|
//Logger.global.noFloodDebug(block.getBlockState().getFullId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlockState() + " (" + e.toString() + ")");
|
||||||
Logger.global.noFloodDebug(block.getBlockState().getFullId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlockState() + " (" + e.toString() + ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blockModel.translate(new Vector3f(x, y, z).sub(modelMin.toFloat()));
|
blockModel.translate(new Vector3f(x, y, z).sub(modelMin.toFloat()));
|
||||||
|
@ -66,7 +66,7 @@ public Collection<TransformedBlockModelResource> getModels(BlockState blockState
|
|||||||
Variant allMatch = null;
|
Variant allMatch = null;
|
||||||
for (Variant variant : variants) {
|
for (Variant variant : variants) {
|
||||||
if (variant.condition.matches(blockState)) {
|
if (variant.condition.matches(blockState)) {
|
||||||
if (variant.condition instanceof All) { //only use "all" conditioned if nothing else matched
|
if (variant.condition instanceof All) { //only use "all" condition if nothing else matched
|
||||||
if (allMatch == null) allMatch = variant;
|
if (allMatch == null) allMatch = variant;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -106,16 +106,21 @@ private Variant() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TransformedBlockModelResource getModel(Vector3i pos) {
|
public TransformedBlockModelResource getModel(Vector3i pos) {
|
||||||
|
if (models.isEmpty()) throw new IllegalStateException("A variant must have at least one model!");
|
||||||
|
|
||||||
double selection = MathUtils.hashToFloat(pos, 827364) * totalWeight; // random based on position
|
double selection = MathUtils.hashToFloat(pos, 827364) * totalWeight; // random based on position
|
||||||
for (Weighted<TransformedBlockModelResource> w : models) {
|
for (Weighted<TransformedBlockModelResource> w : models) {
|
||||||
selection -= w.weight;
|
selection -= w.weight;
|
||||||
if (selection < 0)
|
if (selection <= 0) return w.value;
|
||||||
return w.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new RuntimeException("This line should never be reached!");
|
throw new RuntimeException("This line should never be reached!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void checkValid() throws ParseResourceException {
|
||||||
|
if (models.isEmpty()) throw new ParseResourceException("A variant must have at least one model!");
|
||||||
|
}
|
||||||
|
|
||||||
public void updateTotalWeight() {
|
public void updateTotalWeight() {
|
||||||
totalWeight = 0d;
|
totalWeight = 0d;
|
||||||
for (Weighted<?> w : models) {
|
for (Weighted<?> w : models) {
|
||||||
@ -181,10 +186,11 @@ public BlockStateResource build(String blockstateFile) throws IOException {
|
|||||||
variant.models = loadModels(transformedModelNode, blockstateFile, null);
|
variant.models = loadModels(transformedModelNode, blockstateFile, null);
|
||||||
|
|
||||||
variant.updateTotalWeight();
|
variant.updateTotalWeight();
|
||||||
|
variant.checkValid();
|
||||||
|
|
||||||
blockState.variants.add(variant);
|
blockState.variants.add(variant);
|
||||||
} catch (Exception ex) {
|
} catch (Throwable t) {
|
||||||
Logger.global.logWarning("Failed to parse a variant of " + blockstateFile + ": " + ex);
|
Logger.global.logWarning("Failed to parse a variant of " + blockstateFile + ": " + t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,10 +205,11 @@ public BlockStateResource build(String blockstateFile) throws IOException {
|
|||||||
variant.models = loadModels(partNode.getNode("apply"), blockstateFile, null);
|
variant.models = loadModels(partNode.getNode("apply"), blockstateFile, null);
|
||||||
|
|
||||||
variant.updateTotalWeight();
|
variant.updateTotalWeight();
|
||||||
|
variant.checkValid();
|
||||||
|
|
||||||
blockState.multipart.add(variant);
|
blockState.multipart.add(variant);
|
||||||
} catch (Exception ex) {
|
} catch (Throwable t) {
|
||||||
Logger.global.logWarning("Failed to parse a multipart-part of " + blockstateFile + ": " + ex);
|
Logger.global.logWarning("Failed to parse a multipart-part of " + blockstateFile + ": " + t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +371,14 @@ private BlockStateResource buildForge(ConfigurationNode config, String blockstat
|
|||||||
}
|
}
|
||||||
|
|
||||||
variant.updateTotalWeight();
|
variant.updateTotalWeight();
|
||||||
|
|
||||||
|
try {
|
||||||
|
variant.checkValid();
|
||||||
blockState.variants.add(variant);
|
blockState.variants.add(variant);
|
||||||
|
} catch (ParseResourceException ex) {
|
||||||
|
Logger.global.logWarning("Failed to parse a variant (forge/property) of " + blockstateFile + ": " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//create default straight variant
|
//create default straight variant
|
||||||
@ -390,7 +404,14 @@ private BlockStateResource buildForge(ConfigurationNode config, String blockstat
|
|||||||
}
|
}
|
||||||
|
|
||||||
variant.updateTotalWeight();
|
variant.updateTotalWeight();
|
||||||
|
|
||||||
|
try {
|
||||||
|
variant.checkValid();
|
||||||
blockState.variants.add(variant);
|
blockState.variants.add(variant);
|
||||||
|
} catch (ParseResourceException ex) {
|
||||||
|
Logger.global.logWarning("Failed to parse a variant (forge/straight) of " + blockstateFile + ": " + ex);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockState;
|
return blockState;
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.config.MainConfig.MapConfig;
|
||||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||||
|
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;
|
||||||
@ -129,12 +131,22 @@ public void setFrom(TileRenderer tileRenderer, String mapId) {
|
|||||||
set(pointSize.getY() / 2, mapId, "lowres", "translate", "z");
|
set(pointSize.getY() / 2, mapId, "lowres", "translate", "z");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHiresViewDistance(float hiresViewDistance, String mapId) {
|
public void setFrom(World world, String mapId) {
|
||||||
set(hiresViewDistance, mapId, "hires", "viewDistance");
|
set(world.getSpawnPoint().getX(), mapId, "startPos", "x");
|
||||||
|
set(world.getSpawnPoint().getZ(), mapId, "startPos", "z");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLowresViewDistance(float lowresViewDistance, String mapId) {
|
public void setFrom(MapConfig mapConfig, String mapId) {
|
||||||
set(lowresViewDistance, mapId, "lowres", "viewDistance");
|
Vector2i startPos = mapConfig.getStartPos();
|
||||||
|
if (startPos != null) {
|
||||||
|
set(startPos.getX(), mapId, "startPos", "x");
|
||||||
|
set(startPos.getY(), mapId, "startPos", "z");
|
||||||
|
}
|
||||||
|
|
||||||
|
set(mapConfig.getLowresViewDistance(), mapId, "lowres", "viewDistance");
|
||||||
|
set(mapConfig.getHiresViewDistance(), mapId, "hires", "viewDistance");
|
||||||
|
|
||||||
|
setName(mapConfig.getName(), mapId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOrdinal(int ordinal, String mapId) {
|
public void setOrdinal(int ordinal, String mapId) {
|
||||||
|
@ -81,32 +81,17 @@ export default class BlueMap {
|
|||||||
this.controls = new Controls(this.camera, this.element, this.hiresScene);
|
this.controls = new Controls(this.camera, this.element, this.hiresScene);
|
||||||
|
|
||||||
this.loadSettings().then(async () => {
|
this.loadSettings().then(async () => {
|
||||||
this.controls.setTileSize(this.settings[this.map]['hires']['tileSize']);
|
|
||||||
|
|
||||||
this.lowresTileManager = new TileManager(
|
|
||||||
this,
|
|
||||||
this.settings[this.map]['lowres']['viewDistance'],
|
|
||||||
this.loadLowresTile,
|
|
||||||
this.lowresScene,
|
|
||||||
this.settings[this.map]['lowres']['tileSize'],
|
|
||||||
{x: 0, z: 0}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.hiresTileManager = new TileManager(
|
|
||||||
this,
|
|
||||||
this.settings[this.map]['hires']['viewDistance'],
|
|
||||||
this.loadHiresTile,
|
|
||||||
this.hiresScene,
|
|
||||||
this.settings[this.map]['hires']['tileSize'],
|
|
||||||
{x: 0, z: 0}
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.loadHiresMaterial();
|
await this.loadHiresMaterial();
|
||||||
await this.loadLowresMaterial();
|
await this.loadLowresMaterial();
|
||||||
|
|
||||||
|
this.changeMap(this.map);
|
||||||
|
|
||||||
this.initModules();
|
this.initModules();
|
||||||
this.start();
|
this.start();
|
||||||
}).catch(error => this.onLoadError(error.toString()));
|
}).catch(error => {
|
||||||
|
this.onLoadError(error.toString())
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initModules() {
|
initModules() {
|
||||||
@ -119,12 +104,20 @@ export default class BlueMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changeMap(map) {
|
changeMap(map) {
|
||||||
this.hiresTileManager.close();
|
if (this.hiresTileManager !== undefined) this.hiresTileManager.close();
|
||||||
this.lowresTileManager.close();
|
if (this.lowresTileManager !== undefined) this.lowresTileManager.close();
|
||||||
|
|
||||||
this.map = map;
|
this.map = map;
|
||||||
|
|
||||||
|
let startPos = {
|
||||||
|
x: this.settings[this.map]["startPos"]["x"],
|
||||||
|
z: this.settings[this.map]["startPos"]["z"]
|
||||||
|
};
|
||||||
|
|
||||||
this.controls.setTileSize(this.settings[this.map]['hires']['tileSize']);
|
this.controls.setTileSize(this.settings[this.map]['hires']['tileSize']);
|
||||||
this.controls.resetPosition();
|
this.controls.resetPosition();
|
||||||
|
this.controls.targetPosition.set(startPos.x, this.controls.targetPosition.y, startPos.z);
|
||||||
|
this.controls.position.copy(this.controls.targetPosition);
|
||||||
|
|
||||||
this.lowresTileManager = new TileManager(
|
this.lowresTileManager = new TileManager(
|
||||||
this,
|
this,
|
||||||
@ -132,7 +125,7 @@ export default class BlueMap {
|
|||||||
this.loadLowresTile,
|
this.loadLowresTile,
|
||||||
this.lowresScene,
|
this.lowresScene,
|
||||||
this.settings[this.map]['lowres']['tileSize'],
|
this.settings[this.map]['lowres']['tileSize'],
|
||||||
{x: 0, z: 0}
|
startPos
|
||||||
);
|
);
|
||||||
|
|
||||||
this.hiresTileManager = new TileManager(
|
this.hiresTileManager = new TileManager(
|
||||||
@ -141,7 +134,7 @@ export default class BlueMap {
|
|||||||
this.loadHiresTile,
|
this.loadHiresTile,
|
||||||
this.hiresScene,
|
this.hiresScene,
|
||||||
this.settings[this.map]['hires']['tileSize'],
|
this.settings[this.map]['hires']['tileSize'],
|
||||||
{x: 0, z: 0}
|
startPos
|
||||||
);
|
);
|
||||||
|
|
||||||
this.lowresTileManager.update();
|
this.lowresTileManager.update();
|
||||||
@ -221,7 +214,10 @@ export default class BlueMap {
|
|||||||
+ ':' + Math.round(this.controls.targetDistance * 100) / 100
|
+ ':' + Math.round(this.controls.targetDistance * 100) / 100
|
||||||
+ ':' + Math.ceil(this.controls.targetAngle * 100) / 100
|
+ ':' + Math.ceil(this.controls.targetAngle * 100) / 100
|
||||||
+ ':' + Math.floor(this.controls.targetPosition.y);
|
+ ':' + Math.floor(this.controls.targetPosition.y);
|
||||||
|
// only update hash when changed
|
||||||
|
if (window.location.hash !== this.locationHash) {
|
||||||
history.replaceState(undefined, undefined, this.locationHash);
|
history.replaceState(undefined, undefined, this.locationHash);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
|
@ -37,7 +37,8 @@ export default class TileManager {
|
|||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.tileSize = new Vector2(tileSize.x, tileSize.z);
|
this.tileSize = new Vector2(tileSize.x, tileSize.z);
|
||||||
|
|
||||||
this.tile = new Vector2(position.x, position.z);
|
this.tile = new Vector2(0, 0);
|
||||||
|
this.tile.set(position.x, position.z).divide(this.tileSize).floor();
|
||||||
this.lastTile = this.tile.clone();
|
this.lastTile = this.tile.clone();
|
||||||
|
|
||||||
this.closed = false;
|
this.closed = false;
|
||||||
|
@ -70,6 +70,10 @@ maps: [
|
|||||||
# The path to the save-folder of the world to render
|
# The path to the save-folder of the world to render
|
||||||
world: "world"
|
world: "world"
|
||||||
|
|
||||||
|
# The position on the world where the map will be centered if you open it.
|
||||||
|
# This defaults to the world-spawn if you don't set it.
|
||||||
|
#startPos: [500, -820]
|
||||||
|
|
||||||
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
||||||
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||||
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
# This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible.
|
||||||
|
Loading…
Reference in New Issue
Block a user