mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-24 03:25:17 +01:00
Merge branch 'master' into mc/1.12
This commit is contained in:
commit
4a04746794
3
.gitignore
vendored
3
.gitignore
vendored
@ -20,6 +20,9 @@ bin/*
|
|||||||
.project
|
.project
|
||||||
*/.project
|
*/.project
|
||||||
|
|
||||||
|
.idea
|
||||||
|
*/.idea
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
@ -109,6 +109,12 @@ maps: [
|
|||||||
# Default is enabled
|
# Default is enabled
|
||||||
renderEdges: true
|
renderEdges: true
|
||||||
|
|
||||||
|
# With this set to true, the generated files for this world are compressed using gzip to save A LOT of space.
|
||||||
|
# Files will be only 5% as big with compression!
|
||||||
|
# Note: If you are using NGINX or Apache to host your map, you can configure them to serve the compressed files directly.
|
||||||
|
# This is much better than disabling the compression.
|
||||||
|
useCompression: true
|
||||||
|
|
||||||
# HIRES is the high-resolution render of the map. Where you see every block.
|
# HIRES is the high-resolution render of the map. Where you see every block.
|
||||||
hires {
|
hires {
|
||||||
# Defines the size of one map-tile in blocks.
|
# Defines the size of one map-tile in blocks.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile group: 'commons-cli', name: 'commons-cli', version: '1.4'
|
compile group: 'commons-cli', name: 'commons-cli', version: '1.4'
|
||||||
compile project(':BlueMapCore')
|
compile project(':BlueMapCommon')
|
||||||
}
|
}
|
||||||
|
@ -45,10 +45,15 @@
|
|||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||||
|
|
||||||
|
import com.flowpowered.math.GenericMath;
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.common.MapType;
|
||||||
|
import de.bluecolored.bluemap.common.RenderManager;
|
||||||
|
import de.bluecolored.bluemap.common.RenderTask;
|
||||||
import de.bluecolored.bluemap.core.config.ConfigManager;
|
import de.bluecolored.bluemap.core.config.ConfigManager;
|
||||||
import de.bluecolored.bluemap.core.config.MainConfig;
|
import de.bluecolored.bluemap.core.config.MainConfig;
|
||||||
import de.bluecolored.bluemap.core.config.MainConfig.MapConfig;
|
import de.bluecolored.bluemap.core.config.MainConfig.MapConfig;
|
||||||
@ -116,7 +121,8 @@ public void renderMaps() throws IOException {
|
|||||||
LowresModelManager lowresModelManager = new LowresModelManager(
|
LowresModelManager lowresModelManager = new LowresModelManager(
|
||||||
config.getWebDataPath().resolve(mapConfig.getId()).resolve("lowres"),
|
config.getWebDataPath().resolve(mapConfig.getId()).resolve("lowres"),
|
||||||
new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()),
|
new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()),
|
||||||
new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile())
|
new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile()),
|
||||||
|
mapConfig.useGzipCompression()
|
||||||
);
|
);
|
||||||
|
|
||||||
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager);
|
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager);
|
||||||
@ -146,6 +152,9 @@ public void renderMaps() throws IOException {
|
|||||||
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
||||||
resourcePack.saveTextureFile(textureExportFile);
|
resourcePack.saveTextureFile(textureExportFile);
|
||||||
|
|
||||||
|
RenderManager renderManager = new RenderManager(config.getRenderThreadCount());
|
||||||
|
renderManager.start();
|
||||||
|
|
||||||
for (MapType map : maps.values()) {
|
for (MapType map : maps.values()) {
|
||||||
Logger.global.logInfo("Rendering map '" + map.getId() + "' ...");
|
Logger.global.logInfo("Rendering map '" + map.getId() + "' ...");
|
||||||
Logger.global.logInfo("Collecting tiles to render...");
|
Logger.global.logInfo("Collecting tiles to render...");
|
||||||
@ -166,15 +175,52 @@ public void renderMaps() throws IOException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tiles.isEmpty()) {
|
if (tiles.isEmpty()) {
|
||||||
Logger.global.logInfo("Render finished!");
|
continue;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.global.logInfo("Starting Render...");
|
Logger.global.logInfo("Starting Render...");
|
||||||
long starttime = System.currentTimeMillis();
|
long starttime = System.currentTimeMillis();
|
||||||
|
|
||||||
RenderTask task = new RenderTask(map, tiles, config.getRenderThreadCount());
|
RenderTask task = new RenderTask("Map-Render: " + map.getName(), map);
|
||||||
task.render();
|
task.addTiles(tiles);
|
||||||
|
task.optimizeQueue();
|
||||||
|
|
||||||
|
renderManager.addRenderTask(task);
|
||||||
|
|
||||||
|
long lastLogUpdate = System.currentTimeMillis();
|
||||||
|
long lastSave = lastLogUpdate;
|
||||||
|
|
||||||
|
while(!task.isFinished()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException e) {}
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (lastLogUpdate < now - 10000) { // print update all 10 seconds
|
||||||
|
lastLogUpdate = now;
|
||||||
|
long time = task.getActiveTime();
|
||||||
|
|
||||||
|
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
||||||
|
int tileCount = task.getRemainingTileCount() + task.getRenderedTileCount();
|
||||||
|
double pct = (double)task.getRenderedTileCount() / (double) tileCount;
|
||||||
|
|
||||||
|
long ert = (long)((time / pct) * (1d - pct));
|
||||||
|
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
||||||
|
|
||||||
|
double tps = task.getRenderedTileCount() / (time / 1000.0);
|
||||||
|
|
||||||
|
Logger.global.logInfo("Rendered " + task.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 - 5 * 60000) { // save every 5 minutes
|
||||||
|
lastSave = now;
|
||||||
|
map.getTileRenderer().save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map.getTileRenderer().save();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
webSettings.set(starttime, map.getId(), "last-render");
|
webSettings.set(starttime, map.getId(), "last-render");
|
||||||
@ -184,6 +230,8 @@ public void renderMaps() throws IOException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderManager.stop();
|
||||||
|
|
||||||
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.");
|
||||||
|
@ -1,91 +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.cli;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
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) throws IOException {
|
|
||||||
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,171 +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.cli;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Deque;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
|
||||||
|
|
||||||
import com.flowpowered.math.GenericMath;
|
|
||||||
import com.flowpowered.math.vector.Vector2d;
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
|
||||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
|
||||||
import de.bluecolored.bluemap.core.render.WorldTile;
|
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
|
||||||
|
|
||||||
public class RenderTask {
|
|
||||||
|
|
||||||
private World world;
|
|
||||||
private TileRenderer tileRenderer;
|
|
||||||
private Deque<Vector2i> tilesToRender;
|
|
||||||
|
|
||||||
private int tileCount;
|
|
||||||
private long startTime = -1;
|
|
||||||
private int renderedTiles = 0;
|
|
||||||
|
|
||||||
private Thread[] threads;
|
|
||||||
|
|
||||||
public RenderTask(MapType map, Collection<Vector2i> tilesToRender, int threadCount) {
|
|
||||||
this.world = map.getWorld();
|
|
||||||
this.tileRenderer = map.getTileRenderer();
|
|
||||||
|
|
||||||
//Sort the chunks to opimize the chunk-cache usage of MCAWorld and generate the world in a nicer order, so you can see the first results early in the web-map during render
|
|
||||||
Vector2d sortGridSize = new Vector2d(20, 20).div(tileRenderer.getHiresModelManager().getTileSize().toDouble().div(16)).ceil().max(1, 1); //Find a good grid size to match the MCAWorlds chunk-cache size of 500
|
|
||||||
ArrayList<Vector2i> sortedTiles = new ArrayList<>(tilesToRender);
|
|
||||||
sortedTiles.sort((v1, v2) -> {
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tilesToRender = new ArrayDeque<>(sortedTiles);
|
|
||||||
|
|
||||||
this.tileCount = this.tilesToRender.size();
|
|
||||||
this.threads = new Thread[threadCount];
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void render() {
|
|
||||||
this.startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
for (int i = 0; i < threads.length; i++) {
|
|
||||||
if (threads[i] != null) threads[i].interrupt();
|
|
||||||
|
|
||||||
threads[i] = new Thread(this::renderThread);
|
|
||||||
threads[i].start();
|
|
||||||
}
|
|
||||||
|
|
||||||
long lastLogUpdate = startTime;
|
|
||||||
long lastSave = startTime;
|
|
||||||
|
|
||||||
while (!Thread.interrupted()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean stillRendering = false;
|
|
||||||
for (Thread t : threads) {
|
|
||||||
if (t.isAlive()) {
|
|
||||||
stillRendering = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!stillRendering) break;
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if (lastLogUpdate < now - 10000) { // print update all 10 seconds
|
|
||||||
lastLogUpdate = now;
|
|
||||||
|
|
||||||
long time = now - startTime;
|
|
||||||
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
|
||||||
double pct = (double)renderedTiles / (double)tileCount;
|
|
||||||
|
|
||||||
long ert = (long)((time / pct) * (1d - pct));
|
|
||||||
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
|
||||||
|
|
||||||
double tps = renderedTiles / (time / 1000.0);
|
|
||||||
|
|
||||||
Logger.global.logInfo("Rendered " + renderedTiles + " 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 - 5 * 60000) { // save every 5 minutes
|
|
||||||
lastSave = now;
|
|
||||||
tileRenderer.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tileRenderer.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderThread() {
|
|
||||||
Vector2i tilePos;
|
|
||||||
|
|
||||||
while (!Thread.interrupted()) {
|
|
||||||
synchronized (tilesToRender) {
|
|
||||||
if (tilesToRender.isEmpty()) break;
|
|
||||||
tilePos = tilesToRender.poll();
|
|
||||||
}
|
|
||||||
|
|
||||||
WorldTile tile = new WorldTile(world, tilePos);
|
|
||||||
try {
|
|
||||||
tileRenderer.render(tile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Logger.global.logError("Failed to render tile " + tilePos, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderedTiles++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -108,6 +108,12 @@ maps: [
|
|||||||
# Default is enabled
|
# Default is enabled
|
||||||
renderEdges: true
|
renderEdges: true
|
||||||
|
|
||||||
|
# With this set to true, the generated files for this world are compressed using gzip to save A LOT of space.
|
||||||
|
# Files will be only 5% as big with compression!
|
||||||
|
# Note: If you are using NGINX or Apache to host your map, you can configure them to serve the compressed files directly.
|
||||||
|
# This is much better than disabling the compression.
|
||||||
|
useCompression: true
|
||||||
|
|
||||||
# HIRES is the high-resolution render of the map. Where you see every block.
|
# HIRES is the high-resolution render of the map. Where you see every block.
|
||||||
hires {
|
hires {
|
||||||
# Defines the size of one map-tile in blocks.
|
# Defines the size of one map-tile in blocks.
|
||||||
|
@ -190,7 +190,8 @@ public synchronized void load() throws IOException, ParseResourceException {
|
|||||||
LowresModelManager lowresModelManager = new LowresModelManager(
|
LowresModelManager lowresModelManager = new LowresModelManager(
|
||||||
config.getWebDataPath().resolve(id).resolve("lowres"),
|
config.getWebDataPath().resolve(id).resolve("lowres"),
|
||||||
new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()),
|
new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()),
|
||||||
new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile())
|
new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile()),
|
||||||
|
mapConfig.useGzipCompression()
|
||||||
);
|
);
|
||||||
|
|
||||||
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager);
|
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager);
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
"homepage": "https://github.com/BlueMap-Minecraft/BlueMap#readme",
|
"homepage": "https://github.com/BlueMap-Minecraft/BlueMap#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jquery": "^3.4.1",
|
"jquery": "^3.4.1",
|
||||||
"three": "^0.94.0"
|
"three": "^0.94.0",
|
||||||
|
"hammerjs": "^2.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"css-loader": "^3.4.2",
|
"css-loader": "^3.4.2",
|
||||||
|
@ -201,6 +201,8 @@ public class MapConfig implements RenderSettings {
|
|||||||
private Vector3i min, max;
|
private Vector3i min, max;
|
||||||
private boolean renderEdges;
|
private boolean renderEdges;
|
||||||
|
|
||||||
|
private boolean useGzip;
|
||||||
|
|
||||||
private int hiresTileSize;
|
private int hiresTileSize;
|
||||||
private float hiresViewDistance;
|
private float hiresViewDistance;
|
||||||
|
|
||||||
@ -232,6 +234,8 @@ 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.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);
|
||||||
|
|
||||||
@ -310,6 +314,11 @@ public boolean isRenderEdges() {
|
|||||||
return renderEdges;
|
return renderEdges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useGzipCompression() {
|
||||||
|
return useGzip;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkOutdated(ConfigurationNode node) throws OutdatedConfigException {
|
private void checkOutdated(ConfigurationNode node) throws OutdatedConfigException {
|
||||||
|
@ -357,7 +357,15 @@ private Path getMCAFilePath(Vector2i region) {
|
|||||||
|
|
||||||
public static MCAWorld load(Path worldFolder, UUID uuid, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, BiomeMapper biomeIdMapper) throws IOException {
|
public static MCAWorld load(Path worldFolder, UUID uuid, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, BiomeMapper biomeIdMapper) throws IOException {
|
||||||
try {
|
try {
|
||||||
CompoundTag level = (CompoundTag) NBTUtil.readTag(worldFolder.resolve("level.dat").toFile());
|
File levelFile = new File(worldFolder.toFile(), "level.dat");
|
||||||
|
if (!levelFile.exists()) {
|
||||||
|
levelFile = new File(worldFolder.toFile().getParentFile(), "level.dat");
|
||||||
|
if (!levelFile.exists()) {
|
||||||
|
throw new FileNotFoundException("Could not find a level.dat file for this world!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag level = (CompoundTag) NBTUtil.readTag(levelFile);
|
||||||
CompoundTag levelData = level.getCompoundTag("Data");
|
CompoundTag levelData = level.getCompoundTag("Data");
|
||||||
|
|
||||||
String name = levelData.getString("LevelName");
|
String name = levelData.getString("LevelName");
|
||||||
|
@ -78,6 +78,13 @@ default boolean isRenderEdges() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If gzip compression will be used to compress the generated files
|
||||||
|
*/
|
||||||
|
default boolean useGzipCompression() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
default RenderSettings copy() {
|
default RenderSettings copy() {
|
||||||
return new StaticRenderSettings(
|
return new StaticRenderSettings(
|
||||||
getAmbientOcclusionStrenght(),
|
getAmbientOcclusionStrenght(),
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -58,11 +59,13 @@ public class HiresModelManager {
|
|||||||
|
|
||||||
private ExecutorService savingExecutor;
|
private ExecutorService savingExecutor;
|
||||||
|
|
||||||
|
private boolean useGzip;
|
||||||
|
|
||||||
public HiresModelManager(Path fileRoot, ResourcePack resourcePack, RenderSettings renderSettings, Vector2i tileSize, ExecutorService savingExecutor) {
|
public HiresModelManager(Path fileRoot, ResourcePack resourcePack, RenderSettings renderSettings, Vector2i tileSize, ExecutorService savingExecutor) {
|
||||||
this(fileRoot, new HiresModelRenderer(resourcePack, renderSettings), tileSize, new Vector2i(2, 2), savingExecutor);
|
this(fileRoot, new HiresModelRenderer(resourcePack, renderSettings), tileSize, new Vector2i(2, 2), savingExecutor, renderSettings.useGzipCompression());
|
||||||
}
|
}
|
||||||
|
|
||||||
public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Vector2i tileSize, Vector2i gridOrigin, ExecutorService savingExecutor) {
|
public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Vector2i tileSize, Vector2i gridOrigin, ExecutorService savingExecutor, boolean useGzip) {
|
||||||
this.fileRoot = fileRoot;
|
this.fileRoot = fileRoot;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
|
|
||||||
@ -70,6 +73,7 @@ public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Vector2i ti
|
|||||||
this.gridOrigin = gridOrigin;
|
this.gridOrigin = gridOrigin;
|
||||||
|
|
||||||
this.savingExecutor = savingExecutor;
|
this.savingExecutor = savingExecutor;
|
||||||
|
this.useGzip = useGzip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +91,7 @@ private void save(final HiresModel model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void save(HiresModel model, String modelJson){
|
private void save(HiresModel model, String modelJson){
|
||||||
File file = getFile(model.getTile());
|
File file = getFile(model.getTile(), useGzip);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!file.exists()){
|
if (!file.exists()){
|
||||||
@ -95,9 +99,9 @@ private void save(HiresModel model, String modelJson){
|
|||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileOutputStream fos = new FileOutputStream(file);
|
OutputStream os = new FileOutputStream(file);
|
||||||
GZIPOutputStream zos = new GZIPOutputStream(fos);
|
if (useGzip) os = new GZIPOutputStream(os);
|
||||||
OutputStreamWriter osw = new OutputStreamWriter(zos, StandardCharsets.UTF_8);
|
OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
|
||||||
try (
|
try (
|
||||||
PrintWriter pw = new PrintWriter(osw);
|
PrintWriter pw = new PrintWriter(osw);
|
||||||
){
|
){
|
||||||
@ -185,8 +189,8 @@ public Vector2i posToTile(Vector3d pos){
|
|||||||
/**
|
/**
|
||||||
* Returns the file for a tile
|
* Returns the file for a tile
|
||||||
*/
|
*/
|
||||||
public File getFile(Vector2i tilePos){
|
public File getFile(Vector2i tilePos, boolean gzip){
|
||||||
return FileUtils.coordsToFile(fileRoot, tilePos, "json.gz");
|
return FileUtils.coordsToFile(fileRoot, tilePos, "json" + (gzip ? ".gz" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -95,7 +96,7 @@ public void update(Vector2i point, float height, Vector3f color){
|
|||||||
* Saves this model to its file
|
* Saves this model to its file
|
||||||
* @param force if this is false, the model is only saved if it has any changes
|
* @param force if this is false, the model is only saved if it has any changes
|
||||||
*/
|
*/
|
||||||
public void save(File file, boolean force) throws IOException {
|
public void save(File file, boolean force, boolean useGzip) throws IOException {
|
||||||
if (!force && !hasUnsavedChanges) return;
|
if (!force && !hasUnsavedChanges) return;
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
|
|
||||||
@ -118,9 +119,9 @@ public void save(File file, boolean force) throws IOException {
|
|||||||
throw new IOException("Failed to get write-access to file: " + file, e);
|
throw new IOException("Failed to get write-access to file: " + file, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileOutputStream fos = new FileOutputStream(file);
|
OutputStream os = new FileOutputStream(file);
|
||||||
GZIPOutputStream zos = new GZIPOutputStream(fos);
|
if (useGzip) os = new GZIPOutputStream(os);
|
||||||
OutputStreamWriter osw = new OutputStreamWriter(zos, StandardCharsets.UTF_8);
|
OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
|
||||||
try (
|
try (
|
||||||
PrintWriter pw = new PrintWriter(osw);
|
PrintWriter pw = new PrintWriter(osw);
|
||||||
){
|
){
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -59,13 +60,17 @@ public class LowresModelManager {
|
|||||||
|
|
||||||
private Map<File, CachedModel> models;
|
private Map<File, CachedModel> models;
|
||||||
|
|
||||||
public LowresModelManager(Path fileRoot, Vector2i gridSize, Vector2i pointsPerHiresTile) {
|
private boolean useGzip;
|
||||||
|
|
||||||
|
public LowresModelManager(Path fileRoot, Vector2i gridSize, Vector2i pointsPerHiresTile, boolean useGzip) {
|
||||||
this.fileRoot = fileRoot;
|
this.fileRoot = fileRoot;
|
||||||
|
|
||||||
this.gridSize = gridSize;
|
this.gridSize = gridSize;
|
||||||
this.pointsPerHiresTile = pointsPerHiresTile;
|
this.pointsPerHiresTile = pointsPerHiresTile;
|
||||||
|
|
||||||
models = new ConcurrentHashMap<>();
|
models = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
this.useGzip = useGzip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,13 +171,13 @@ public void update(UUID world, Vector2i point, float height, Vector3f color) thr
|
|||||||
/**
|
/**
|
||||||
* Returns the file for a tile
|
* Returns the file for a tile
|
||||||
*/
|
*/
|
||||||
public File getFile(Vector2i tile){
|
public File getFile(Vector2i tile, boolean useGzip){
|
||||||
return FileUtils.coordsToFile(fileRoot, tile, "json.gz");
|
return FileUtils.coordsToFile(fileRoot, tile, "json" + (useGzip ? ".gz" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LowresModel getModel(UUID world, Vector2i tile) throws IOException {
|
private LowresModel getModel(UUID world, Vector2i tile) throws IOException {
|
||||||
|
|
||||||
File modelFile = getFile(tile);
|
File modelFile = getFile(tile, useGzip);
|
||||||
CachedModel model = models.get(modelFile);
|
CachedModel model = models.get(modelFile);
|
||||||
|
|
||||||
if (model == null){
|
if (model == null){
|
||||||
@ -181,11 +186,10 @@ private LowresModel getModel(UUID world, Vector2i tile) throws IOException {
|
|||||||
if (model == null){
|
if (model == null){
|
||||||
if (modelFile.exists()){
|
if (modelFile.exists()){
|
||||||
|
|
||||||
FileInputStream fis = new FileInputStream(modelFile);
|
InputStream is = new FileInputStream(modelFile);
|
||||||
try(
|
if (useGzip) is = new GZIPInputStream(is);
|
||||||
GZIPInputStream zis = new GZIPInputStream(fis);
|
try {
|
||||||
){
|
String json = IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||||
String json = IOUtils.toString(zis, StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
model = new CachedModel(world, tile, BufferGeometry.fromJson(json));
|
model = new CachedModel(world, tile, BufferGeometry.fromJson(json));
|
||||||
@ -194,6 +198,8 @@ private LowresModel getModel(UUID world, Vector2i tile) throws IOException {
|
|||||||
//gridFile.renameTo(gridFile.toPath().getParent().resolve(gridFile.getName() + ".broken").toFile());
|
//gridFile.renameTo(gridFile.toPath().getParent().resolve(gridFile.getName() + ".broken").toFile());
|
||||||
modelFile.delete();
|
modelFile.delete();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -238,10 +244,10 @@ public synchronized void tidyUpModelCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void saveAndRemoveModel(CachedModel model) {
|
private synchronized void saveAndRemoveModel(CachedModel model) {
|
||||||
File modelFile = getFile(model.getTile());
|
File modelFile = getFile(model.getTile(), useGzip);
|
||||||
models.remove(modelFile);
|
models.remove(modelFile);
|
||||||
try {
|
try {
|
||||||
model.save(modelFile, false);
|
model.save(modelFile, false, useGzip);
|
||||||
//logger.logDebug("Saved and unloaded lowres tile: " + model.getTile());
|
//logger.logDebug("Saved and unloaded lowres tile: " + model.getTile());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logError("Failed to save and unload lowres-model: " + modelFile, ex);
|
Logger.global.logError("Failed to save and unload lowres-model: " + modelFile, ex);
|
||||||
@ -249,9 +255,9 @@ private synchronized void saveAndRemoveModel(CachedModel model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void saveModel(CachedModel model) {
|
private void saveModel(CachedModel model) {
|
||||||
File modelFile = getFile(model.getTile());
|
File modelFile = getFile(model.getTile(), useGzip);
|
||||||
try {
|
try {
|
||||||
model.save(modelFile, false);
|
model.save(modelFile, false, useGzip);
|
||||||
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logError("Failed to save lowres-model: " + modelFile, ex);
|
Logger.global.logError("Failed to save lowres-model: " + modelFile, ex);
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
@ -160,9 +161,21 @@ private HttpResponse generateResponse(HttpRequest request) {
|
|||||||
} catch (IllegalArgumentException e){}
|
} catch (IllegalArgumentException e){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//check ETag
|
||||||
|
String eTag = Long.toHexString(file.length()) + Integer.toHexString(file.hashCode()) + Long.toHexString(lastModified);
|
||||||
|
Set<String> etagStringSet = request.getHeader("If-None-Match");
|
||||||
|
if (!etagStringSet.isEmpty()){
|
||||||
|
if(etagStringSet.iterator().next().equals(eTag)) {
|
||||||
|
return new HttpResponse(HttpStatusCode.NOT_MODIFIED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//create response
|
||||||
HttpResponse response = new HttpResponse(HttpStatusCode.OK);
|
HttpResponse response = new HttpResponse(HttpStatusCode.OK);
|
||||||
|
response.addHeader("ETag", eTag);
|
||||||
if (lastModified > 0) response.addHeader("Last-Modified", timestampToString(lastModified));
|
if (lastModified > 0) response.addHeader("Last-Modified", timestampToString(lastModified));
|
||||||
|
response.addHeader("Cache-Control", "public");
|
||||||
|
response.addHeader("Cache-Control", "max-age=" + TimeUnit.HOURS.toSeconds(1));
|
||||||
|
|
||||||
//add content type header
|
//add content type header
|
||||||
String filetype = file.getName().toString();
|
String filetype = file.getName().toString();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
<title>BlueMap</title>
|
<title>BlueMap</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -69,6 +69,7 @@ export default class BlueMap {
|
|||||||
this.dataRoot = dataRoot;
|
this.dataRoot = dataRoot;
|
||||||
|
|
||||||
this.loadingNoticeElement = $('<div id="bluemap-loading" class="box">loading...</div>').appendTo($(this.element));
|
this.loadingNoticeElement = $('<div id="bluemap-loading" class="box">loading...</div>').appendTo($(this.element));
|
||||||
|
window.onerror = this.onLoadError;
|
||||||
|
|
||||||
this.fileLoader = new FileLoader();
|
this.fileLoader = new FileLoader();
|
||||||
this.blobLoader = new FileLoader();
|
this.blobLoader = new FileLoader();
|
||||||
@ -80,6 +81,8 @@ 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.lowresTileManager = new TileManager(
|
||||||
this,
|
this,
|
||||||
this.settings[this.map]['lowres']['viewDistance'],
|
this.settings[this.map]['lowres']['viewDistance'],
|
||||||
@ -103,7 +106,7 @@ export default class BlueMap {
|
|||||||
|
|
||||||
this.initModules();
|
this.initModules();
|
||||||
this.start();
|
this.start();
|
||||||
});
|
}).catch(error => this.onLoadError(error.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
initModules() {
|
initModules() {
|
||||||
@ -120,6 +123,7 @@ export default class BlueMap {
|
|||||||
this.lowresTileManager.close();
|
this.lowresTileManager.close();
|
||||||
|
|
||||||
this.map = map;
|
this.map = map;
|
||||||
|
this.controls.setTileSize(this.settings[this.map]['hires']['tileSize']);
|
||||||
this.controls.resetPosition();
|
this.controls.resetPosition();
|
||||||
|
|
||||||
this.lowresTileManager = new TileManager(
|
this.lowresTileManager = new TileManager(
|
||||||
@ -205,7 +209,9 @@ export default class BlueMap {
|
|||||||
setTimeout(this.update, 1000);
|
setTimeout(this.update, 1000);
|
||||||
|
|
||||||
this.lowresTileManager.setPosition(this.controls.targetPosition);
|
this.lowresTileManager.setPosition(this.controls.targetPosition);
|
||||||
|
if (this.camera.position.y < 400) {
|
||||||
this.hiresTileManager.setPosition(this.controls.targetPosition);
|
this.hiresTileManager.setPosition(this.controls.targetPosition);
|
||||||
|
}
|
||||||
|
|
||||||
this.locationHash =
|
this.locationHash =
|
||||||
'#' + this.map
|
'#' + this.map
|
||||||
@ -239,7 +245,7 @@ export default class BlueMap {
|
|||||||
this.renderer.clearDepth();
|
this.renderer.clearDepth();
|
||||||
this.renderer.render(this.hiresScene, this.camera, this.renderer.getRenderTarget(), false);
|
this.renderer.render(this.hiresScene, this.camera, this.renderer.getRenderTarget(), false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleContainerResize = () => {
|
handleContainerResize = () => {
|
||||||
this.camera.aspect = this.element.clientWidth / this.element.clientHeight;
|
this.camera.aspect = this.element.clientWidth / this.element.clientHeight;
|
||||||
@ -254,7 +260,7 @@ export default class BlueMap {
|
|||||||
.css('height', this.element.clientHeight);
|
.css('height', this.element.clientHeight);
|
||||||
|
|
||||||
this.updateFrame = true;
|
this.updateFrame = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
@ -358,7 +364,6 @@ export default class BlueMap {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.fileLoader.load(this.dataRoot + 'textures.json', textures => {
|
this.fileLoader.load(this.dataRoot + 'textures.json', textures => {
|
||||||
textures = JSON.parse(textures);
|
textures = JSON.parse(textures);
|
||||||
|
|
||||||
let materials = [];
|
let materials = [];
|
||||||
for (let i = 0; i < textures['textures'].length; i++) {
|
for (let i = 0; i < textures['textures'].length; i++) {
|
||||||
let t = textures['textures'][i];
|
let t = textures['textures'][i];
|
||||||
@ -394,7 +399,6 @@ export default class BlueMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.hiresMaterial = materials;
|
this.hiresMaterial = materials;
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -453,22 +457,44 @@ export default class BlueMap {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onLoadError = (message, url, line, col) => {
|
||||||
|
this.loadingNoticeElement.remove();
|
||||||
|
|
||||||
|
this.toggleAlert(undefined, `
|
||||||
|
<div style="max-width: 500px">
|
||||||
|
<h1>Error</h1>
|
||||||
|
<p style="color: red; font-family: monospace">${message}</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|
||||||
// ###### UI ######
|
// ###### UI ######
|
||||||
|
|
||||||
alert(content) {
|
toggleAlert(id, content) {
|
||||||
let alertBox = $('#alert-box');
|
let alertBox = $('#alert-box');
|
||||||
if (alertBox.length === 0){
|
if (alertBox.length === 0){
|
||||||
alertBox = $('<div id="alert-box"></div>').appendTo(this.element);
|
alertBox = $('<div id="alert-box"></div>').appendTo(this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayAlert = () => {
|
let displayAlert = () => {
|
||||||
let alert = $(`<div class="alert box" style="display: none;"><div class="alert-close-button"></div>${content}</div>`).appendTo(alertBox);
|
let alert = $(`<div class="alert box" data-alert-id="${id}" style="display: none;"><div class="alert-close-button"></div>${content}</div>`).appendTo(alertBox);
|
||||||
alert.find('.alert-close-button').click(() => {
|
alert.find('.alert-close-button').click(() => {
|
||||||
alert.fadeOut(200, () => alert.remove());
|
alert.fadeOut(200, () => alert.remove());
|
||||||
});
|
});
|
||||||
alert.fadeIn(200);
|
alert.fadeIn(200);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (id !== undefined) {
|
||||||
|
let sameAlert = alertBox.find(`.alert[data-alert-id=${id}]`);
|
||||||
|
if (sameAlert.length > 0) {
|
||||||
|
alertBox.fadeOut(200, () => {
|
||||||
|
alertBox.html('');
|
||||||
|
alertBox.show();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let oldAlerts = alertBox.find('.alert');
|
let oldAlerts = alertBox.find('.alert');
|
||||||
if (oldAlerts.length > 0){
|
if (oldAlerts.length > 0){
|
||||||
alertBox.fadeOut(200, () => {
|
alertBox.fadeOut(200, () => {
|
||||||
@ -476,8 +502,9 @@ export default class BlueMap {
|
|||||||
alertBox.show();
|
alertBox.show();
|
||||||
displayAlert();
|
displayAlert();
|
||||||
});
|
});
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
displayAlert();
|
displayAlert();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
Vector3,
|
Vector3,
|
||||||
MOUSE
|
MOUSE
|
||||||
} from 'three';
|
} from 'three';
|
||||||
|
import Hammer from 'hammerjs';
|
||||||
|
|
||||||
import { Vector2_ZERO } from './utils.js';
|
import { Vector2_ZERO } from './utils.js';
|
||||||
|
|
||||||
@ -94,25 +95,65 @@ export default class Controls {
|
|||||||
this.cameraPosDelta = new Vector3(0, 0, 0);
|
this.cameraPosDelta = new Vector3(0, 0, 0);
|
||||||
this.moveDelta = new Vector2(0, 0);
|
this.moveDelta = new Vector2(0, 0);
|
||||||
|
|
||||||
this.keyStates = {}
|
this.touchStart = new Vector2(0, 0);
|
||||||
|
this.touchDelta = new Vector2(0, 0);
|
||||||
|
|
||||||
|
this.keyStates = {};
|
||||||
this.state = Controls.STATES.NONE;
|
this.state = Controls.STATES.NONE;
|
||||||
|
|
||||||
let canvas = $(this.element).find('canvas').get(0);
|
let canvas = $(this.element).find('canvas').get(0);
|
||||||
|
|
||||||
|
// mouse events
|
||||||
window.addEventListener('contextmenu', event => {
|
window.addEventListener('contextmenu', event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}, false);
|
}, false);
|
||||||
window.addEventListener('mousemove', this.onMouseMove, false);
|
window.addEventListener('mousemove', this.onMouseMove, false);
|
||||||
canvas.addEventListener('mousedown', this.onMouseDown, false);
|
canvas.addEventListener('mousedown', this.onMouseDown, false);
|
||||||
canvas.addEventListener('mouseup', this.onMouseUp, false);
|
window.addEventListener('mouseup', this.onMouseUp, false); //this is on the window instead of the canvas, so if we drag out of the canvas and release the mouse it actually gets released
|
||||||
canvas.addEventListener('wheel', this.onMouseWheel, { passive: true });
|
canvas.addEventListener('wheel', this.onMouseWheel, { passive: true });
|
||||||
window.addEventListener('keydown', this.onKeyDown, false);
|
window.addEventListener('keydown', this.onKeyDown, false);
|
||||||
window.addEventListener('keyup', this.onKeyUp, false);
|
window.addEventListener('keyup', this.onKeyUp, false);
|
||||||
|
|
||||||
|
// touch events
|
||||||
|
this.hammer = new Hammer.Manager(canvas);
|
||||||
|
let touchMove = new Hammer.Pan({ event: 'move', direction: Hammer.DIRECTION_ALL, threshold: 0 });
|
||||||
|
let touchTilt = new Hammer.Pan({ event: 'tilt', direction: Hammer.DIRECTION_VERTICAL, pointers: 2, threshold: 0 });
|
||||||
|
let touchRotate = new Hammer.Rotate({ event: 'rotate', pointers: 2, threshold: 10 });
|
||||||
|
let touchZoom = new Hammer.Pinch({ event: 'zoom', pointers: 2, threshold: 0 });
|
||||||
|
|
||||||
|
touchTilt.recognizeWith(touchRotate);
|
||||||
|
touchTilt.recognizeWith(touchZoom);
|
||||||
|
touchRotate.recognizeWith(touchZoom);
|
||||||
|
|
||||||
|
this.hammer.add( touchMove );
|
||||||
|
this.hammer.add( touchTilt );
|
||||||
|
this.hammer.add( touchRotate );
|
||||||
|
this.hammer.add( touchZoom );
|
||||||
|
|
||||||
|
this.hammer.on('movestart', this.onTouchDown);
|
||||||
|
this.hammer.on('movemove', this.onTouchMove);
|
||||||
|
this.hammer.on('moveend', this.onTouchUp);
|
||||||
|
this.hammer.on('movecancel', this.onTouchUp);
|
||||||
|
this.hammer.on('tiltstart', this.onTouchTiltDown);
|
||||||
|
this.hammer.on('tiltmove', this.onTouchTiltMove);
|
||||||
|
this.hammer.on('tiltend', this.onTouchTiltUp);
|
||||||
|
this.hammer.on('tiltcancel', this.onTouchTiltUp);
|
||||||
|
this.hammer.on('rotatestart', this.onTouchRotateDown);
|
||||||
|
this.hammer.on('rotatemove', this.onTouchRotateMove);
|
||||||
|
this.hammer.on('rotateend', this.onTouchRotateUp);
|
||||||
|
this.hammer.on('rotatecancel', this.onTouchRotateUp);
|
||||||
|
this.hammer.on('zoomstart', this.onTouchZoomDown);
|
||||||
|
this.hammer.on('zoommove', this.onTouchZoomMove);
|
||||||
|
|
||||||
this.camera.position.set(0, 1000, 0);
|
this.camera.position.set(0, 1000, 0);
|
||||||
this.camera.lookAt(this.position);
|
this.camera.lookAt(this.position);
|
||||||
this.camera.updateProjectionMatrix();
|
this.camera.updateProjectionMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTileSize(tileSize) {
|
||||||
|
this.tileSize = tileSize;
|
||||||
|
}
|
||||||
|
|
||||||
resetPosition() {
|
resetPosition() {
|
||||||
this.position = new Vector3(0, 70, 0);
|
this.position = new Vector3(0, 70, 0);
|
||||||
this.targetPosition = new Vector3(0, 70, 0);
|
this.targetPosition = new Vector3(0, 70, 0);
|
||||||
@ -174,13 +215,25 @@ export default class Controls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateHeights() {
|
updateHeights() {
|
||||||
//TODO: this can be performance-improved by only intersecting the correct tile?
|
function between(n, min, max) {
|
||||||
|
return n >= min && n < max;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inTile = (pos, thisPos) => {
|
||||||
|
return between(pos.x, thisPos.x - this.tileSize.x, thisPos.x) &&
|
||||||
|
between(pos.z, thisPos.z - this.tileSize.z, thisPos.z);
|
||||||
|
};
|
||||||
|
|
||||||
|
let tileChildren = (targetPos) => {
|
||||||
|
return this.heightScene.children.filter(child => inTile(child.position, targetPos))
|
||||||
|
};
|
||||||
|
|
||||||
let rayStart = new Vector3(this.targetPosition.x, 300, this.targetPosition.z);
|
let rayStart = new Vector3(this.targetPosition.x, 300, this.targetPosition.z);
|
||||||
this.raycaster.set(rayStart, this.rayDirection);
|
this.raycaster.set(rayStart, this.rayDirection);
|
||||||
this.raycaster.near = 1;
|
this.raycaster.near = 1;
|
||||||
this.raycaster.far = 300;
|
this.raycaster.far = 300;
|
||||||
let intersects = this.raycaster.intersectObjects(this.heightScene.children);
|
let intersects = this.raycaster.intersectObjects(tileChildren(this.targetPosition));
|
||||||
|
|
||||||
if (intersects.length > 0){
|
if (intersects.length > 0){
|
||||||
this.minHeight = intersects[0].point.y;
|
this.minHeight = intersects[0].point.y;
|
||||||
//this.targetPosition.y = this.minHeight;
|
//this.targetPosition.y = this.minHeight;
|
||||||
@ -191,20 +244,17 @@ export default class Controls {
|
|||||||
rayStart.set(this.camera.position.x, 300, this.camera.position.z);
|
rayStart.set(this.camera.position.x, 300, this.camera.position.z);
|
||||||
this.raycaster.set(rayStart, this.rayDirection);
|
this.raycaster.set(rayStart, this.rayDirection);
|
||||||
intersects.length = 0;
|
intersects.length = 0;
|
||||||
intersects = this.raycaster.intersectObjects(this.heightScene.children);
|
intersects = this.raycaster.intersectObjects(tileChildren(this.camera.position));
|
||||||
if (intersects.length > 0){
|
if (intersects.length > 0){
|
||||||
if (intersects[0].point.y > this.minHeight){
|
if (intersects[0].point.y > this.minHeight){
|
||||||
this.minHeight = intersects[0].point.y;
|
this.minHeight = intersects[0].point.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
updateMouseMoves = () => {
|
updateMouseMoves = () => {
|
||||||
this.deltaMouse.set(this.lastMouse.x - this.mouse.x, this.lastMouse.y - this.mouse.y);
|
this.deltaMouse.set(this.lastMouse.x - this.mouse.x, this.lastMouse.y - this.mouse.y);
|
||||||
|
|
||||||
this.moveDelta.x = 0;
|
|
||||||
this.moveDelta.y = 0;
|
|
||||||
|
|
||||||
if (this.keyStates[Controls.KEYS.UP]){
|
if (this.keyStates[Controls.KEYS.UP]){
|
||||||
this.moveDelta.y -= 20;
|
this.moveDelta.y -= 20;
|
||||||
}
|
}
|
||||||
@ -238,6 +288,9 @@ export default class Controls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.lastMouse.copy(this.mouse);
|
this.lastMouse.copy(this.mouse);
|
||||||
|
|
||||||
|
this.moveDelta.x = 0;
|
||||||
|
this.moveDelta.y = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMouseWheel = event => {
|
onMouseWheel = event => {
|
||||||
@ -249,7 +302,7 @@ export default class Controls {
|
|||||||
|
|
||||||
if (this.targetDistance < this.settings.zoom.min) this.targetDistance = this.settings.zoom.min;
|
if (this.targetDistance < this.settings.zoom.min) this.targetDistance = this.settings.zoom.min;
|
||||||
if (this.targetDistance > this.settings.zoom.max) this.targetDistance = this.settings.zoom.max;
|
if (this.targetDistance > this.settings.zoom.max) this.targetDistance = this.settings.zoom.max;
|
||||||
}
|
};
|
||||||
|
|
||||||
onMouseMove = event => {
|
onMouseMove = event => {
|
||||||
this.mouse.set(event.clientX, event.clientY);
|
this.mouse.set(event.clientX, event.clientY);
|
||||||
@ -257,11 +310,13 @@ export default class Controls {
|
|||||||
if (this.state !== Controls.STATES.NONE){
|
if (this.state !== Controls.STATES.NONE){
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onMouseDown = event => {
|
onMouseDown = event => {
|
||||||
if (this.state !== Controls.STATES.NONE) return;
|
if (this.state !== Controls.STATES.NONE) return;
|
||||||
|
|
||||||
|
$(":focus").blur();
|
||||||
|
|
||||||
switch (event.button) {
|
switch (event.button) {
|
||||||
case Controls.KEYS.MOVE :
|
case Controls.KEYS.MOVE :
|
||||||
this.state = Controls.STATES.MOVE;
|
this.state = Controls.STATES.MOVE;
|
||||||
@ -272,7 +327,7 @@ export default class Controls {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onMouseUp = event => {
|
onMouseUp = event => {
|
||||||
if (this.state === Controls.STATES.NONE) return;
|
if (this.state === Controls.STATES.NONE) return;
|
||||||
@ -285,13 +340,91 @@ export default class Controls {
|
|||||||
if (this.state === Controls.STATES.ORBIT) this.state = Controls.STATES.NONE;
|
if (this.state === Controls.STATES.ORBIT) this.state = Controls.STATES.NONE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchDown = event => {
|
||||||
|
if (event.pointerType === "mouse") return;
|
||||||
|
|
||||||
|
$(":focus").blur();
|
||||||
|
|
||||||
|
this.touchStart.x = this.targetPosition.x;
|
||||||
|
this.touchStart.y = this.targetPosition.z;
|
||||||
|
this.state = Controls.STATES.MOVE;
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchMove = event => {
|
||||||
|
if (event.pointerType === "mouse") return;
|
||||||
|
if (this.state !== Controls.STATES.MOVE) return;
|
||||||
|
|
||||||
|
this.touchDelta.x = event.deltaX;
|
||||||
|
this.touchDelta.y = event.deltaY;
|
||||||
|
|
||||||
|
if (this.touchDelta.x !== 0 || this.touchDelta.y !== 0) {
|
||||||
|
this.touchDelta.rotateAround(Vector2_ZERO, -this.direction);
|
||||||
|
|
||||||
|
this.targetPosition.x = this.touchStart.x - (this.touchDelta.x * this.distance / this.element.clientHeight * this.settings.move.speed);
|
||||||
|
this.targetPosition.z = this.touchStart.y - (this.touchDelta.y * this.distance / this.element.clientHeight * this.settings.move.speed);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchUp = event => {
|
||||||
|
if (event.pointerType === "mouse") return;
|
||||||
|
|
||||||
|
this.state = Controls.STATES.NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchTiltDown = event => {
|
||||||
|
this.touchTiltStart = this.targetAngle;
|
||||||
|
this.state = Controls.STATES.ORBIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchTiltMove = event => {
|
||||||
|
if (this.state !== Controls.STATES.ORBIT) return;
|
||||||
|
|
||||||
|
this.targetAngle = this.touchTiltStart - (event.deltaY / this.element.clientHeight * Math.PI);
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchTiltUp = event => {
|
||||||
|
this.state = Controls.STATES.NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchRotateDown = event => {
|
||||||
|
this.lastTouchRotation = event.rotation;
|
||||||
|
this.state = Controls.STATES.ORBIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchRotateMove = event => {
|
||||||
|
if (this.state !== Controls.STATES.ORBIT) return;
|
||||||
|
|
||||||
|
let delta = event.rotation - this.lastTouchRotation;
|
||||||
|
this.lastTouchRotation = event.rotation;
|
||||||
|
if (delta > 180) delta -= 360;
|
||||||
|
if (delta < -180) delta += 360;
|
||||||
|
|
||||||
|
this.targetDirection += (delta * (Math.PI / 180)) * 1.4;
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchRotateUp = event => {
|
||||||
|
this.state = Controls.STATES.NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
onTouchZoomDown = event => {
|
||||||
|
this.touchZoomStart = this.targetDistance;
|
||||||
|
};
|
||||||
|
|
||||||
|
onTouchZoomMove = event => {
|
||||||
|
this.targetDistance = this.touchZoomStart / event.scale;
|
||||||
|
|
||||||
|
if (this.targetDistance < this.settings.zoom.min) this.targetDistance = this.settings.zoom.min;
|
||||||
|
if (this.targetDistance > this.settings.zoom.max) this.targetDistance = this.settings.zoom.max;
|
||||||
|
};
|
||||||
|
|
||||||
onKeyDown = event => {
|
onKeyDown = event => {
|
||||||
this.keyStates[event.keyCode] = true;
|
this.keyStates[event.keyCode] = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
onKeyUp = event => {
|
onKeyUp = event => {
|
||||||
this.keyStates[event.keyCode] = false;
|
this.keyStates[event.keyCode] = false;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,12 @@ export default class Compass {
|
|||||||
|
|
||||||
onBlueMapUpdateFrame = () => {
|
onBlueMapUpdateFrame = () => {
|
||||||
this.needle.css('transform', `rotate(${this.blueMap.controls.direction}rad)`);
|
this.needle.css('transform', `rotate(${this.blueMap.controls.direction}rad)`);
|
||||||
}
|
};
|
||||||
|
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
this.blueMap.controls.targetDirection = 0;
|
this.blueMap.controls.targetDirection = 0;
|
||||||
this.blueMap.controls.direction = this.blueMap.controls.direction % (Math.PI * 2);
|
this.blueMap.controls.direction = this.blueMap.controls.direction % (Math.PI * 2);
|
||||||
if (this.blueMap.controls.direction < -Math.PI) this.blueMap.controls.direction += Math.PI * 2;
|
if (this.blueMap.controls.direction < -Math.PI) this.blueMap.controls.direction += Math.PI * 2;
|
||||||
if (this.blueMap.controls.direction > Math.PI) this.blueMap.controls.direction -= Math.PI * 2;
|
if (this.blueMap.controls.direction > Math.PI) this.blueMap.controls.direction -= Math.PI * 2;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ export default class Info {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
this.blueMap.alert(
|
this.blueMap.toggleAlert('bluemap-info',
|
||||||
'<h1>Info</h1>' +
|
'<h1>Info</h1>' +
|
||||||
'Visit BlueMap on <a href="https://github.com/BlueMap-Minecraft">GitHub</a>!<br>' +
|
'Visit BlueMap on <a href="https://github.com/BlueMap-Minecraft">GitHub</a>!<br>' +
|
||||||
'BlueMap works best with <a href="https://www.google.com/chrome/">Chrome</a>.<br>' +
|
'BlueMap works best with <a href="https://www.google.com/chrome/">Chrome</a>.<br>' +
|
||||||
@ -45,5 +45,5 @@ export default class Info {
|
|||||||
'Rightclick-drag with your mouse to rotate your view.<br>' +
|
'Rightclick-drag with your mouse to rotate your view.<br>' +
|
||||||
'Scroll to zoom.<br>'
|
'Scroll to zoom.<br>'
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -53,11 +53,11 @@ export default class MapMenu {
|
|||||||
onMapClick = event => {
|
onMapClick = event => {
|
||||||
const map = $(event.target).attr('map');
|
const map = $(event.target).attr('map');
|
||||||
this.bluemap.changeMap(map);
|
this.bluemap.changeMap(map);
|
||||||
}
|
};
|
||||||
|
|
||||||
onBlueMapMapChange = () => {
|
onBlueMapMapChange = () => {
|
||||||
this.maplist.find('li').show();
|
this.maplist.find('li').show();
|
||||||
this.maplist.find('li[map=' + this.bluemap.map + ']').hide();
|
this.maplist.find('li[map=' + this.bluemap.map + ']').hide();
|
||||||
this.element.find('.selection').html(this.bluemap.settings[this.bluemap.map].name);
|
this.element.find('.selection').html(this.bluemap.settings[this.bluemap.map].name);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -43,13 +43,13 @@ export default class Settings {
|
|||||||
|
|
||||||
this.elementQuality = $(
|
this.elementQuality = $(
|
||||||
'<div id="bluemap-settings-quality" class="dropdown-container"><span class="selection">Quality: <span>Normal</span></span><div class="dropdown"><ul>' +
|
'<div id="bluemap-settings-quality" class="dropdown-container"><span class="selection">Quality: <span>Normal</span></span><div class="dropdown"><ul>' +
|
||||||
'<li quality="2">High</li>' +
|
'<li data-quality="2">High</li>' +
|
||||||
'<li quality="1" style="display: none">Normal</li>' +
|
'<li data-quality="1" style="display: none">Normal</li>' +
|
||||||
'<li quality="0.75">Fast</li>' +
|
'<li data-quality="0.75">Fast</li>' +
|
||||||
'</ul></div></div>'
|
'</ul></div></div>'
|
||||||
).prependTo(this.elementMenu);
|
).prependTo(this.elementMenu);
|
||||||
|
|
||||||
this.elementQuality.find('li[quality]').click(this.onQualityClick);
|
this.elementQuality.find('li[data-quality]').click(this.onQualityClick);
|
||||||
this.elementRenderDistance = $('<div id="bluemap-settings-render-distance" class="dropdown-container"></div>').prependTo(this.elementMenu);
|
this.elementRenderDistance = $('<div id="bluemap-settings-render-distance" class="dropdown-container"></div>').prependTo(this.elementMenu);
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
@ -84,10 +84,10 @@ export default class Settings {
|
|||||||
onQualityClick = (event) => {
|
onQualityClick = (event) => {
|
||||||
const target = event.target
|
const target = event.target
|
||||||
const desc = $(target).html();
|
const desc = $(target).html();
|
||||||
this.blueMap.quality = parseFloat($(target).attr('quality'));
|
this.blueMap.quality = parseFloat($(target).attr('data-quality'));
|
||||||
|
|
||||||
this.elementQuality.find('li').show();
|
this.elementQuality.find('li').show();
|
||||||
this.elementQuality.find(`li[quality="${this.blueMap.quality}"]`).hide();
|
this.elementQuality.find(`li[data-quality="${this.blueMap.quality}"]`).hide();
|
||||||
|
|
||||||
this.elementQuality.find('.selection > span').html(desc);
|
this.elementQuality.find('.selection > span').html(desc);
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ export default class Settings {
|
|||||||
this.elementMenu.animate({
|
this.elementMenu.animate({
|
||||||
width: 'toggle'
|
width: 'toggle'
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
};
|
||||||
|
|
||||||
pctToRenderDistance(value, defaultValue) {
|
pctToRenderDistance(value, defaultValue) {
|
||||||
let max = defaultValue * 5;
|
let max = defaultValue * 5;
|
||||||
|
@ -45,10 +45,6 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
.alert-close-button {
|
.alert-close-button {
|
||||||
/*position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
right: 5px;
|
|
||||||
*/
|
|
||||||
margin: -10px -10px 0px 0px;
|
margin: -10px -10px 0px 0px;
|
||||||
padding: 0 0 5px 5px;
|
padding: 0 0 5px 5px;
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
#bluemap-compass {
|
#bluemap-compass {
|
||||||
width: 30px;
|
width: 2rem;
|
||||||
height: 30px;
|
height: 2rem;
|
||||||
|
|
||||||
|
#bluemap-compass-needle {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover #bluemap-compass-needle {
|
&:hover #bluemap-compass-needle {
|
||||||
filter: invert(1);
|
filter: invert(1);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#bluemap-info {
|
#bluemap-info {
|
||||||
width: 30px;
|
width: 2rem;
|
||||||
height: 30px;
|
height: 2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#bluemap-mapmenu {
|
#bluemap-mapmenu {
|
||||||
width: 200px;
|
width: 15rem;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
.selection, .dropdown li {
|
.selection, .dropdown li {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
|
@ -2,13 +2,24 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
width: 60px;
|
width: 4rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0 5px 0 25px;
|
padding: 0 5px 0 25px;
|
||||||
font-family: inherit;
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
// remove number spinner firefox
|
||||||
|
-moz-appearance:textfield;
|
||||||
|
|
||||||
|
// remove number spinner webkit
|
||||||
|
&::-webkit-inner-spin-button,
|
||||||
|
&::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-pos]::before {
|
&[data-pos]::before {
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
#bluemap-settings {
|
#bluemap-settings {
|
||||||
width: 30px;
|
width: 2rem;
|
||||||
height: 30px;
|
height: 2rem;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#bluemap-settings.active:not(:hover) {
|
#bluemap-settings.active:not(:hover) {
|
||||||
@ -18,7 +23,7 @@
|
|||||||
|
|
||||||
#bluemap-settings-quality {
|
#bluemap-settings-quality {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 30px;
|
height: 2rem;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,17 +10,21 @@ html, body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 15px;
|
line-height: 1rem;
|
||||||
font-family: Verdana,Helvetica,Arial,sans-serif;
|
font-family: Verdana,Helvetica,Arial,sans-serif;
|
||||||
|
|
||||||
color: #333333;
|
color: #333333;
|
||||||
background-color: #dddddd;
|
background-color: #dddddd;
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
color: #333333;
|
color: #333333;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: 0px 1px 4px 0px rgba(50, 50, 50, 0.8);
|
box-shadow: 0 1px 4px 0 rgba(50, 50, 50, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
@ -37,7 +41,7 @@ html, body {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
transition: all 0.3s;
|
transition: background-color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-container:hover {
|
.dropdown-container:hover {
|
||||||
@ -53,7 +57,7 @@ html, body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
max-height: 0px;
|
max-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-container:hover > .dropdown {
|
.dropdown-container:hover > .dropdown {
|
||||||
@ -104,9 +108,15 @@ html, body {
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
|
|
||||||
line-height: 30px;
|
line-height: 2rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#bluemap-topright > *:not(:last-child) {
|
#bluemap-topright > *:not(:last-child) {
|
||||||
@ -118,9 +128,34 @@ html, body {
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
|
|
||||||
line-height: 30px;
|
line-height: 2rem;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
>:last-child {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
> :not(:first-child) {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
>:last-child {
|
||||||
|
width: 100%;
|
||||||
|
border-top: solid 1px #dddddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#bluemap-topleft > *:not(:last-child) {
|
#bluemap-topleft > *:not(:last-child) {
|
||||||
|
@ -22,7 +22,7 @@ module.exports = {
|
|||||||
contentBase: WORLD_DATA_PATH,
|
contentBase: WORLD_DATA_PATH,
|
||||||
compress: true,
|
compress: true,
|
||||||
port: 8080,
|
port: 8080,
|
||||||
hot: true,
|
hot: true
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
|
@ -104,6 +104,12 @@ maps: [
|
|||||||
# Default is enabled
|
# Default is enabled
|
||||||
renderEdges: true
|
renderEdges: true
|
||||||
|
|
||||||
|
# With this set to true, the generated files for this world are compressed using gzip to save A LOT of space.
|
||||||
|
# Files will be only 5% as big with compression!
|
||||||
|
# Note: If you are using NGINX or Apache to host your map, you can configure them to serve the compressed files directly.
|
||||||
|
# This is much better than disabling the compression.
|
||||||
|
useCompression: true
|
||||||
|
|
||||||
# HIRES is the high-resolution render of the map. Where you see every block.
|
# HIRES is the high-resolution render of the map. Where you see every block.
|
||||||
hires {
|
hires {
|
||||||
# Defines the size of one map-tile in blocks.
|
# Defines the size of one map-tile in blocks.
|
||||||
|
@ -29,7 +29,7 @@ allprojects {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile project(':BlueMapCLI')
|
compile project(':BlueMapCLI')
|
||||||
compile project(':BlueMapBukkit')
|
compile project(':BlueMapBukkit')
|
||||||
compile project(':BlueMapSponge')
|
//compile project(':BlueMapSponge')
|
||||||
}
|
}
|
||||||
|
|
||||||
assemble.dependsOn shadowJar {
|
assemble.dependsOn shadowJar {
|
||||||
|
Loading…
Reference in New Issue
Block a user