mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2025-02-16 12:31:58 +01:00
Add SpongePlugin implementation and fix and improve a lot of different things
This commit is contained in:
parent
987cc5a01b
commit
3fd724c74b
@ -250,7 +250,7 @@ private ResourcePack loadResources() throws IOException, NoSuchResourceException
|
||||
throw new IOException("Failed to create temporary resource file!", e);
|
||||
}
|
||||
try {
|
||||
ResourcePack.createDefaultResource(defaultResourceFile);
|
||||
ResourcePack.downloadDefaultResource(defaultResourceFile);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to create default resources!", e);
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ public void run() {
|
||||
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
||||
double pct = (double)renderedTiles / (double)tileCount;
|
||||
|
||||
long ert = (long)(((double) time / pct) * (1d - pct));
|
||||
long ert = (long)((time / pct) * (1d - pct));
|
||||
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
||||
|
||||
Logger.global.logInfo("Rendered " + renderedTiles + " of " + tileCount + " tiles in " + durationString);
|
||||
|
@ -27,7 +27,10 @@
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@ -36,6 +39,7 @@
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.web.WebServerConfig;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
|
||||
@ -46,6 +50,8 @@ public class ConfigurationFile implements WebServerConfig {
|
||||
|
||||
private String configVersion;
|
||||
|
||||
private boolean downloadAccepted;
|
||||
|
||||
private boolean webserverEnabled;
|
||||
private int webserverPort;
|
||||
private int webserverMaxConnections;
|
||||
@ -66,10 +72,11 @@ private ConfigurationFile(File configFile) throws IOException {
|
||||
CommentedConfigurationNode rootNode = configLoader.load();
|
||||
|
||||
configVersion = rootNode.getNode("version").getString("-");
|
||||
downloadAccepted = rootNode.getNode("accept-download").getBoolean(false);
|
||||
|
||||
loadWebConfig(rootNode.getNode("web"));
|
||||
|
||||
int defaultCount = (int) Math.max(Math.min((double) Runtime.getRuntime().availableProcessors() * 0.75, 16), 1);
|
||||
int defaultCount = (int) Math.max(Math.min(Runtime.getRuntime().availableProcessors() * 0.75, 16), 1);
|
||||
renderThreadCount = rootNode.getNode("renderThreadCount").getInt(defaultCount);
|
||||
if (renderThreadCount <= 0) renderThreadCount = defaultCount;
|
||||
|
||||
@ -143,6 +150,10 @@ public String getConfigVersion() {
|
||||
return configVersion;
|
||||
}
|
||||
|
||||
public boolean isDownloadAccepted() {
|
||||
return downloadAccepted;
|
||||
}
|
||||
|
||||
public int getRenderThreadCount() {
|
||||
return renderThreadCount;
|
||||
}
|
||||
@ -165,6 +176,12 @@ public static ConfigurationFile loadOrCreate(File configFile) throws IOException
|
||||
configFile.getParentFile().mkdirs();
|
||||
|
||||
FileUtils.copyURLToFile(ConfigurationFile.class.getResource("/bluemap.conf"), configFile, 10000, 10000);
|
||||
|
||||
//replace placeholder
|
||||
String content = new String(Files.readAllBytes(configFile.toPath()), StandardCharsets.UTF_8);
|
||||
content = content.replaceAll("%resource-file%", ResourcePack.MINECRAFT_CLIENT_URL);
|
||||
content = content.replaceAll("%date%", LocalDateTime.now().withNano(0).toString());
|
||||
Files.write(configFile.toPath(), content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
return new ConfigurationFile(configFile);
|
||||
@ -226,7 +243,7 @@ public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getWorldId() {
|
||||
public String getWorldPath() {
|
||||
return world;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ public String getBiomeId(Vector3i pos) {
|
||||
int z = pos.getZ() & 0xF;
|
||||
int biomeByteIndex = z * 16 + x;
|
||||
|
||||
return biomeIdMapper.get(biomes[biomeByteIndex]);
|
||||
return biomeIdMapper.get(biomes[biomeByteIndex] & 0xFF);
|
||||
}
|
||||
|
||||
private class Section {
|
||||
|
@ -74,7 +74,7 @@ public ChunkAnvil113(MCAWorld world, CompoundTag chunkTag) {
|
||||
biomes = new int[bs.length];
|
||||
|
||||
for (int i = 0; i < bs.length; i++) {
|
||||
biomes[i] = bs[i];
|
||||
biomes[i] = bs[i] & 0xFF;
|
||||
}
|
||||
}
|
||||
else if (tag instanceof IntArrayTag) {
|
||||
|
@ -172,8 +172,10 @@ public Block getBlock(Vector3i pos) throws ChunkNotGeneratedException {
|
||||
private BlockState getExtendedBlockState(Chunk chunk, Vector3i pos) throws ChunkNotGeneratedException {
|
||||
BlockState blockState = chunk.getBlockState(pos);
|
||||
|
||||
for (BlockStateExtension ext : BLOCK_STATE_EXTENSIONS.get(blockState.getId())) {
|
||||
blockState = ext.extend(this, pos, blockState);
|
||||
if (chunk instanceof ChunkAnvil112) { // only use extensions if old format chunk (1.12) in the new format block-states are saved witch extensions
|
||||
for (BlockStateExtension ext : BLOCK_STATE_EXTENSIONS.get(blockState.getFullId())) {
|
||||
blockState = ext.extend(this, pos, blockState);
|
||||
}
|
||||
}
|
||||
|
||||
return blockState;
|
||||
@ -314,6 +316,16 @@ public Vector3i getSpawnPoint() {
|
||||
return spawnPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateChunkCache() {
|
||||
CHUNK_CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateChunkCache(Vector2i chunk) {
|
||||
CHUNK_CACHE.invalidate(new WorldChunkHash(this, chunk));
|
||||
}
|
||||
|
||||
public BlockIdMapper getBlockIdMapper() {
|
||||
return blockIdMapper;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
public class DoorExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:wooden_door",
|
||||
"minecraft:oak_door",
|
||||
"minecraft:iron_door",
|
||||
"minecraft:spruce_door",
|
||||
"minecraft:birch_door",
|
||||
|
@ -25,7 +25,6 @@
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
@ -37,7 +36,12 @@
|
||||
public class DoublePlantExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:double_plant"
|
||||
"minecraft:sunflower",
|
||||
"minecraft:lilac",
|
||||
"minecraft:tall_grass",
|
||||
"minecraft:large_fern",
|
||||
"minecraft:rose_bush",
|
||||
"minecraft:peony"
|
||||
);
|
||||
|
||||
@Override
|
||||
@ -45,12 +49,7 @@ public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
if (state.getProperties().get("half").equals("upper")) {
|
||||
BlockState otherPlant = world.getBlockState(pos.add(Direction.DOWN.toVector()));
|
||||
|
||||
//copy all properties from the other half
|
||||
for (Entry<String, String> prop : otherPlant.getProperties().entrySet()) {
|
||||
if (!state.getProperties().containsKey(prop.getKey())) {
|
||||
state = state.with(prop.getKey(), prop.getValue());
|
||||
}
|
||||
}
|
||||
return otherPlant.with("half", "upper");
|
||||
}
|
||||
|
||||
return state;
|
||||
|
@ -33,7 +33,21 @@ public class GlassPaneConnectExtension extends ConnectSameOrFullBlockExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:glass_pane",
|
||||
"minecraft:stained_glass_pane",
|
||||
"minecraft:white_stained_glass_pane",
|
||||
"minecraft:orange_stained_glass_pane",
|
||||
"minecraft:magenta_stained_glass_pane",
|
||||
"minecraft:light_blue_white_stained_glass_pane",
|
||||
"minecraft:yellow_stained_glass_pane",
|
||||
"minecraft:lime_stained_glass_pane",
|
||||
"minecraft:pink_stained_glass_pane",
|
||||
"minecraft:gray_stained_glass_pane",
|
||||
"minecraft:light_gray_stained_glass_pane",
|
||||
"minecraft:cyan_stained_glass_pane",
|
||||
"minecraft:purple_stained_glass_pane",
|
||||
"minecraft:blue_stained_glass_pane",
|
||||
"minecraft:green_stained_glass_pane",
|
||||
"minecraft:red_stained_glass_pane",
|
||||
"minecraft:black_stained_glass_pane",
|
||||
"minecraft:iron_bars"
|
||||
);
|
||||
|
||||
|
@ -44,14 +44,14 @@ public class RedstoneExtension implements BlockStateExtension {
|
||||
|
||||
private static final Set<String> CONNECTIBLE = Sets.newHashSet(
|
||||
"minecraft:redstone_wire",
|
||||
"minecraft:unlit_redstone_torch",
|
||||
"minecraft:redstone_wall_torch",
|
||||
"minecraft:redstone_torch",
|
||||
"minecraft:stone_button",
|
||||
"minecraft:wooden_button",
|
||||
"minecraft:oak_button",
|
||||
"minecraft:stone_button",
|
||||
"minecraft:lever",
|
||||
"minecraft:stone_pressure_plate",
|
||||
"minecraft:wooden_pressure_plate",
|
||||
"minecraft:oak_pressure_plate",
|
||||
"minecraft:light_weighted_pressure_plate",
|
||||
"minecraft:heavy_weighted_pressure_plate"
|
||||
);
|
||||
@ -69,7 +69,7 @@ public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
|
||||
private String connection(MCAWorld world, Vector3i pos, BlockState state, Direction direction) {
|
||||
BlockState next = world.getBlockState(pos.add(direction.toVector()));
|
||||
if (CONNECTIBLE.contains(next.getId())) return "side";
|
||||
if (CONNECTIBLE.contains(next.getFullId())) return "side";
|
||||
|
||||
//TODO: up
|
||||
|
||||
|
@ -35,15 +35,15 @@
|
||||
public class SnowyExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:grass",
|
||||
"minecraft:dirt"
|
||||
"minecraft:grass_block",
|
||||
"minecraft:podzol"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
BlockState above = world.getBlockState(pos.add(0, 1, 0));
|
||||
|
||||
if (above.getId().equals("minecraft:snow_layer") || above.getId().equals("minecraft:snow")) {
|
||||
if (above.getFullId().equals("minecraft:snow") || above.getFullId().equals("minecraft:snow_block")) {
|
||||
return state.with("snowy", "true");
|
||||
} else {
|
||||
return state.with("snowy", "false");
|
||||
|
@ -38,7 +38,7 @@ public class StairShapeExtension implements BlockStateExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:oak_stairs",
|
||||
"minecraft:stone_stairs",
|
||||
"minecraft:cobblestone_stairs",
|
||||
"minecraft:brick_stairs",
|
||||
"minecraft:stone_brick_stairs",
|
||||
"minecraft:nether_brick_stairs",
|
||||
@ -102,7 +102,7 @@ public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
}
|
||||
|
||||
private boolean isStairs(BlockState state) {
|
||||
return AFFECTED_BLOCK_IDS.contains(state.getId());
|
||||
return AFFECTED_BLOCK_IDS.contains(state.getFullId());
|
||||
}
|
||||
|
||||
private boolean isEqualStairs(BlockState stair1, BlockState stair2) {
|
||||
|
@ -32,7 +32,7 @@
|
||||
public class WoodenFenceConnectExtension extends ConnectSameOrFullBlockExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:fence",
|
||||
"minecraft:oak_fence",
|
||||
"minecraft:spruce_fence",
|
||||
"minecraft:birch_fence",
|
||||
"minecraft:jungle_fence",
|
||||
|
@ -162,6 +162,12 @@ public Vector3i getSpawnPoint() {
|
||||
public Collection<Vector2i> getChunkList(long modifiedSince) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateChunkCache() {}
|
||||
|
||||
@Override
|
||||
public void invalidateChunkCache(Vector2i chunk) {}
|
||||
|
||||
}
|
||||
|
||||
|
@ -37,12 +37,14 @@ public interface ExtendedBlockContext extends BlockContext {
|
||||
* This returns neighbour blocks.<br>
|
||||
* The distance can not be larger than two blocks in each direction!<br>
|
||||
*/
|
||||
@Override
|
||||
Block getRelativeBlock(Vector3i direction);
|
||||
|
||||
/**
|
||||
* This returns neighbour blocks.<br>
|
||||
* The distance can not be larger than two blocks in each direction!<br>
|
||||
*/
|
||||
@Override
|
||||
default Block getRelativeBlock(int x, int y, int z){
|
||||
return getRelativeBlock(new Vector3i(x, y, z));
|
||||
}
|
||||
|
@ -153,8 +153,8 @@ public Vector2i posToTile(Vector3i pos){
|
||||
public Vector2i posToTile(Vector3d pos){
|
||||
pos = pos.sub(new Vector3d(gridOrigin.getX(), 0.0, gridOrigin.getY()));
|
||||
return Vector2i.from(
|
||||
(int) Math.floor(pos.getX() / (double) getTileSize().getX()),
|
||||
(int) Math.floor(pos.getZ() / (double) getTileSize().getY())
|
||||
(int) Math.floor(pos.getX() / getTileSize().getX()),
|
||||
(int) Math.floor(pos.getZ() / getTileSize().getY())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ public BlockStateModel build() throws NoSuchTextureException {
|
||||
BlockStateModel model = new BlockStateModel();
|
||||
|
||||
for (WeighedArrayList<BlockModelResource> bmrList : resource.getModelResources()){
|
||||
BlockModelResource bmr = bmrList.get((int) Math.floor(MathUtil.hashToFloat(context.getPosition(), 23489756) * (float) bmrList.size()));
|
||||
BlockModelResource bmr = bmrList.get((int) Math.floor(MathUtil.hashToFloat(context.getPosition(), 23489756) * bmrList.size()));
|
||||
|
||||
model.merge(fromModelResource(bmr));
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ private Vector2f getColorMapPosition(String biomeId, int blocksAboveSeaLevel){
|
||||
throw new NoSuchElementException("No biome found with id: " + biomeId);
|
||||
}
|
||||
|
||||
float adjTemp = (float) GenericMath.clamp(bi.temp - (0.00166667 * (double) blocksAboveSeaLevel), 0d, 1d);
|
||||
float adjTemp = (float) GenericMath.clamp(bi.temp - (0.00166667 * blocksAboveSeaLevel), 0d, 1d);
|
||||
float adjHumidity = (float) GenericMath.clamp(bi.humidity, 0d, 1d) * adjTemp;
|
||||
return new Vector2f(1 - adjTemp, 1 - adjHumidity);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -50,6 +51,8 @@
|
||||
|
||||
public class ResourcePack {
|
||||
|
||||
public static final String MINECRAFT_CLIENT_URL = "https://launcher.mojang.com/v1/objects/8c325a0c5bd674dd747d6ebaa4c791fd363ad8a9/client.jar";
|
||||
|
||||
private Map<Path, Resource> resources;
|
||||
|
||||
private TextureProvider textureProvider;
|
||||
@ -121,6 +124,13 @@ private void overrideResourcesWithZipFile(File resourceFile){
|
||||
if (file.isDirectory()) continue;
|
||||
|
||||
Path resourcePath = Paths.get("", file.getName().split("/"));
|
||||
if (
|
||||
!resourcePath.startsWith(Paths.get("assets", "minecraft", "blockstates")) &&
|
||||
!resourcePath.startsWith(Paths.get("assets", "minecraft", "models", "block")) &&
|
||||
!resourcePath.startsWith(Paths.get("assets", "minecraft", "textures", "block")) &&
|
||||
!resourcePath.startsWith(Paths.get("assets", "minecraft", "textures", "colormap"))
|
||||
) continue;
|
||||
|
||||
InputStream fileInputStream = zipFile.getInputStream(file);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(Math.max(8, (int) file.getSize()));
|
||||
@ -179,11 +189,10 @@ public InputStream getStream(){
|
||||
|
||||
}
|
||||
|
||||
public static void createDefaultResource(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
FileUtils.copyURLToFile(ResourcePack.class.getResource("/DefaultResources.zip"), file, 10000, 10000);
|
||||
}
|
||||
public static void downloadDefaultResource(File file) throws IOException {
|
||||
if (file.exists()) file.delete();
|
||||
file.getParentFile().mkdirs();
|
||||
FileUtils.copyURLToFile(new URL(MINECRAFT_CLIENT_URL), file, 10000, 10000);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
package de.bluecolored.bluemap.core.world;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.context.BlockContext;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
@ -87,5 +88,15 @@ private void calculateLight(BlockContext context) {
|
||||
blockLight = (float) Math.max(neighbor.getBlockLightLevel(), blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("pos", getPosition())
|
||||
.add("biome", getBiome())
|
||||
.add("blocklight", getBlockLightLevel())
|
||||
.add("sunlight", getSunLightLevel())
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -74,5 +74,25 @@ public default Collection<Vector2i> getChunkList(){
|
||||
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
||||
*/
|
||||
public Collection<Vector2i> getChunkList(long modifiedSince);
|
||||
|
||||
/**
|
||||
* Invalidates the complete chunk cache (if there is a cache), so that every chunk has to be reloaded from disk
|
||||
*/
|
||||
public void invalidateChunkCache();
|
||||
|
||||
/**
|
||||
* Invalidates the chunk from the chunk-cache (if there is a cache), so that the chunk has to be reloaded from disk
|
||||
*/
|
||||
public void invalidateChunkCache(Vector2i chunk);
|
||||
|
||||
/**
|
||||
* Returns the ChunkPosition for a BlockPosition
|
||||
*/
|
||||
public default Vector2i blockPosToChunkPos(Vector3i block) {
|
||||
return new Vector2i(
|
||||
block.getX() >> 4,
|
||||
block.getZ() >> 4
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
Binary file not shown.
@ -10,6 +10,15 @@
|
||||
# and update configuration correctly.
|
||||
version: "1.0.0"
|
||||
|
||||
# By changing the setting (accept-download) below to TRUE you are indicating that you have accepted mojang's EULA (https://account.mojang.com/documents/minecraft_eula),
|
||||
# you confirm that you own a license to Minecraft (Java Edition)
|
||||
# and you agree that BlueMap will download and use this file for you: %resource-file%
|
||||
# (Alternatively you can download the file yourself and store it here: ./config/bluemap/resourcepacks/client.jar)
|
||||
# This file contains resources that belong to mojang and you must not redistribute it or do anything else that is not compilant with mojang's EULA.
|
||||
# BlueMap uses resources in this file to generate the 3D-Models used for the map and texture them. (BlueMap will not work without those resources.)
|
||||
# %date%
|
||||
accept-download: false
|
||||
|
||||
web {
|
||||
# With this setting you can disable the web-server.
|
||||
# This is usefull if you want to only render the map-data for later use, or if you setup your own webserver.
|
||||
|
Binary file not shown.
@ -351,8 +351,8 @@ BlueMap.prototype.loadHiresMaterial = function (callback) {
|
||||
texture.generateMipmaps = false;
|
||||
texture.magFilter = THREE.NearestFilter;
|
||||
texture.minFilter = THREE.NearestFilter;
|
||||
texture.wrapS = THREE.RepeatWrapping;
|
||||
texture.wrapT = THREE.RepeatWrapping;
|
||||
texture.wrapS = THREE.ClampToEdgeWrapping;
|
||||
texture.wrapT = THREE.ClampToEdgeWrapping;
|
||||
texture.flipY = false;
|
||||
texture.needsUpdate = true;
|
||||
texture.flatShading = true;
|
||||
|
@ -0,0 +1,312 @@
|
||||
package de.bluecolored.bluemap.sponge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.command.CommandResult;
|
||||
import org.spongepowered.api.command.CommandSource;
|
||||
import org.spongepowered.api.command.args.GenericArguments;
|
||||
import org.spongepowered.api.command.spec.CommandSpec;
|
||||
import org.spongepowered.api.text.Text;
|
||||
import org.spongepowered.api.text.action.TextActions;
|
||||
import org.spongepowered.api.text.format.TextColors;
|
||||
import org.spongepowered.api.world.Locatable;
|
||||
import org.spongepowered.api.world.Location;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class Commands {
|
||||
|
||||
private SpongePlugin plugin;
|
||||
|
||||
public Commands(SpongePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public CommandSpec createRootCommand() {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
CommandSpec debugCommand = CommandSpec.builder()
|
||||
.executor((source, args) -> {
|
||||
if (source instanceof Locatable) {
|
||||
try {
|
||||
Location<org.spongepowered.api.world.World> loc = ((Locatable) source).getLocation();
|
||||
UUID worldUuid = loc.getExtent().getUniqueId();
|
||||
World world = plugin.getWorld(worldUuid);
|
||||
Block block = world.getBlock(loc.getBlockPosition());
|
||||
Block blockBelow = world.getBlock(loc.getBlockPosition().add(0, -1, 0));
|
||||
|
||||
source.sendMessages(Lists.newArrayList(
|
||||
Text.of("Block: " + block),
|
||||
Text.of("Block below: " + blockBelow)
|
||||
));
|
||||
} catch (ChunkNotGeneratedException e) {
|
||||
Logger.global.logError("Failed to debug!", e);
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
|
||||
return CommandSpec.builder()
|
||||
.description(Text.of("Displays BlueMaps render status"))
|
||||
.permission("bluemap.status")
|
||||
.childArgumentParseExceptionFallback(false)
|
||||
.child(createReloadCommand(), "reload")
|
||||
.child(createPauseRenderCommand(), "pause")
|
||||
.child(createResumeRenderCommand(), "resume")
|
||||
.child(createRenderCommand(), "render")
|
||||
//.child(debugCommand, "debug")
|
||||
.executor((source, args) -> {
|
||||
source.sendMessages(createStatusMessage());
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public CommandSpec createReloadCommand() {
|
||||
return CommandSpec.builder()
|
||||
.description(Text.of("Reloads all resources and configuration-files"))
|
||||
.permission("bluemap.reload")
|
||||
.executor((source, args) -> {
|
||||
try {
|
||||
plugin.reload();
|
||||
|
||||
if (plugin.isLoaded()) {
|
||||
source.sendMessage(Text.of(TextColors.GREEN, "BlueMap reloaded!"));
|
||||
return CommandResult.success();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColors.RED, "Could not load BlueMap! See the console for details!"));
|
||||
return CommandResult.empty();
|
||||
}
|
||||
} catch (IOException | NoSuchResourceException ex) {
|
||||
Logger.global.logError("Failed to reload BlueMap!", ex);
|
||||
|
||||
source.sendMessage(Text.of(TextColors.RED, "There was an error reloading BlueMap! See the console for details!"));
|
||||
return CommandResult.empty();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public CommandSpec createPauseRenderCommand() {
|
||||
return CommandSpec.builder()
|
||||
.description(Text.of("Pauses all rendering"))
|
||||
.permission("bluemap.pause")
|
||||
.executor((source, args) -> {
|
||||
if (plugin.getRenderManager().isRunning()) {
|
||||
plugin.getRenderManager().stop();
|
||||
source.sendMessage(Text.of(TextColors.GREEN, "BlueMap rendering paused!"));
|
||||
return CommandResult.success();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColors.RED, "BlueMap rendering are already paused!"));
|
||||
return CommandResult.empty();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public CommandSpec createResumeRenderCommand() {
|
||||
return CommandSpec.builder()
|
||||
.description(Text.of("Resumes all paused rendering"))
|
||||
.permission("bluemap.resume")
|
||||
.executor((source, args) -> {
|
||||
if (!plugin.getRenderManager().isRunning()) {
|
||||
plugin.getRenderManager().start();
|
||||
source.sendMessage(Text.of(TextColors.GREEN, "BlueMap renders resumed!"));
|
||||
return CommandResult.success();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColors.RED, "BlueMap renders are already running!"));
|
||||
return CommandResult.empty();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public CommandSpec createRenderCommand() {
|
||||
return CommandSpec.builder()
|
||||
.description(Text.of("Renders the whole world"))
|
||||
.permission("bluemap.rendertask.create.world")
|
||||
.childArgumentParseExceptionFallback(false)
|
||||
.child(createPrioritizeTaskCommand(), "prioritize")
|
||||
.child(createRemoveTaskCommand(), "remove")
|
||||
.arguments(GenericArguments.optional(GenericArguments.world(Text.of("world"))))
|
||||
.executor((source, args) -> {
|
||||
org.spongepowered.api.world.World spongeWorld = args.<org.spongepowered.api.world.World>getOne("world").orElse(null);
|
||||
if (spongeWorld == null && source instanceof Locatable) {
|
||||
Location<org.spongepowered.api.world.World> loc = ((Locatable) source).getLocation();
|
||||
spongeWorld = loc.getExtent();
|
||||
} else {
|
||||
source.sendMessage(Text.of(TextColors.RED, "You have to define a world to render!"));
|
||||
return CommandResult.empty();
|
||||
}
|
||||
|
||||
World world = plugin.getWorld(spongeWorld.getUniqueId());
|
||||
if (world == null) {
|
||||
source.sendMessage(Text.of(TextColors.RED, "This world is not loaded with BlueMap! Maybe it is not configured?"));
|
||||
}
|
||||
|
||||
world.invalidateChunkCache();
|
||||
|
||||
Sponge.getScheduler().createTaskBuilder()
|
||||
.async()
|
||||
.execute(() -> createWorldRenderTask(source, world))
|
||||
.submit(plugin);
|
||||
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public CommandSpec createPrioritizeTaskCommand() {
|
||||
return CommandSpec.builder()
|
||||
.description(Text.of("Prioritizes the render-task with the given uuid"))
|
||||
.permission("bluemap.rendertask.prioritize")
|
||||
.arguments(GenericArguments.uuid(Text.of("task-uuid")))
|
||||
.executor((source, args) -> {
|
||||
Optional<UUID> uuid = args.<UUID>getOne("task-uuid");
|
||||
if (!uuid.isPresent()) {
|
||||
source.sendMessage(Text.of("You need to specify a task-uuid"));
|
||||
return CommandResult.empty();
|
||||
}
|
||||
|
||||
for (RenderTask task : plugin.getRenderManager().getRenderTasks()) {
|
||||
if (task.getUuid().equals(uuid.get())) {
|
||||
plugin.getRenderManager().prioritizeRenderTask(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
source.sendMessages(createStatusMessage());
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public CommandSpec createRemoveTaskCommand() {
|
||||
return CommandSpec.builder()
|
||||
.description(Text.of("Removes the render-task with the given uuid"))
|
||||
.permission("bluemap.rendertask.remove")
|
||||
.arguments(GenericArguments.uuid(Text.of("task-uuid")))
|
||||
.executor((source, args) -> {
|
||||
Optional<UUID> uuid = args.<UUID>getOne("task-uuid");
|
||||
if (!uuid.isPresent()) {
|
||||
source.sendMessage(Text.of("You need to specify a task-uuid"));
|
||||
return CommandResult.empty();
|
||||
}
|
||||
|
||||
for (RenderTask task : plugin.getRenderManager().getRenderTasks()) {
|
||||
if (task.getUuid().equals(uuid.get())) {
|
||||
plugin.getRenderManager().removeRenderTask(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
source.sendMessages(createStatusMessage());
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<Text> createStatusMessage(){
|
||||
List<Text> lines = new ArrayList<>();
|
||||
|
||||
RenderManager renderer = plugin.getRenderManager();
|
||||
|
||||
lines.add(Text.EMPTY);
|
||||
lines.add(Text.of(TextColors.BLUE, "Tile-Updates:"));
|
||||
|
||||
if (renderer.isRunning()) {
|
||||
lines.add(Text.of(TextColors.WHITE, " Render-Threads are ", Text.of(TextActions.runCommand("/bluemap pause"), TextActions.showText(Text.of("click to pause rendering")), TextColors.GREEN, "running"), TextColors.GRAY, "!"));
|
||||
} else {
|
||||
lines.add(Text.of(TextColors.WHITE, " Render-Threads are ", Text.of(TextActions.runCommand("/bluemap resume"), TextActions.showText(Text.of("click to resume rendering")), TextColors.RED, "paused"), TextColors.GRAY, "!"));
|
||||
}
|
||||
|
||||
lines.add(Text.of(TextColors.WHITE, " Scheduled tile-updates: ", Text.of(TextActions.showText(Text.of("tiles waiting for a free render-thread")), TextColors.GOLD, renderer.getQueueSize()), Text.of(TextActions.showText(Text.of("tiles waiting for world-save")), TextColors.GRAY, " + " + plugin.getUpdateHandler().getUpdateBufferCount())));
|
||||
|
||||
RenderTask[] tasks = renderer.getRenderTasks();
|
||||
if (tasks.length > 0) {
|
||||
RenderTask task = tasks[0];
|
||||
|
||||
long time = task.getActiveTime();
|
||||
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
||||
double pct = (double)task.getRenderedTileCount() / (double)(task.getRenderedTileCount() + task.getRemainingTileCount());
|
||||
|
||||
long ert = (long)((time / pct) * (1d - pct));
|
||||
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
||||
|
||||
lines.add(Text.of(TextColors.BLUE, "Current task:"));
|
||||
lines.add(Text.of(" ", createCancelTaskText(task), TextColors.WHITE, " Task ", TextColors.GOLD, task.getName(), TextColors.WHITE, " for map ", TextActions.showText(Text.of(TextColors.WHITE, "World: ", TextColors.GOLD, task.getMapType().getWorld().getName())), TextColors.GOLD, task.getMapType().getName()));
|
||||
lines.add(Text.of(TextColors.WHITE, " rendered ", TextColors.GOLD, task.getRenderedTileCount(), TextColors.WHITE, " tiles ", TextColors.GRAY, "(" + (Math.round(pct * 1000)/10.0) + "%)", TextColors.WHITE, " in ", TextColors.GOLD, durationString));
|
||||
lines.add(Text.of(TextColors.WHITE, " with ", TextColors.GOLD, task.getRemainingTileCount(), TextColors.WHITE, " tiles to go. ETA: ", TextColors.GOLD, ertDurationString));
|
||||
}
|
||||
|
||||
if (tasks.length > 1) {
|
||||
lines.add(Text.of(TextColors.BLUE, "Waiting tasks:"));
|
||||
for (int i = 1; i < tasks.length; i++) {
|
||||
RenderTask task = tasks[i];
|
||||
lines.add(Text.of(" ", createCancelTaskText(task), createPrioritizeTaskText(task), TextColors.WHITE, " Task ", TextColors.GOLD, task.getName(), TextColors.WHITE, " for map ", Text.of(TextActions.showText(Text.of(TextColors.WHITE, "World: ", TextColors.GOLD, task.getMapType().getWorld().getName())), TextColors.GOLD, task.getMapType().getName()), TextColors.GRAY, " (" + task.getRemainingTileCount() + " tiles)"));
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
private Text createCancelTaskText(RenderTask task) {
|
||||
return Text.of(TextActions.runCommand("/bluemap render remove " + task.getUuid()), TextActions.showText(Text.of("click to remove this render-task")), TextColors.RED, "[X]");
|
||||
}
|
||||
|
||||
private Text createPrioritizeTaskText(RenderTask task) {
|
||||
return Text.of(TextActions.runCommand("/bluemap render prioritize " + task.getUuid()), TextActions.showText(Text.of("click to prioritize this render-task")), TextColors.GREEN, "[^]");
|
||||
}
|
||||
|
||||
private void createWorldRenderTask(CommandSource source, World world) {
|
||||
source.sendMessage(Text.of(TextColors.GOLD, "Collecting chunks to render..."));
|
||||
Collection<Vector2i> chunks = world.getChunkList();
|
||||
source.sendMessage(Text.of(TextColors.GREEN, chunks.size() + " chunks found!"));
|
||||
|
||||
for (MapType map : SpongePlugin.getInstance().getMapTypes()) {
|
||||
if (!map.getWorld().getUUID().equals(world.getUUID())) continue;
|
||||
|
||||
source.sendMessage(Text.of(TextColors.GOLD, "Collecting tiles for map '" + map.getId() + "'"));
|
||||
|
||||
HiresModelManager hmm = map.getTileRenderer().getHiresModelManager();
|
||||
Set<Vector2i> tiles = new HashSet<>();
|
||||
for (Vector2i chunk : chunks) {
|
||||
Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16);
|
||||
tiles.add(hmm.posToTile(minBlockPos));
|
||||
tiles.add(hmm.posToTile(minBlockPos.add(0, 0, 15)));
|
||||
tiles.add(hmm.posToTile(minBlockPos.add(15, 0, 0)));
|
||||
tiles.add(hmm.posToTile(minBlockPos.add(15, 0, 15)));
|
||||
}
|
||||
|
||||
RenderTask task = new RenderTask("world-render", map);
|
||||
task.addTiles(tiles);
|
||||
task.optimizeQueue();
|
||||
plugin.getRenderManager().addRenderTask(task);
|
||||
|
||||
source.sendMessage(Text.of(TextColors.GREEN, tiles.size() + " tiles found! Task created."));
|
||||
}
|
||||
|
||||
source.sendMessage(Text.of(TextColors.GREEN, "All render tasks created! Use /bluemap to view the progress!"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of BlueMapSponge, 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.sponge;
|
||||
|
||||
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.ChunkNotGeneratedException;
|
||||
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, ChunkNotGeneratedException {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package de.bluecolored.bluemap.sponge;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.block.BlockSnapshot;
|
||||
import org.spongepowered.api.data.Transaction;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.Order;
|
||||
import org.spongepowered.api.event.block.ChangeBlockEvent;
|
||||
import org.spongepowered.api.event.filter.type.Exclude;
|
||||
import org.spongepowered.api.event.world.SaveWorldEvent;
|
||||
import org.spongepowered.api.event.world.chunk.PopulateChunkEvent;
|
||||
import org.spongepowered.api.world.Location;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
|
||||
public class MapUpdateHandler {
|
||||
|
||||
public Multimap<MapType, Vector2i> updateBuffer;
|
||||
|
||||
public MapUpdateHandler() {
|
||||
updateBuffer = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||
|
||||
Sponge.getEventManager().registerListeners(SpongePlugin.getInstance(), this);
|
||||
}
|
||||
|
||||
@Listener(order = Order.POST)
|
||||
public void onWorldSave(SaveWorldEvent.Post evt) {
|
||||
UUID worldUuid = evt.getTargetWorld().getUniqueId();
|
||||
RenderManager renderManager = SpongePlugin.getInstance().getRenderManager();
|
||||
|
||||
synchronized (updateBuffer) {
|
||||
Iterator<MapType> iterator = updateBuffer.keys().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MapType map = iterator.next();
|
||||
if (map.getWorld().getUUID().equals(worldUuid)) {
|
||||
renderManager.createTickets(map, updateBuffer.get(map));
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Listener(order = Order.POST)
|
||||
@Exclude({ChangeBlockEvent.Post.class, ChangeBlockEvent.Pre.class, ChangeBlockEvent.Modify.class})
|
||||
public void onBlockChange(ChangeBlockEvent evt) {
|
||||
synchronized (updateBuffer) {
|
||||
for (Transaction<BlockSnapshot> tr : evt.getTransactions()) {
|
||||
if (!tr.isValid()) continue;
|
||||
|
||||
Optional<Location<org.spongepowered.api.world.World>> ow = tr.getFinal().getLocation();
|
||||
if (ow.isPresent()) {
|
||||
updateBlock(ow.get().getExtent().getUniqueId(), ow.get().getPosition().toInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Listener(order = Order.POST)
|
||||
public void onChunkPopulate(PopulateChunkEvent.Post evt) {
|
||||
UUID world = evt.getTargetChunk().getWorld().getUniqueId();
|
||||
|
||||
int x = evt.getTargetChunk().getPosition().getX();
|
||||
int z = evt.getTargetChunk().getPosition().getZ();
|
||||
|
||||
// also update the chunks around, because they might be modified or not rendered yet due to finalizations
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
updateChunk(world, new Vector2i(x + dx, z + dz));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChunk(UUID world, Vector2i chunkPos) {
|
||||
Vector3i min = new Vector3i(chunkPos.getX() * 16, 0, chunkPos.getY() * 16);
|
||||
Vector3i max = min.add(15, 255, 15);
|
||||
|
||||
Vector3i xmin = new Vector3i(min.getX(), 0, max.getY());
|
||||
Vector3i xmax = new Vector3i(max.getX(), 255, min.getY());
|
||||
|
||||
//update all corners so we always update all tiles containing this chunk
|
||||
synchronized (updateBuffer) {
|
||||
updateBlock(world, min);
|
||||
updateBlock(world, max);
|
||||
updateBlock(world, xmin);
|
||||
updateBlock(world, xmax);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBlock(UUID world, Vector3i pos){
|
||||
synchronized (updateBuffer) {
|
||||
for (MapType mapType : SpongePlugin.getInstance().getMapTypes()) {
|
||||
if (mapType.getWorld().getUUID().equals(world)) {
|
||||
mapType.getWorld().invalidateChunkCache(mapType.getWorld().blockPosToChunkPos(pos));
|
||||
|
||||
Vector2i tile = mapType.getTileRenderer().getHiresModelManager().posToTile(pos);
|
||||
updateBuffer.put(mapType, tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getUpdateBufferCount() {
|
||||
return updateBuffer.size();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
package de.bluecolored.bluemap.sponge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
|
||||
public class RenderManager {
|
||||
|
||||
private boolean running;
|
||||
|
||||
private Thread[] renderThreads;
|
||||
private ArrayDeque<RenderTicket> renderTickets;
|
||||
private Map<RenderTicket, RenderTicket> renderTicketMap;
|
||||
private Deque<RenderTask> renderTasks;
|
||||
|
||||
public RenderManager(int threadCount) {
|
||||
running = false;
|
||||
renderThreads = new Thread[threadCount];
|
||||
renderTickets = new ArrayDeque<>(1000);
|
||||
renderTicketMap = new HashMap<>(1000);
|
||||
renderTasks = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
stop(); //ensure everything is stopped first
|
||||
|
||||
for (int i = 0; i < renderThreads.length; i++) {
|
||||
renderThreads[i] = new Thread(this::renderThread);
|
||||
renderThreads[i].setDaemon(true);
|
||||
renderThreads[i].setPriority(Thread.MIN_PRIORITY);
|
||||
renderThreads[i].start();
|
||||
}
|
||||
|
||||
running = true;
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
for (int i = 0; i < renderThreads.length; i++) {
|
||||
if (renderThreads[i] != null) {
|
||||
renderThreads[i].interrupt();
|
||||
renderThreads[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
running = false;
|
||||
}
|
||||
|
||||
public void addRenderTask(RenderTask task) {
|
||||
synchronized (renderTasks) {
|
||||
renderTasks.add(task);
|
||||
}
|
||||
}
|
||||
|
||||
public RenderTicket createTicket(MapType mapType, Vector2i tile) {
|
||||
RenderTicket ticket = new RenderTicket(mapType, tile);
|
||||
synchronized (renderTickets) {
|
||||
if (renderTicketMap.putIfAbsent(ticket, ticket) == null) {
|
||||
renderTickets.add(ticket);
|
||||
return ticket;
|
||||
} else {
|
||||
return renderTicketMap.get(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<RenderTicket> createTickets(MapType mapType, Collection<Vector2i> tiles) {
|
||||
if (tiles.size() < 0) return Collections.emptyList();
|
||||
|
||||
Collection<RenderTicket> tickets = new ArrayList<>(tiles.size());
|
||||
synchronized (renderTickets) {
|
||||
for (Vector2i tile : tiles) {
|
||||
tickets.add(createTicket(mapType, tile));
|
||||
}
|
||||
}
|
||||
|
||||
return tickets;
|
||||
}
|
||||
|
||||
public boolean prioritizeRenderTask(RenderTask renderTask) {
|
||||
synchronized (renderTasks) {
|
||||
if (renderTasks.remove(renderTask)) {
|
||||
renderTasks.addFirst(renderTask);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeRenderTask(RenderTask renderTask) {
|
||||
synchronized (renderTasks) {
|
||||
return renderTasks.remove(renderTask);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderThread() {
|
||||
RenderTicket ticket = null;
|
||||
|
||||
while (!Thread.interrupted()) {
|
||||
synchronized (renderTickets) {
|
||||
ticket = renderTickets.poll();
|
||||
if (ticket != null) renderTicketMap.remove(ticket);
|
||||
}
|
||||
|
||||
if (ticket == null) {
|
||||
synchronized (renderTasks) {
|
||||
RenderTask task = renderTasks.peek();
|
||||
if (task != null) {
|
||||
ticket = task.poll();
|
||||
if (task.isFinished()) renderTasks.poll();
|
||||
task.getMapType().getTileRenderer().save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ticket != null) {
|
||||
try {
|
||||
ticket.render();
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to render tile " + ticket.getTile() + " of map '" + ticket.getMapType().getId() + "'!", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(1000); // we don't need a super fast response time, so waiting a second is totally fine
|
||||
} catch (InterruptedException e) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
return renderTickets.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the deque with the render tasks in order as array
|
||||
*/
|
||||
public RenderTask[] getRenderTasks(){
|
||||
return renderTasks.toArray(new RenderTask[renderTasks.size()]);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package de.bluecolored.bluemap.sponge;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2d;
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
public class RenderTask {
|
||||
|
||||
private final UUID uuid;
|
||||
private String name;
|
||||
|
||||
private final MapType mapType;
|
||||
private Deque<Vector2i> renderTiles;
|
||||
|
||||
private long firstTileTime;
|
||||
private long additionalRunTime;
|
||||
private int renderedTiles;
|
||||
|
||||
private UUID taskOwner;
|
||||
|
||||
public RenderTask(String name, MapType mapType) {
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.name = name;
|
||||
this.mapType = mapType;
|
||||
this.renderTiles = new ArrayDeque<>();
|
||||
this.firstTileTime = -1;
|
||||
this.additionalRunTime = 0;
|
||||
this.renderedTiles = 0;
|
||||
this.taskOwner = null;
|
||||
}
|
||||
|
||||
public void optimizeQueue() {
|
||||
//Find a good grid size to match the MCAWorlds chunk-cache size of 500
|
||||
Vector2d sortGridSize = new Vector2d(20, 20).div(mapType.getTileRenderer().getHiresModelManager().getTileSize().toDouble().div(16)).ceil().max(1, 1);
|
||||
|
||||
synchronized (renderTiles) {
|
||||
Vector2i[] array = renderTiles.toArray(new Vector2i[renderTiles.size()]);
|
||||
Arrays.sort(array, (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 (v1.getY() < v1.getY()) return -1;
|
||||
if (v1.getY() > v1.getY()) return 1;
|
||||
if (v1.getX() < v1.getX()) return -1;
|
||||
if (v1.getX() > v1.getX()) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
renderTiles.clear();
|
||||
for (Vector2i tile : array) {
|
||||
renderTiles.add(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addTile(Vector2i tile) {
|
||||
synchronized (renderTiles) {
|
||||
renderTiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public void addTiles(Collection<Vector2i> tiles) {
|
||||
synchronized (renderTiles) {
|
||||
renderTiles.addAll(tiles);
|
||||
}
|
||||
}
|
||||
|
||||
public RenderTicket poll() {
|
||||
synchronized (renderTiles) {
|
||||
Vector2i tile = renderTiles.poll();
|
||||
if (tile != null) {
|
||||
renderedTiles++;
|
||||
if (firstTileTime < 0) firstTileTime = System.currentTimeMillis();
|
||||
return new RenderTicket(mapType, tile);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the render-time counter.
|
||||
* So if the rendering gets paused, the statistics remain correct.
|
||||
* It will resume as soon as a new ticket gets polled
|
||||
*/
|
||||
public void pause() {
|
||||
synchronized (renderTiles) {
|
||||
additionalRunTime += System.currentTimeMillis() - firstTileTime;
|
||||
firstTileTime = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public long getActiveTime() {
|
||||
if (firstTileTime < 0) return additionalRunTime;
|
||||
return (System.currentTimeMillis() - firstTileTime) + additionalRunTime;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public MapType getMapType() {
|
||||
return mapType;
|
||||
}
|
||||
|
||||
public int getRenderedTileCount() {
|
||||
return renderedTiles;
|
||||
}
|
||||
|
||||
public int getRemainingTileCount() {
|
||||
return renderTiles.size();
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return renderTiles.isEmpty();
|
||||
}
|
||||
|
||||
public UUID getTaskOwner() {
|
||||
return taskOwner;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package de.bluecolored.bluemap.sponge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
|
||||
public class RenderTicket {
|
||||
|
||||
private final MapType map;
|
||||
private final Vector2i tile;
|
||||
|
||||
private boolean finished;
|
||||
|
||||
public RenderTicket(MapType map, Vector2i tile) {
|
||||
this.map = map;
|
||||
this.tile = tile;
|
||||
this.finished = false;
|
||||
}
|
||||
|
||||
public synchronized void render() throws IOException {
|
||||
if (!finished) {
|
||||
|
||||
try {
|
||||
map.renderTile(tile);
|
||||
} catch (ChunkNotGeneratedException e) {
|
||||
//ignore
|
||||
}
|
||||
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
public MapType getMapType() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public Vector2i getTile() {
|
||||
return tile;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(map.getId(), tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof RenderTicket)) return false;
|
||||
RenderTicket ticket = (RenderTicket) other;
|
||||
|
||||
if (!ticket.tile.equals(tile)) return false;
|
||||
return ticket.map.getId().equals(map.getId());
|
||||
}
|
||||
|
||||
}
|
@ -24,14 +24,43 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.sponge;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.config.ConfigDir;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.game.GameReloadEvent;
|
||||
import org.spongepowered.api.event.game.state.GameStartingServerEvent;
|
||||
import org.spongepowered.api.event.game.state.GameStoppingEvent;
|
||||
import org.spongepowered.api.plugin.Plugin;
|
||||
import org.spongepowered.api.scheduler.SpongeExecutorService;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationFile;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationFile.MapConfig;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.web.BlueMapWebServer;
|
||||
import de.bluecolored.bluemap.core.web.WebFilesManager;
|
||||
import de.bluecolored.bluemap.core.web.WebSettings;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
import net.querz.nbt.NBTUtil;
|
||||
|
||||
@Plugin(
|
||||
id = SpongePlugin.PLUGIN_ID,
|
||||
@ -46,25 +75,284 @@ public class SpongePlugin {
|
||||
public static final String PLUGIN_NAME = "BlueMap";
|
||||
public static final String PLUGIN_VERSION = "0.0.0";
|
||||
|
||||
private static Object plugin;
|
||||
private static SpongePlugin instance;
|
||||
|
||||
@Inject
|
||||
@ConfigDir(sharedRoot = false)
|
||||
private Path configurationDir;
|
||||
|
||||
private ConfigurationFile config;
|
||||
private ResourcePack resourcePack;
|
||||
|
||||
private Map<UUID, World> worlds;
|
||||
private Map<String, MapType> maps;
|
||||
|
||||
private RenderManager renderManager;
|
||||
private MapUpdateHandler updateHandler;
|
||||
private BlueMapWebServer webServer;
|
||||
|
||||
private SpongeExecutorService syncExecutor;
|
||||
private SpongeExecutorService asyncExecutor;
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
@Inject
|
||||
public SpongePlugin(org.slf4j.Logger logger) {
|
||||
plugin = this;
|
||||
|
||||
Logger.global = new Slf4jLogger(logger);
|
||||
|
||||
this.maps = new HashMap<>();
|
||||
this.worlds = new HashMap<>();
|
||||
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public synchronized void load() throws IOException, NoSuchResourceException {
|
||||
if (loaded) return;
|
||||
unload(); //ensure nothing is left running (from a failed load or something)
|
||||
|
||||
//init commands
|
||||
Sponge.getCommandManager().register(this, new Commands(this).createRootCommand(), "bluemap");
|
||||
|
||||
//load configs
|
||||
File configFile = getConfigPath().resolve("bluemap.conf").toFile();
|
||||
config = ConfigurationFile.loadOrCreate(configFile);
|
||||
|
||||
//load resources
|
||||
File defaultResourceFile = getConfigPath().resolve("resourcepacks").resolve("client.jar").toFile();
|
||||
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
||||
|
||||
if (!defaultResourceFile.exists()) {
|
||||
handleMissingResources(defaultResourceFile, configFile);
|
||||
return;
|
||||
}
|
||||
|
||||
resourcePack = new ResourcePack(Lists.newArrayList(defaultResourceFile), textureExportFile);
|
||||
|
||||
//load maps
|
||||
for (MapConfig mapConfig : config.getMapConfigs()) {
|
||||
String id = mapConfig.getId();
|
||||
String name = mapConfig.getName();
|
||||
|
||||
File worldFolder = new File(mapConfig.getWorldPath());
|
||||
if (!worldFolder.exists() || !worldFolder.isDirectory()) {
|
||||
Logger.global.logError("Failed to load map '" + id + "': '" + worldFolder + "' does not exist or is no directory!", new IOException());
|
||||
continue;
|
||||
}
|
||||
|
||||
UUID worldUUID;
|
||||
try {
|
||||
CompoundTag levelSponge = (CompoundTag) NBTUtil.readTag(new File(worldFolder, "level_sponge.dat"));
|
||||
CompoundTag spongeData = levelSponge.getCompoundTag("SpongeData");
|
||||
long least = spongeData.getLong("UUIDLeast");
|
||||
long most = spongeData.getLong("UUIDMost");
|
||||
worldUUID = new UUID(most, least);
|
||||
} catch (Exception e) {
|
||||
Logger.global.logError("Failed to load map '" + id + "': Failed to read level_sponge.dat", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
World world = worlds.get(worldUUID);
|
||||
if (world == null) {
|
||||
try {
|
||||
world = MCAWorld.load(worldFolder.toPath(), worldUUID);
|
||||
worlds.put(worldUUID, world);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to load map '" + id + "': Failed to read level.dat", e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
HiresModelManager hiresModelManager = new HiresModelManager(
|
||||
config.getWebDataPath().resolve("hires").resolve(id),
|
||||
resourcePack,
|
||||
new Vector2i(mapConfig.getHiresTileSize(), mapConfig.getHiresTileSize()),
|
||||
getAsyncExecutor()
|
||||
);
|
||||
|
||||
LowresModelManager lowresModelManager = new LowresModelManager(
|
||||
config.getWebDataPath().resolve("lowres").resolve(id),
|
||||
new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()),
|
||||
new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile())
|
||||
);
|
||||
|
||||
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager, mapConfig);
|
||||
|
||||
MapType mapType = new MapType(id, name, world, tileRenderer);
|
||||
maps.put(id, mapType);
|
||||
}
|
||||
|
||||
//initialize render manager
|
||||
renderManager = new RenderManager(config.getRenderThreadCount());
|
||||
renderManager.start();
|
||||
|
||||
//start map updater
|
||||
updateHandler = new MapUpdateHandler();
|
||||
|
||||
//create/update webfiles
|
||||
WebFilesManager webFilesManager = new WebFilesManager(config.getWebRoot());
|
||||
if (webFilesManager.needsUpdate()) {
|
||||
webFilesManager.updateFiles();
|
||||
}
|
||||
|
||||
WebSettings webSettings = new WebSettings(config.getWebDataPath().resolve("settings.json").toFile());
|
||||
for (MapType map : maps.values()) {
|
||||
webSettings.setName(map.getName(), map.getId());
|
||||
webSettings.setFrom(map.getTileRenderer(), map.getId());
|
||||
}
|
||||
for (ConfigurationFile.MapConfig map : config.getMapConfigs()) {
|
||||
webSettings.setHiresViewDistance(map.getHiresViewDistance(), map.getId());
|
||||
webSettings.setLowresViewDistance(map.getLowresViewDistance(), map.getId());
|
||||
}
|
||||
webSettings.save();
|
||||
|
||||
//start webserver
|
||||
if (config.isWebserverEnabled()) {
|
||||
webServer = new BlueMapWebServer(config);
|
||||
webServer.updateWebfiles();
|
||||
webServer.start();
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public synchronized void unload() {
|
||||
|
||||
//stop services
|
||||
if (renderManager != null) renderManager.stop();
|
||||
if (webServer != null) webServer.close();
|
||||
|
||||
//unregister listeners
|
||||
if (updateHandler != null) Sponge.getEventManager().unregisterListeners(updateHandler);
|
||||
updateHandler = null;
|
||||
|
||||
//unregister commands
|
||||
Sponge.getCommandManager().getOwnedBy(this).forEach(Sponge.getCommandManager()::removeMapping);
|
||||
|
||||
//stop scheduled tasks
|
||||
Sponge.getScheduler().getScheduledTasks(this).forEach(t -> t.cancel());
|
||||
|
||||
//save renders
|
||||
for (MapType map : maps.values()) {
|
||||
map.getTileRenderer().save();
|
||||
}
|
||||
|
||||
//clear resources and configs
|
||||
renderManager = null;
|
||||
webServer = null;
|
||||
resourcePack = null;
|
||||
config = null;
|
||||
maps.clear();
|
||||
worlds.clear();
|
||||
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
public synchronized void reload() throws IOException, NoSuchResourceException {
|
||||
unload();
|
||||
load();
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onServerStart(GameStartingServerEvent evt) {
|
||||
syncExecutor = Sponge.getScheduler().createSyncExecutor(this);
|
||||
asyncExecutor = Sponge.getScheduler().createAsyncExecutor(this);
|
||||
|
||||
try {
|
||||
load();
|
||||
if (isLoaded()) Logger.global.logInfo("Loaded!");
|
||||
} catch (IOException | NoSuchResourceException e) {
|
||||
Logger.global.logError("Failed to load!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onServerStop(GameStoppingEvent evt) {
|
||||
unload();
|
||||
Logger.global.logInfo("Saved and stopped!");
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onServerReload(GameReloadEvent evt) {
|
||||
try {
|
||||
reload();
|
||||
Logger.global.logInfo("Reloaded!");
|
||||
} catch (IOException | NoSuchResourceException e) {
|
||||
Logger.global.logError("Failed to load!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMissingResources(File resourceFile, File configFile) {
|
||||
if (config.isDownloadAccepted()) {
|
||||
|
||||
//download file async
|
||||
Sponge.getScheduler().createTaskBuilder()
|
||||
.async()
|
||||
.execute(() -> {
|
||||
try {
|
||||
Logger.global.logInfo("Downloading " + ResourcePack.MINECRAFT_CLIENT_URL + " to " + resourceFile + " ...");
|
||||
ResourcePack.downloadDefaultResource(resourceFile);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to download resources!", e);
|
||||
return;
|
||||
}
|
||||
|
||||
//reload bluemap on server thread
|
||||
Sponge.getScheduler().createTaskBuilder()
|
||||
.execute(() -> {
|
||||
try {
|
||||
Logger.global.logInfo("Download finished! Reloading...");
|
||||
reload();
|
||||
Logger.global.logInfo("Reloaded!");
|
||||
} catch (IOException | NoSuchResourceException e) {
|
||||
Logger.global.logError("Failed to reload BlueMap!", e);
|
||||
}
|
||||
})
|
||||
.submit(SpongePlugin.getInstance());
|
||||
})
|
||||
.submit(SpongePlugin.getInstance());
|
||||
|
||||
} else {
|
||||
Logger.global.logWarning("BlueMap is missing important resources!");
|
||||
Logger.global.logWarning("You need to accept the download of the required files in order of BlueMap to work!");
|
||||
Logger.global.logWarning("Please check: " + configFile);
|
||||
Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload");
|
||||
}
|
||||
}
|
||||
|
||||
public SpongeExecutorService getSyncExecutor(){
|
||||
return syncExecutor;
|
||||
}
|
||||
|
||||
public SpongeExecutorService getAsyncExecutor(){
|
||||
return asyncExecutor;
|
||||
}
|
||||
|
||||
public World getWorld(UUID uuid){
|
||||
return worlds.get(uuid);
|
||||
}
|
||||
|
||||
public Collection<MapType> getMapTypes(){
|
||||
return maps.values();
|
||||
}
|
||||
|
||||
public RenderManager getRenderManager() {
|
||||
return renderManager;
|
||||
}
|
||||
|
||||
public MapUpdateHandler getUpdateHandler() {
|
||||
return updateHandler;
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public Path getConfigPath(){
|
||||
return configurationDir;
|
||||
}
|
||||
|
||||
public static Object getPlugin() {
|
||||
return plugin;
|
||||
public static SpongePlugin getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user