diff --git a/BlueMapCommon/build.gradle.kts b/BlueMapCommon/build.gradle.kts index 6e71f776..ad5b5dec 100644 --- a/BlueMapCommon/build.gradle.kts +++ b/BlueMapCommon/build.gradle.kts @@ -33,6 +33,9 @@ dependencies { api ("de.bluecolored.bluemap.core:BlueMapCore") compileOnly ("org.jetbrains:annotations:16.0.2") + compileOnly ("org.projectlombok:lombok:1.18.28") + + annotationProcessor ("org.projectlombok:lombok:1.18.28") testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2") testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2") diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapConfigProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapConfiguration.java similarity index 85% rename from BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapConfigProvider.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapConfiguration.java index 6774ac02..51406385 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapConfigProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapConfiguration.java @@ -26,10 +26,16 @@ package de.bluecolored.bluemap.common; import de.bluecolored.bluemap.common.config.*; import de.bluecolored.bluemap.common.config.storage.StorageConfig; +import de.bluecolored.bluemap.core.MinecraftVersion; +import org.jetbrains.annotations.Nullable; +import java.nio.file.Path; import java.util.Map; -public interface BlueMapConfigProvider { +public interface BlueMapConfiguration { + + MinecraftVersion getMinecraftVersion(); + CoreConfig getCoreConfig(); WebappConfig getWebappConfig(); @@ -42,4 +48,8 @@ public interface BlueMapConfigProvider { Map getStorageConfigs(); + @Nullable Path getResourcePacksFolder(); + + @Nullable Path getModsFolder(); + } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java index c8f93364..94c39c08 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java @@ -36,17 +36,17 @@ import de.bluecolored.bluemap.common.config.ConfigurationException; import de.bluecolored.bluemap.common.config.MapConfig; import de.bluecolored.bluemap.common.config.storage.StorageConfig; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; -import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.debug.StateDumper; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; -import de.bluecolored.bluemap.core.mca.MCAWorld; +import de.bluecolored.bluemap.core.resources.datapack.DataPack; import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.util.FileHelper; +import de.bluecolored.bluemap.core.util.Key; import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.Nullable; import org.spongepowered.configurate.ConfigurateException; @@ -60,7 +60,6 @@ import java.lang.reflect.Type; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; @@ -72,85 +71,32 @@ import java.util.stream.Stream; @DebugDump public class BlueMapService implements Closeable { - private final ServerInterface serverInterface; - private final BlueMapConfigProvider configs; - - private final Map worldIds; - private final Map storages; - - private volatile WebFilesManager webFilesManager; - - private Map worlds; - private Map maps; + private final BlueMapConfiguration config; + private final WebFilesManager webFilesManager; private ResourcePack resourcePack; + private final Map worlds; + private final Map maps; + private final Map storages; - public BlueMapService(ServerInterface serverInterface, BlueMapConfigProvider configProvider, @Nullable ResourcePack preloadedResourcePack) { - this(serverInterface, configProvider); - - if (preloadedResourcePack != null) - this.resourcePack = preloadedResourcePack; + public BlueMapService(BlueMapConfiguration configuration, @Nullable ResourcePack preloadedResourcePack) { + this(configuration); + this.resourcePack = preloadedResourcePack; } - public BlueMapService(ServerInterface serverInterface, BlueMapConfigProvider configProvider) { - this.serverInterface = serverInterface; - this.configs = configProvider; + public BlueMapService(BlueMapConfiguration configuration) { + this.config = configuration; + this.webFilesManager = new WebFilesManager(config.getWebappConfig().getWebroot()); - this.worldIds = new ConcurrentHashMap<>(); - this.storages = new HashMap<>(); + this.worlds = new ConcurrentHashMap<>(); + this.maps = new ConcurrentHashMap<>(); + this.storages = new ConcurrentHashMap<>(); StateDumper.global().register(this); } - public String getWorldId(Path worldFolder) throws IOException { - // fast-path - String id = worldIds.get(worldFolder); - if (id != null) return id; - - // second try with normalized absolute path - worldFolder = worldFolder.toAbsolutePath().normalize(); - id = worldIds.get(worldFolder); - if (id != null) return id; - - // secure (slower) query with real path - worldFolder = worldFolder.toRealPath(); - id = worldIds.get(worldFolder); - if (id != null) return id; - - synchronized (worldIds) { - // check again if another thread has already added the world - id = worldIds.get(worldFolder); - if (id != null) return id; - - Logger.global.logDebug("Loading world id for '" + worldFolder + "'..."); - - // now we can be sure it wasn't loaded yet .. load - Path idFile = worldFolder.resolve("bluemap.id"); - if (!Files.exists(idFile)) { - id = this.serverInterface.getWorld(worldFolder) - .flatMap(ServerWorld::getId) - .orElse(UUID.randomUUID().toString()); - Files.writeString(idFile, id, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); - - worldIds.put(worldFolder, id); - return id; - } - - id = Files.readString(idFile); - worldIds.put(worldFolder, id); - return id; - } - } - public WebFilesManager getWebFilesManager() { - if (webFilesManager == null) { - synchronized (this) { - if (webFilesManager == null) - webFilesManager = new WebFilesManager(configs.getWebappConfig().getWebroot()); - } - } - return webFilesManager; } @@ -164,13 +110,13 @@ public class BlueMapService implements Closeable { } // update settings.json - if (!configs.getWebappConfig().isUpdateSettingsFile()) { + if (!config.getWebappConfig().isUpdateSettingsFile()) { webFilesManager.loadSettings(); - webFilesManager.addFrom(configs.getWebappConfig()); + webFilesManager.addFrom(config.getWebappConfig()); } else { - webFilesManager.setFrom(configs.getWebappConfig()); + webFilesManager.setFrom(config.getWebappConfig()); } - for (String mapId : configs.getMapConfigs().keySet()) { + for (String mapId : config.getMapConfigs().keySet()) { webFilesManager.addMap(mapId); } webFilesManager.saveSettings(); @@ -180,32 +126,45 @@ public class BlueMapService implements Closeable { } } - public synchronized Map getWorlds() throws InterruptedException { - return getWorlds(mapId -> true); + /** + * Gets all loaded maps. + * @return A map of loaded maps + */ + public Map getMaps() { + return Collections.unmodifiableMap(maps); } - public synchronized Map getWorlds(Predicate mapFilter) throws InterruptedException { - if (worlds == null) loadWorldsAndMaps(mapFilter); - return worlds; + /** + * Gets all loaded worlds. + * @return A map of loaded worlds + */ + public Map getWorlds() { + return Collections.unmodifiableMap(worlds); } - public synchronized Map getMaps() throws InterruptedException { - return getMaps(mapId -> true); + /** + * Gets or loads configured maps. + * @return A map of loaded maps + */ + public Map getOrLoadMaps() throws InterruptedException { + return getOrLoadMaps(mapId -> true); } - public synchronized Map getMaps(Predicate mapFilter) throws InterruptedException { - if (maps == null) loadWorldsAndMaps(mapFilter); - return maps; - } + /** + * Gets or loads configured maps. + * @param filter A predicate filtering map-ids that should be loaded + * (if maps are already loaded, they will be returned as well) + * @return A map of all loaded maps + */ + public synchronized Map getOrLoadMaps(Predicate filter) throws InterruptedException { + for (var entry : config.getMapConfigs().entrySet()) { + if (Thread.interrupted()) throw new InterruptedException(); - private synchronized void loadWorldsAndMaps(Predicate mapFilter) throws InterruptedException { - maps = new HashMap<>(); - worlds = new HashMap<>(); + if (!filter.test(entry.getKey())) continue; + if (maps.containsKey(entry.getKey())) continue; - for (var entry : configs.getMapConfigs().entrySet()) { - if (!mapFilter.test(entry.getKey())) continue; try { - loadMapConfig(entry.getKey(), entry.getValue()); + loadMap(entry.getKey(), entry.getValue()); } catch (ConfigurationException ex) { Logger.global.logWarning(ex.getFormattedExplanation()); Throwable cause = ex.getRootCause(); @@ -214,16 +173,15 @@ public class BlueMapService implements Closeable { } } } - - worlds = Collections.unmodifiableMap(worlds); - maps = Collections.unmodifiableMap(maps); + return Collections.unmodifiableMap(maps); } - private synchronized void loadMapConfig(String id, MapConfig mapConfig) throws ConfigurationException, InterruptedException { + private synchronized void loadMap(String id, MapConfig mapConfig) throws ConfigurationException, InterruptedException { String name = mapConfig.getName(); if (name == null) name = id; Path worldFolder = mapConfig.getWorld(); + Key dimension = mapConfig.getDimension(); // if there is no world configured, we assume the map is static, or supplied from a different server if (worldFolder == null) { @@ -231,48 +189,63 @@ public class BlueMapService implements Closeable { return; } + // if there is no dimension configured, we assume world-folder is actually the dimension-folder and convert (backwards compatibility) + if (dimension == null) { + worldFolder = worldFolder.normalize(); + if (worldFolder.endsWith("DIM-1")) { + worldFolder = worldFolder.getParent(); + dimension = DataPack.DIMENSION_THE_NETHER; + } else if (worldFolder.endsWith("DIM1")) { + worldFolder = worldFolder.getParent(); + dimension = DataPack.DIMENSION_THE_END; + } else if ( + worldFolder.getNameCount() > 3 && + worldFolder.getName(worldFolder.getNameCount() - 3).equals(Path.of("dimensions")) + ) { + String namespace = worldFolder.getName(worldFolder.getNameCount() - 2).toString(); + String value = worldFolder.getName(worldFolder.getNameCount() - 1).toString(); + worldFolder = worldFolder.subpath(0, worldFolder.getNameCount() - 3); + dimension = new Key(namespace, value); + } else { + dimension = DataPack.DIMENSION_OVERWORLD; + } + + Logger.global.logInfo("The map '" + name + "' has no dimension configured.\n" + + "Assuming world: '" + worldFolder + "' and dimension: '" + dimension + "'."); + } + if (!Files.isDirectory(worldFolder)) { throw new ConfigurationException( "'" + worldFolder.toAbsolutePath().normalize() + "' does not exist or is no directory!\n" + "Check if the 'world' setting in the config-file for that map is correct, or remove the entire config-file if you don't want that map."); } - String worldId; - try { - worldId = getWorldId(worldFolder); - } catch (IOException ex) { - throw new ConfigurationException( - "Could not load the ID for the world (" + worldFolder.toAbsolutePath().normalize() + ")!\n" + - "Make sure BlueMap has read and write access/permissions to the world-files for this map.", - ex); - } - + String worldId = MCAWorld.id(worldFolder, dimension); World world = worlds.get(worldId); if (world == null) { try { - Logger.global.logInfo("Loading world '" + worldId + "' (" + worldFolder.toAbsolutePath().normalize() + ")..."); - world = new MCAWorld(worldFolder, mapConfig.getWorldSkyLight(), mapConfig.isIgnoreMissingLightData()); + Logger.global.logDebug("Loading world " + worldId + " ..."); + world = MCAWorld.load(worldFolder, dimension); worlds.put(worldId, world); } catch (IOException ex) { throw new ConfigurationException( - "Failed to load world '" + worldId + "' (" + worldFolder.toAbsolutePath().normalize() + ")!\n" + + "Failed to load world " + worldId + "!\n" + "Is the level.dat of that world present and not corrupted?", ex); } } - Storage storage = getStorage(mapConfig.getStorage()); + Storage storage = getOrLoadStorage(mapConfig.getStorage()); try { - Logger.global.logInfo("Loading map '" + name + "'..."); + Logger.global.logInfo("Loading map '" + id + "'..."); BmMap map = new BmMap( id, name, - worldId, world, storage, - getResourcePack(), + getOrLoadResourcePack(), mapConfig ); maps.put(id, map); @@ -303,12 +276,12 @@ public class BlueMapService implements Closeable { } } - public synchronized Storage getStorage(String storageId) throws ConfigurationException { + public synchronized Storage getOrLoadStorage(String storageId) throws ConfigurationException, InterruptedException { Storage storage = storages.get(storageId); if (storage == null) { try { - StorageConfig storageConfig = getConfigs().getStorageConfigs().get(storageId); + StorageConfig storageConfig = getConfig().getStorageConfigs().get(storageId); if (storageConfig == null) { throw new ConfigurationException("There is no storage-configuration for '" + storageId + "'!\n" + "You will either need to define that storage, or change the map-config to use a storage-config that exists."); @@ -341,27 +314,33 @@ public class BlueMapService implements Closeable { return storage; } - public synchronized ResourcePack getResourcePack() throws ConfigurationException, InterruptedException { + public @Nullable ResourcePack getResourcePack() { + return resourcePack; + } + + public synchronized ResourcePack getOrLoadResourcePack() throws ConfigurationException, InterruptedException { if (resourcePack == null) { - MinecraftVersion minecraftVersion = serverInterface.getMinecraftVersion(); + MinecraftVersion minecraftVersion = config.getMinecraftVersion(); + @Nullable Path resourcePackFolder = config.getResourcePacksFolder(); + @Nullable Path modsFolder = config.getModsFolder(); - Path defaultResourceFile = configs.getCoreConfig().getData().resolve("minecraft-client-" + minecraftVersion.getResource().getVersion().getVersionString() + ".jar"); - Path resourceExtensionsFile = configs.getCoreConfig().getData().resolve("resourceExtensions.zip"); + Path defaultResourceFile = config.getCoreConfig().getData().resolve("minecraft-client-" + minecraftVersion.getResource().getVersion().getVersionString() + ".jar"); + Path resourceExtensionsFile = config.getCoreConfig().getData().resolve("resourceExtensions.zip"); - Path resourcePackFolder = serverInterface.getConfigFolder().resolve("resourcepacks"); + try { + FileHelper.createDirectories(resourcePackFolder); + } catch (IOException ex) { + throw new ConfigurationException( + "BlueMap failed to create this folder:\n" + + resourcePackFolder + "\n" + + "Does BlueMap have sufficient permissions?", + ex); + } - try { - FileHelper.createDirectories(resourcePackFolder); - } catch (IOException ex) { - throw new ConfigurationException( - "BlueMap failed to create this folder:\n" + - resourcePackFolder + "\n" + - "Does BlueMap have sufficient permissions?", - ex); - } + if (Thread.interrupted()) throw new InterruptedException(); if (!Files.exists(defaultResourceFile)) { - if (configs.getCoreConfig().isAcceptDownload()) { + if (config.getCoreConfig().isAcceptDownload()) { //download file try { Logger.global.logInfo("Downloading " + minecraftVersion.getResource().getClientUrl() + " to " + defaultResourceFile + " ..."); @@ -380,37 +359,43 @@ public class BlueMapService implements Closeable { } } + if (Thread.interrupted()) throw new InterruptedException(); + try { Files.deleteIfExists(resourceExtensionsFile); FileHelper.createDirectories(resourceExtensionsFile.getParent()); URL resourceExtensionsUrl = Objects.requireNonNull( Plugin.class.getResource( "/de/bluecolored/bluemap/" + minecraftVersion.getResource().getResourcePrefix() + - "/resourceExtensions.zip") + "/resourceExtensions.zip") ); FileUtils.copyURLToFile(resourceExtensionsUrl, resourceExtensionsFile.toFile(), 10000, 10000); } catch (IOException ex) { throw new ConfigurationException( "Failed to create resourceExtensions.zip!\n" + - "Does BlueMap has sufficient write permissions?", + "Does BlueMap has sufficient write permissions?", ex); } + if (Thread.interrupted()) throw new InterruptedException(); + try { - resourcePack = new ResourcePack(); + ResourcePack resourcePack = new ResourcePack(); List resourcePackRoots = new ArrayList<>(); - // load from resourcepack folder - try (Stream resourcepackFiles = Files.list(resourcePackFolder)) { - resourcepackFiles - .sorted(Comparator.reverseOrder()) - .forEach(resourcePackRoots::add); + + if (resourcePackFolder != null) { + // load from resourcepack folder + try (Stream resourcepackFiles = Files.list(resourcePackFolder)) { + resourcepackFiles + .sorted(Comparator.reverseOrder()) + .forEach(resourcePackRoots::add); + } } - if (configs.getCoreConfig().isScanForModResources()) { + if (config.getCoreConfig().isScanForModResources()) { // load from mods folder - Path modsFolder = serverInterface.getModsFolder().orElse(null); if (modsFolder != null && Files.isDirectory(modsFolder)) { try (Stream resourcepackFiles = Files.list(modsFolder)) { resourcepackFiles @@ -436,23 +421,20 @@ public class BlueMapService implements Closeable { resourcePackRoots.add(defaultResourceFile); resourcePack.loadResources(resourcePackRoots); + + this.resourcePack = resourcePack; } catch (IOException | RuntimeException e) { throw new ConfigurationException("Failed to parse resources!\n" + "Is one of your resource-packs corrupted?", e); } - } - return resourcePack; - } - - public Optional getResourcePackIfLoaded() { - return Optional.ofNullable(this.resourcePack); + return this.resourcePack; } private Collection getWorldFolders() { Set folders = new HashSet<>(); - for (MapConfig mapConfig : configs.getMapConfigs().values()) { + for (MapConfig mapConfig : config.getMapConfigs().values()) { Path folder = mapConfig.getWorld(); if (folder == null) continue; folder = folder.toAbsolutePath().normalize(); @@ -463,8 +445,8 @@ public class BlueMapService implements Closeable { return folders; } - public BlueMapConfigProvider getConfigs() { - return configs; + public BlueMapConfiguration getConfig() { + return config; } @Override diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java index f14222da..729c7759 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/WebFilesManager.java @@ -24,6 +24,9 @@ */ package de.bluecolored.bluemap.common; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import de.bluecolored.bluemap.common.config.WebappConfig; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.logger.Logger; @@ -47,6 +50,11 @@ import java.util.zip.ZipFile; public class WebFilesManager { + private static final Gson GSON = ResourcesGson.addAdapter(new GsonBuilder()) + .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) + //.setPrettyPrinting() // enable pretty printing for easy editing + .create(); + private final Path webRoot; private Settings settings; @@ -61,7 +69,7 @@ public class WebFilesManager { public void loadSettings() throws IOException { try (BufferedReader reader = Files.newBufferedReader(getSettingsFile())) { - this.settings = ResourcesGson.INSTANCE.fromJson(reader, Settings.class); + this.settings = GSON.fromJson(reader, Settings.class); } } @@ -69,7 +77,7 @@ public class WebFilesManager { FileHelper.createDirectories(getSettingsFile().getParent()); try (BufferedWriter writer = Files.newBufferedWriter(getSettingsFile(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { - ResourcesGson.INSTANCE.toJson(this.settings, writer); + GSON.toJson(this.settings, writer); } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java index 13d600a5..fbdbfdd2 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapAPIImpl.java @@ -30,13 +30,15 @@ import de.bluecolored.bluemap.api.BlueMapAPI; import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.BlueMapWorld; import de.bluecolored.bluemap.common.plugin.Plugin; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.world.World; -import java.io.IOException; -import java.util.*; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -75,30 +77,21 @@ public class BlueMapAPIImpl extends BlueMapAPI { @Override public Collection getMaps() { - Map maps = plugin.getMaps(); - if (maps == null) return Collections.emptyList(); - - return maps.values().stream() - .map(map -> { - try { - return new BlueMapMapImpl(plugin, map); - } catch (IOException e) { - Logger.global.logError("[API] Failed to create BlueMapMap for map " + map.getId(), e); - return null; - } - }) - .filter(Objects::nonNull) + Map maps = plugin.getBlueMap().getMaps(); + return maps.keySet().stream() + .map(this::getMap) + .filter(Optional::isPresent) + .map(Optional::get) .collect(Collectors.toUnmodifiableSet()); } @Override public Collection getWorlds() { - Map worlds = plugin.getWorlds(); - if (worlds == null) return Collections.emptyList(); - - return worlds.values().stream() - .map(world -> getWorld(world).orElse(null)) - .filter(Objects::nonNull) + Map worlds = plugin.getBlueMap().getWorlds(); + return worlds.keySet().stream() + .map(this::getWorld) + .filter(Optional::isPresent) + .map(Optional::get) .collect(Collectors.toUnmodifiableSet()); } @@ -108,43 +101,24 @@ public class BlueMapAPIImpl extends BlueMapAPI { } public Optional getWorldUncached(Object world) { - var worlds = plugin.getWorlds(); - if (worlds == null) return Optional.empty(); - - if (world instanceof UUID) { - var coreWorld = worlds.get(world.toString()); - if (coreWorld != null) world = coreWorld; - } if (world instanceof String) { - var coreWorld = worlds.get(world); + var coreWorld = plugin.getBlueMap().getWorlds().get(world); if (coreWorld != null) world = coreWorld; } if (world instanceof World) { var coreWorld = (World) world; - try { - return Optional.of(new BlueMapWorldImpl(plugin, coreWorld)); - } catch (IOException e) { - Logger.global.logError("[API] Failed to create BlueMapWorld for world " + coreWorld.getSaveFolder(), e); - } - return Optional.empty(); + return Optional.of(new BlueMapWorldImpl(plugin, coreWorld)); } - var serverWorld = plugin.getServerInterface().getWorld(world).orElse(null); + ServerWorld serverWorld = plugin.getServerInterface().getServerWorld(world).orElse(null); if (serverWorld == null) return Optional.empty(); - try { - String id = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - var coreWorld = worlds.get(id); - if (coreWorld == null) return Optional.empty(); - - return Optional.of(new BlueMapWorldImpl(plugin, coreWorld)); - } catch (IOException e) { - Logger.global.logError("[API] Failed to create BlueMapWorld for world " + serverWorld.getSaveFolder(), e); - return Optional.empty(); - } + World coreWorld = plugin.getWorld(serverWorld); + if (coreWorld == null) return Optional.empty(); + return Optional.of(new BlueMapWorldImpl(plugin, coreWorld)); } @Override @@ -153,8 +127,7 @@ public class BlueMapAPIImpl extends BlueMapAPI { } public Optional getMapUncached(String id) { - var maps = plugin.getMaps(); - if (maps == null) return Optional.empty(); + var maps = plugin.getBlueMap().getMaps(); var map = maps.get(id); if (map == null) return Optional.empty(); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapWorldImpl.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapWorldImpl.java index 64db32f7..a0203d5e 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapWorldImpl.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/BlueMapWorldImpl.java @@ -28,8 +28,8 @@ import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.BlueMapWorld; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; -import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.file.Path; import java.util.Collection; @@ -38,13 +38,13 @@ import java.util.stream.Collectors; public class BlueMapWorldImpl implements BlueMapWorld { - private final WeakReference plugin; private final String id; + private final WeakReference plugin; private final WeakReference world; - public BlueMapWorldImpl(Plugin plugin, World world) throws IOException { + public BlueMapWorldImpl(Plugin plugin, World world) { + this.id = world.getId(); this.plugin = new WeakReference<>(plugin); - this.id = plugin.getBlueMap().getWorldId(world.getSaveFolder()); this.world = new WeakReference<>(world); } @@ -58,15 +58,23 @@ public class BlueMapWorldImpl implements BlueMapWorld { } @Override + @Deprecated public Path getSaveFolder() { - return unpack(world).getSaveFolder(); + World world = unpack(this.world); + if (world instanceof MCAWorld) { + return ((MCAWorld) world).getDimensionFolder(); + } else { + throw new UnsupportedOperationException("This world-type has no save-folder."); + } } @Override public Collection getMaps() { - return unpack(plugin).getMaps().values().stream() - .filter(map -> map.getWorld().equals(unpack(world))) - .map(map -> new BlueMapMapImpl(unpack(plugin), map, this)) + Plugin plugin = unpack(this.plugin); + World world = unpack(this.world); + return plugin.getBlueMap().getMaps().values().stream() + .filter(map -> map.getWorld().equals(world)) + .map(map -> new BlueMapMapImpl(plugin, map, this)) .collect(Collectors.toUnmodifiableSet()); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/RenderManagerImpl.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/RenderManagerImpl.java index 69a15334..e56d53f2 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/RenderManagerImpl.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/RenderManagerImpl.java @@ -82,7 +82,7 @@ public class RenderManagerImpl implements RenderManager { @Override public void start() { if (!isRunning()){ - renderManager.start(plugin.getConfigs().getCoreConfig().getRenderThreadCount()); + renderManager.start(plugin.getBlueMap().getConfig().getCoreConfig().getRenderThreadCount()); } plugin.getPluginState().setRenderThreadsEnabled(true); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/WebAppImpl.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/WebAppImpl.java index 18f373df..88fb11ea 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/WebAppImpl.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/api/WebAppImpl.java @@ -49,7 +49,7 @@ public class WebAppImpl implements WebApp { @Override public Path getWebRoot() { - return plugin.getConfigs().getWebappConfig().getWebroot(); + return plugin.getBlueMap().getConfig().getWebappConfig().getWebroot(); } @Override @@ -98,6 +98,7 @@ public class WebAppImpl implements WebApp { } @Override + @Deprecated(forRemoval = true) public Map availableImages() throws IOException { Path webRoot = getWebRoot().toAbsolutePath(); String separator = webRoot.getFileSystem().getSeparator(); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigManager.java similarity index 70% rename from BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigManager.java index 88951fd1..72a01450 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigs.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/BlueMapConfigManager.java @@ -25,114 +25,92 @@ package de.bluecolored.bluemap.common.config; import de.bluecolored.bluemap.api.debug.DebugDump; -import de.bluecolored.bluemap.common.BlueMapConfigProvider; +import de.bluecolored.bluemap.common.BlueMapConfiguration; import de.bluecolored.bluemap.common.config.storage.StorageConfig; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; +import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.resources.datapack.DataPack; import de.bluecolored.bluemap.core.util.FileHelper; -import de.bluecolored.bluemap.core.util.Tristate; +import de.bluecolored.bluemap.core.util.Key; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.*; import java.time.LocalDateTime; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.stream.Stream; @DebugDump -public class BlueMapConfigs implements BlueMapConfigProvider { +@Getter +public class BlueMapConfigManager implements BlueMapConfiguration { - private final ServerInterface serverInterface; private final ConfigManager configManager; + private final MinecraftVersion minecraftVersion; private final CoreConfig coreConfig; private final WebserverConfig webserverConfig; private final WebappConfig webappConfig; private final PluginConfig pluginConfig; private final Map mapConfigs; private final Map storageConfigs; + private final Path resourcePacksFolder; + private final @Nullable Path modsFolder; - public BlueMapConfigs(ServerInterface serverInterface) throws ConfigurationException { - this(serverInterface, Path.of("bluemap"), Path.of("bluemap", "web"), true); - } + @Builder + private BlueMapConfigManager( + @NonNull MinecraftVersion minecraftVersion, + @NonNull Path configRoot, + @Nullable Path defaultDataFolder, + @Nullable Path defaultWebroot, + @Nullable Collection autoConfigWorlds, + @Nullable Boolean usePluginConfig, + @Nullable Boolean useMetricsConfig, + @Nullable Path resourcePacksFolder, + @Nullable Path modsFolder + ) throws ConfigurationException { + // set defaults + if (defaultDataFolder == null) defaultDataFolder = Path.of("bluemap"); + if (defaultWebroot == null) defaultWebroot = Path.of("bluemap", "web"); + if (autoConfigWorlds == null) autoConfigWorlds = Collections.emptyList(); + if (usePluginConfig == null) usePluginConfig = true; + if (useMetricsConfig == null) useMetricsConfig = true; + if (resourcePacksFolder == null) resourcePacksFolder = configRoot.resolve("resourcepacks"); - public BlueMapConfigs(ServerInterface serverInterface, Path defaultDataFolder, Path defaultWebroot, boolean usePluginConf) throws ConfigurationException { - this.serverInterface = serverInterface; - this.configManager = new ConfigManager(serverInterface.getConfigFolder()); - - this.coreConfig = loadCoreConfig(defaultDataFolder); + // load + this.minecraftVersion = minecraftVersion; + this.configManager = new ConfigManager(configRoot); + this.coreConfig = loadCoreConfig(defaultDataFolder, useMetricsConfig); this.webappConfig = loadWebappConfig(defaultWebroot); this.webserverConfig = loadWebserverConfig(webappConfig.getWebroot(), coreConfig.getData()); - this.pluginConfig = usePluginConf ? loadPluginConfig() : new PluginConfig(); + this.pluginConfig = usePluginConfig ? loadPluginConfig() : new PluginConfig(); this.storageConfigs = Collections.unmodifiableMap(loadStorageConfigs(webappConfig.getWebroot())); - this.mapConfigs = Collections.unmodifiableMap(loadMapConfigs()); + this.mapConfigs = Collections.unmodifiableMap(loadMapConfigs(autoConfigWorlds)); + this.resourcePacksFolder = resourcePacksFolder; + this.modsFolder = modsFolder; } - public ConfigManager getConfigManager() { - return configManager; - } - - @Override - public CoreConfig getCoreConfig() { - return coreConfig; - } - - @Override - public WebappConfig getWebappConfig() { - return webappConfig; - } - - @Override - public WebserverConfig getWebserverConfig() { - return webserverConfig; - } - - @Override - public PluginConfig getPluginConfig() { - return pluginConfig; - } - - @Override - public Map getMapConfigs() { - return mapConfigs; - } - - @Override - public Map getStorageConfigs() { - return storageConfigs; - } - - private synchronized CoreConfig loadCoreConfig(Path defaultDataFolder) throws ConfigurationException { + private CoreConfig loadCoreConfig(Path defaultDataFolder, boolean useMetricsConfig) throws ConfigurationException { Path configFileRaw = Path.of("core"); Path configFile = configManager.findConfigPath(configFileRaw); Path configFolder = configFile.getParent(); if (!Files.exists(configFile)) { - - // determine render-thread preset (very pessimistic, rather let people increase it themselves) - Runtime runtime = Runtime.getRuntime(); - int availableCores = runtime.availableProcessors(); - long availableMemoryMiB = runtime.maxMemory() / 1024L / 1024L; - int presetRenderThreadCount = 1; - if (availableCores >= 6 && availableMemoryMiB >= 4096) - presetRenderThreadCount = 2; - if (availableCores >= 10 && availableMemoryMiB >= 8192) - presetRenderThreadCount = 3; - try { FileHelper.createDirectories(configFolder); Files.writeString( configFolder.resolve("core.conf"), configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/core.conf") - .setConditional("metrics", serverInterface.isMetricsEnabled() == Tristate.UNDEFINED) + .setConditional("metrics", useMetricsConfig) .setVariable("timestamp", LocalDateTime.now().withNano(0).toString()) .setVariable("version", BlueMap.VERSION) .setVariable("data", formatPath(defaultDataFolder)) .setVariable("implementation", "bukkit") - .setVariable("render-thread-count", Integer.toString(presetRenderThreadCount)) + .setVariable("render-thread-count", Integer.toString(suggestRenderThreadCount())) .setVariable("logfile", formatPath(defaultDataFolder.resolve("logs").resolve("debug.log"))) .setVariable("logfile-with-time", formatPath(defaultDataFolder.resolve("logs").resolve("debug_%1$tF_%1$tT.log"))) .build(), @@ -146,7 +124,22 @@ public class BlueMapConfigs implements BlueMapConfigProvider { return configManager.loadConfig(configFileRaw, CoreConfig.class); } - private synchronized WebserverConfig loadWebserverConfig(Path defaultWebroot, Path dataRoot) throws ConfigurationException { + /** + * determine render-thread preset (very pessimistic, rather let people increase it themselves) + */ + private int suggestRenderThreadCount() { + Runtime runtime = Runtime.getRuntime(); + int availableCores = runtime.availableProcessors(); + long availableMemoryMiB = runtime.maxMemory() / 1024L / 1024L; + int presetRenderThreadCount = 1; + if (availableCores >= 6 && availableMemoryMiB >= 4096) + presetRenderThreadCount = 2; + if (availableCores >= 10 && availableMemoryMiB >= 8192) + presetRenderThreadCount = 3; + return presetRenderThreadCount; + } + + private WebserverConfig loadWebserverConfig(Path defaultWebroot, Path dataRoot) throws ConfigurationException { Path configFileRaw = Path.of("webserver"); Path configFile = configManager.findConfigPath(configFileRaw); Path configFolder = configFile.getParent(); @@ -171,7 +164,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider { return configManager.loadConfig(configFileRaw, WebserverConfig.class); } - private synchronized WebappConfig loadWebappConfig(Path defaultWebroot) throws ConfigurationException { + private WebappConfig loadWebappConfig(Path defaultWebroot) throws ConfigurationException { Path configFileRaw = Path.of("webapp"); Path configFile = configManager.findConfigPath(configFileRaw); Path configFolder = configFile.getParent(); @@ -194,7 +187,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider { return configManager.loadConfig(configFileRaw, WebappConfig.class); } - private synchronized PluginConfig loadPluginConfig() throws ConfigurationException { + private PluginConfig loadPluginConfig() throws ConfigurationException { Path configFileRaw = Path.of("plugin"); Path configFile = configManager.findConfigPath(configFileRaw); Path configFolder = configFile.getParent(); @@ -216,7 +209,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider { return configManager.loadConfig(configFileRaw, PluginConfig.class); } - private synchronized Map loadMapConfigs() throws ConfigurationException { + private Map loadMapConfigs(Collection autoConfigWorlds) throws ConfigurationException { Map mapConfigs = new HashMap<>(); Path mapFolder = Paths.get("maps"); @@ -225,41 +218,68 @@ public class BlueMapConfigs implements BlueMapConfigProvider { if (!Files.exists(mapConfigFolder)){ try { FileHelper.createDirectories(mapConfigFolder); - var worlds = serverInterface.getLoadedWorlds(); - if (worlds.isEmpty()) { + if (autoConfigWorlds.isEmpty()) { + Path worldFolder = Path.of("world"); Files.writeString( mapConfigFolder.resolve("overworld.conf"), - createOverworldMapTemplate("Overworld", Path.of("world"), 0).build(), + createOverworldMapTemplate("Overworld", worldFolder, + DataPack.DIMENSION_OVERWORLD, 0).build(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING ); Files.writeString( mapConfigFolder.resolve("nether.conf"), - createNetherMapTemplate("Nether", Path.of("world", "DIM-1"), 0).build(), + createNetherMapTemplate("Nether", worldFolder, + DataPack.DIMENSION_THE_NETHER, 0).build(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING ); Files.writeString( mapConfigFolder.resolve("end.conf"), - createEndMapTemplate("End", Path.of("world", "DIM1"), 0).build(), + createEndMapTemplate("End", worldFolder, + DataPack.DIMENSION_THE_END, 0).build(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING ); } else { - for (var world : worlds) { - String name = world.getName().orElse(world.getDimension().getName()); - Path worldFolder = world.getSaveFolder(); + // make sure overworld-dimensions come first, so they are the ones where the + // dimension-key is omitted in the generated map-id + List overworldFirstAutoConfigWorlds = new ArrayList<>(autoConfigWorlds.size()); + overworldFirstAutoConfigWorlds.addAll(autoConfigWorlds); + overworldFirstAutoConfigWorlds.sort(Comparator.comparingInt(w -> + DataPack.DIMENSION_OVERWORLD.equals(w.getDimension()) ? 0 : 1 + )); - Path configFile = mapConfigFolder.resolve(sanitiseMapId(name.toLowerCase(Locale.ROOT)) + ".conf"); + Set mapIds = new HashSet<>(); + for (var world : overworldFirstAutoConfigWorlds) { + Path worldFolder = world.getWorldFolder().normalize(); + Key dimension = world.getDimension(); + + String dimensionName = dimension.getNamespace().equals("minecraft") ? + dimension.getValue() : dimension.getFormatted(); + + // find unique map id + String id = sanitiseMapId(worldFolder.getFileName().toString()).toLowerCase(Locale.ROOT); + if (mapIds.contains(id)) + id = sanitiseMapId(worldFolder.getFileName() + "_" + dimensionName).toLowerCase(Locale.ROOT); int i = 1; - while (Files.exists(configFile)) { - configFile = mapConfigFolder.resolve(sanitiseMapId(name.toLowerCase(Locale.ROOT)) + '_' + (++i) + ".conf"); - } + String uniqueId = id; + while (mapIds.contains(uniqueId)) + uniqueId = id + "_" + (++i); + mapIds.add(uniqueId); - if (i > 1) name = name + " " + i; + Path configFile = mapConfigFolder.resolve(uniqueId + ".conf"); + String name = worldFolder.getFileName() + " (" + dimensionName + ")"; + if (i > 1) name = name + " (" + i + ")"; ConfigTemplate template; - switch (world.getDimension()) { - case NETHER: template = createNetherMapTemplate(name, worldFolder, i - 1); break; - case END: template = createEndMapTemplate(name, worldFolder, i - 1); break; - default: template = createOverworldMapTemplate(name, worldFolder, i - 1); break; + switch (world.getDimension().getFormatted()) { + case "minecraft:the_nether": + template = createNetherMapTemplate(name, worldFolder, dimension, i - 1); + break; + case "minecraft:the_end": + template = createEndMapTemplate(name, worldFolder, dimension, i - 1); + break; + default: + template = createOverworldMapTemplate(name, worldFolder, dimension, i - 1); + break; } Files.writeString( @@ -302,7 +322,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider { return mapConfigs; } - private synchronized Map loadStorageConfigs(Path defaultWebroot) throws ConfigurationException { + private Map loadStorageConfigs(Path defaultWebroot) throws ConfigurationException { Map storageConfigs = new HashMap<>(); Path storageFolder = Paths.get("storages"); @@ -357,43 +377,43 @@ public class BlueMapConfigs implements BlueMapConfigProvider { return id.replaceAll("\\W", "_"); } - private ConfigTemplate createOverworldMapTemplate(String name, Path worldFolder, int index) throws IOException { + private ConfigTemplate createOverworldMapTemplate(String name, Path worldFolder, Key dimension, int index) throws IOException { return configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/maps/map.conf") .setVariable("name", name) .setVariable("sorting", "" + index) .setVariable("world", formatPath(worldFolder)) + .setVariable("dimension", dimension.getFormatted()) .setVariable("sky-color", "#7dabff") .setVariable("void-color", "#000000") .setVariable("ambient-light", "0.1") - .setVariable("world-sky-light", "15") .setVariable("remove-caves-below-y", "55") .setConditional("max-y-comment", true) .setVariable("max-y", "100"); } - private ConfigTemplate createNetherMapTemplate(String name, Path worldFolder, int index) throws IOException { + private ConfigTemplate createNetherMapTemplate(String name, Path worldFolder, Key dimension, int index) throws IOException { return configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/maps/map.conf") .setVariable("name", name) .setVariable("sorting", "" + (100 + index)) .setVariable("world", formatPath(worldFolder)) + .setVariable("dimension", dimension.getFormatted()) .setVariable("sky-color", "#290000") .setVariable("void-color", "#150000") .setVariable("ambient-light", "0.6") - .setVariable("world-sky-light", "0") .setVariable("remove-caves-below-y", "-10000") .setConditional("max-y-comment", false) .setVariable("max-y", "90"); } - private ConfigTemplate createEndMapTemplate(String name, Path worldFolder, int index) throws IOException { + private ConfigTemplate createEndMapTemplate(String name, Path worldFolder, Key dimension, int index) throws IOException { return configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/maps/map.conf") .setVariable("name", name) .setVariable("sorting", "" + (200 + index)) .setVariable("world", formatPath(worldFolder)) + .setVariable("dimension", dimension.getFormatted()) .setVariable("sky-color", "#080010") .setVariable("void-color", "#080010") .setVariable("ambient-light", "0.6") - .setVariable("world-sky-light", "0") .setVariable("remove-caves-below-y", "-10000") .setConditional("max-y-comment", true) .setVariable("max-y", "100"); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/ConfigManager.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/ConfigManager.java index ec8f2d60..b9620e86 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/ConfigManager.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/ConfigManager.java @@ -25,8 +25,10 @@ package de.bluecolored.bluemap.common.config; import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.common.config.typeserializer.KeyTypeSerializer; import de.bluecolored.bluemap.common.config.typeserializer.Vector2iTypeSerializer; import de.bluecolored.bluemap.core.BlueMap; +import de.bluecolored.bluemap.core.util.Key; import org.apache.commons.io.IOUtils; import org.spongepowered.configurate.ConfigurateException; import org.spongepowered.configurate.ConfigurationNode; @@ -165,6 +167,7 @@ public class ConfigManager { .path(path) .defaultOptions(o -> o.serializers(b -> { b.register(Vector2i.class, new Vector2iTypeSerializer()); + b.register(Key.class, new KeyTypeSerializer()); })) .build(); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/MapConfig.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/MapConfig.java index 553c0833..d986bc61 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/MapConfig.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/MapConfig.java @@ -28,43 +28,45 @@ import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.core.map.MapSettings; +import de.bluecolored.bluemap.core.util.Key; +import lombok.AccessLevel; +import lombok.Getter; import org.jetbrains.annotations.Nullable; import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import java.nio.file.Path; -import java.util.Optional; @SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"}) @DebugDump @ConfigSerializable +@Getter public class MapConfig implements MapSettings { - private String name = null; + @Nullable private Path world = null; + @Nullable private Key dimension = null; - private Path world = null; + @Nullable private String name = null; private int sorting = 0; - private Vector2i startPos = null; + @Nullable private Vector2i startPos = null; private String skyColor = "#7dabff"; private String voidColor = "#000000"; private float ambientLight = 0; - private int worldSkyLight = 15; - private int removeCavesBelowY = 55; private int caveDetectionOceanFloor = 10000; private boolean caveDetectionUsesBlockLight = false; - private int minX = Integer.MIN_VALUE; - private int maxX = Integer.MAX_VALUE; - private int minZ = Integer.MIN_VALUE; - private int maxZ = Integer.MAX_VALUE; - private int minY = Integer.MIN_VALUE; - private int maxY = Integer.MAX_VALUE; + @Getter(AccessLevel.NONE) private int minX = Integer.MIN_VALUE; + @Getter(AccessLevel.NONE) private int maxX = Integer.MAX_VALUE; + @Getter(AccessLevel.NONE) private int minZ = Integer.MIN_VALUE; + @Getter(AccessLevel.NONE) private int maxZ = Integer.MAX_VALUE; + @Getter(AccessLevel.NONE) private int minY = Integer.MIN_VALUE; + @Getter(AccessLevel.NONE) private int maxY = Integer.MAX_VALUE; private transient Vector3i min = null; private transient Vector3i max = null; @@ -80,7 +82,7 @@ public class MapConfig implements MapSettings { private boolean ignoreMissingLightData = false; - private ConfigurationNode markerSets = null; + @Nullable private ConfigurationNode markerSets = null; // hidden config fields private int hiresTileSize = 32; @@ -88,61 +90,6 @@ public class MapConfig implements MapSettings { private int lodCount = 3; private int lodFactor = 5; - @Nullable - public String getName() { - return name; - } - - @Nullable - public Path getWorld() { - return world; - } - - @Override - public int getSorting() { - return sorting; - } - - @Override - public Optional getStartPos() { - return Optional.ofNullable(startPos); - } - - @Override - public String getSkyColor() { - return skyColor; - } - - @Override - public String getVoidColor() { - return voidColor; - } - - @Override - public float getAmbientLight() { - return ambientLight; - } - - @Override - public int getWorldSkyLight() { - return worldSkyLight; - } - - @Override - public int getRemoveCavesBelowY() { - return removeCavesBelowY; - } - - @Override - public boolean isCaveDetectionUsesBlockLight() { - return caveDetectionUsesBlockLight; - } - - @Override - public int getCaveDetectionOceanFloor() { - return caveDetectionOceanFloor; - } - public Vector3i getMinPos() { if (min == null) min = new Vector3i(minX, minY, minZ); return min; @@ -153,57 +100,4 @@ public class MapConfig implements MapSettings { return max; } - @Override - public long getMinInhabitedTime() { - return minInhabitedTime; - } - - @Override - public int getMinInhabitedTimeRadius() { - return minInhabitedTimeRadius; - } - - @Override - public boolean isRenderEdges() { - return renderEdges; - } - - @Override - public boolean isSaveHiresLayer() { - return saveHiresLayer; - } - - public String getStorage() { - return storage; - } - - public boolean isIgnoreMissingLightData() { - return ignoreMissingLightData; - } - - @Nullable - public ConfigurationNode getMarkerSets() { - return markerSets; - } - - @Override - public int getHiresTileSize() { - return hiresTileSize; - } - - @Override - public int getLowresTileSize() { - return lowresTileSize; - } - - @Override - public int getLodCount() { - return lodCount; - } - - @Override - public int getLodFactor() { - return lodFactor; - } - } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Dimension.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/typeserializer/KeyTypeSerializer.java similarity index 61% rename from BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Dimension.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/typeserializer/KeyTypeSerializer.java index 7c557b7c..96eaa4c5 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Dimension.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/typeserializer/KeyTypeSerializer.java @@ -22,30 +22,27 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.common.serverinterface; +package de.bluecolored.bluemap.common.config.typeserializer; -import java.nio.file.Path; +import de.bluecolored.bluemap.core.util.Key; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; -public enum Dimension { +import java.lang.reflect.Type; - OVERWORLD ("Overworld", Path.of("")), - NETHER ("Nether", Path.of("DIM-1")), - END ("End", Path.of("DIM1")); +public class KeyTypeSerializer implements TypeSerializer { - private final String name; - private final Path dimensionSubPath; - - Dimension(String name, Path dimensionSubPath) { - this.name = name; - this.dimensionSubPath = dimensionSubPath; + @Override + public Key deserialize(Type type, ConfigurationNode node) { + String formatted = node.getString(); + return formatted != null ? new Key(node.getString()) : null; } - public String getName() { - return name; - } - - public Path getDimensionSubPath() { - return dimensionSubPath; + @Override + public void serialize(Type type, @Nullable Key obj, ConfigurationNode node) throws SerializationException { + if (obj != null) node.set(obj.getFormatted()); } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LivePlayersDataSupplier.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LivePlayersDataSupplier.java index 4e011e9c..3f57c5cf 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LivePlayersDataSupplier.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/live/LivePlayersDataSupplier.java @@ -27,9 +27,9 @@ package de.bluecolored.bluemap.common.live; import com.google.gson.stream.JsonWriter; import de.bluecolored.bluemap.common.config.PluginConfig; import de.bluecolored.bluemap.common.serverinterface.Player; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.logger.Logger; -import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.StringWriter; @@ -39,15 +39,15 @@ import java.util.function.Supplier; public class LivePlayersDataSupplier implements Supplier { - private final ServerInterface server; + private final Server server; private final PluginConfig config; - @Nullable private final String worldId; + private final ServerWorld world; private final Predicate playerFilter; - public LivePlayersDataSupplier(ServerInterface server, PluginConfig config, @Nullable String worldId, Predicate playerFilter) { + public LivePlayersDataSupplier(Server server, PluginConfig config, ServerWorld world, Predicate playerFilter) { this.server = server; this.config = config; - this.worldId = worldId; + this.world = world; this.playerFilter = playerFilter; } @@ -61,9 +61,7 @@ public class LivePlayersDataSupplier implements Supplier { if (config.isLivePlayerMarkers()) { for (Player player : this.server.getOnlinePlayers()) { - if (!player.isOnline()) continue; - - boolean isCorrectWorld = player.getWorld().equals(this.worldId); + boolean isCorrectWorld = player.getWorld().equals(this.world); if (config.isHideInvisible() && player.isInvisible()) continue; if (config.isHideVanished() && player.isVanished()) continue; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java index 411ad4c8..d867d275 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java @@ -25,7 +25,7 @@ package de.bluecolored.bluemap.common.plugin; import de.bluecolored.bluemap.api.debug.DebugDump; -import de.bluecolored.bluemap.common.BlueMapConfigProvider; +import de.bluecolored.bluemap.common.BlueMapConfiguration; import de.bluecolored.bluemap.common.BlueMapService; import de.bluecolored.bluemap.common.InterruptableReentrantLock; import de.bluecolored.bluemap.common.MissingResourcesException; @@ -36,7 +36,8 @@ import de.bluecolored.bluemap.common.plugin.skins.PlayerSkinUpdater; import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask; import de.bluecolored.bluemap.common.rendermanager.RenderManager; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.common.web.*; import de.bluecolored.bluemap.common.web.http.HttpServer; import de.bluecolored.bluemap.core.debug.StateDumper; @@ -46,7 +47,9 @@ import de.bluecolored.bluemap.core.metrics.Metrics; import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.util.FileHelper; +import de.bluecolored.bluemap.core.util.Tristate; import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; import org.jetbrains.annotations.Nullable; import org.spongepowered.configurate.gson.GsonConfigurationLoader; import org.spongepowered.configurate.serialize.SerializationException; @@ -78,15 +81,12 @@ public class Plugin implements ServerEventListener { private final InterruptableReentrantLock loadingLock = new InterruptableReentrantLock(); private final String implementationType; - private final ServerInterface serverInterface; + private final Server serverInterface; private BlueMapService blueMap; private PluginState pluginState; - private Map worlds; - private Map maps; - private RenderManager renderManager; private HttpServer webServer; private Logger webLogger; @@ -101,7 +101,7 @@ public class Plugin implements ServerEventListener { private boolean loaded = false; - public Plugin(String implementationType, ServerInterface serverInterface) { + public Plugin(String implementationType, Server serverInterface) { this.implementationType = implementationType.toLowerCase(); this.serverInterface = serverInterface; @@ -121,11 +121,18 @@ public class Plugin implements ServerEventListener { unload(); //ensure nothing is left running (from a failed load or something) //load configs - blueMap = new BlueMapService(serverInterface, new BlueMapConfigs(serverInterface), preloadedResourcePack); - CoreConfig coreConfig = getConfigs().getCoreConfig(); - WebserverConfig webserverConfig = getConfigs().getWebserverConfig(); - WebappConfig webappConfig = getConfigs().getWebappConfig(); - PluginConfig pluginConfig = getConfigs().getPluginConfig(); + BlueMapConfigManager configManager = BlueMapConfigManager.builder() + .minecraftVersion(serverInterface.getMinecraftVersion()) + .configRoot(serverInterface.getConfigFolder()) + .resourcePacksFolder(serverInterface.getConfigFolder().resolve("resourcepacks")) + .modsFolder(serverInterface.getModsFolder().orElse(null)) + .useMetricsConfig(serverInterface.isMetricsEnabled() == Tristate.UNDEFINED) + .autoConfigWorlds(serverInterface.getLoadedServerWorlds()) + .build(); + CoreConfig coreConfig = configManager.getCoreConfig(); + WebserverConfig webserverConfig = configManager.getWebserverConfig(); + WebappConfig webappConfig = configManager.getWebappConfig(); + PluginConfig pluginConfig = configManager.getPluginConfig(); //apply new file-logger config if (coreConfig.getLog().getFile() != null) { @@ -149,16 +156,19 @@ public class Plugin implements ServerEventListener { pluginState = new PluginState(); } + //create bluemap-service + blueMap = new BlueMapService(configManager, preloadedResourcePack); + //try load resources try { - blueMap.getResourcePack(); + blueMap.getOrLoadResourcePack(); } catch (MissingResourcesException ex) { Logger.global.logWarning("BlueMap is missing important resources!"); Logger.global.logWarning("You must accept the required file download in order for BlueMap to work!"); - BlueMapConfigProvider configProvider = blueMap.getConfigs(); - if (configProvider instanceof BlueMapConfigs) { - Logger.global.logWarning("Please check: " + ((BlueMapConfigs) configProvider).getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize()); + BlueMapConfiguration configProvider = blueMap.getConfig(); + if (configProvider instanceof BlueMapConfigManager) { + Logger.global.logWarning("Please check: " + ((BlueMapConfigManager) configProvider).getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize()); } Logger.global.logInfo("If you have changed the config you can simply reload the plugin using: /bluemap reload"); @@ -167,9 +177,8 @@ public class Plugin implements ServerEventListener { return; } - //load worlds and maps - worlds = blueMap.getWorlds(); - maps = blueMap.getMaps(); + //load maps + Map maps = blueMap.getOrLoadMaps(); //create and start webserver if (webserverConfig.isEnabled()) { @@ -182,7 +191,7 @@ public class Plugin implements ServerEventListener { routingRequestHandler.register(".*", new FileRequestHandler(webroot)); // map route - for (var mapConfigEntry : getConfigs().getMapConfigs().entrySet()) { + for (var mapConfigEntry : configManager.getMapConfigs().entrySet()) { String id = mapConfigEntry.getKey(); MapConfig mapConfig = mapConfigEntry.getValue(); @@ -191,7 +200,7 @@ public class Plugin implements ServerEventListener { if (map != null) { mapRequestHandler = new MapRequestHandler(map, serverInterface, pluginConfig, Predicate.not(pluginState::isPlayerHidden)); } else { - Storage storage = blueMap.getStorage(mapConfig.getStorage()); + Storage storage = blueMap.getOrLoadStorage(mapConfig.getStorage()); mapRequestHandler = new MapRequestHandler(id, storage); } @@ -447,9 +456,6 @@ public class Plugin implements ServerEventListener { Logger.global.remove(DEBUG_FILE_LOG_NAME); //clear resources - worlds = null; - maps = null; - pluginState = null; //done @@ -479,7 +485,7 @@ public class Plugin implements ServerEventListener { } // hold and reuse loaded resourcepack - ResourcePack preloadedResourcePack = this.blueMap.getResourcePackIfLoaded().orElse(null); + ResourcePack preloadedResourcePack = this.blueMap.getResourcePack(); unload(); load(preloadedResourcePack); @@ -491,10 +497,12 @@ public class Plugin implements ServerEventListener { } public synchronized void save() { + if (blueMap == null) return; + if (pluginState != null) { try { GsonConfigurationLoader loader = GsonConfigurationLoader.builder() - .path(blueMap.getConfigs().getCoreConfig().getData().resolve("pluginState.json")) + .path(blueMap.getConfig().getCoreConfig().getData().resolve("pluginState.json")) .build(); loader.save(loader.createNode().set(PluginState.class, pluginState)); } catch (IOException ex) { @@ -502,38 +510,41 @@ public class Plugin implements ServerEventListener { } } - if (maps != null) { - for (BmMap map : maps.values()) { - map.save(); - } + var maps = blueMap.getMaps(); + for (BmMap map : maps.values()) { + map.save(); } } public void saveMarkerStates() { - if (maps != null) { - for (BmMap map : maps.values()) { - map.saveMarkerState(); - } + if (blueMap == null) return; + + var maps = blueMap.getMaps(); + for (BmMap map : maps.values()) { + map.saveMarkerState(); } } public void savePlayerStates() { - if (maps != null) { - for (BmMap map : maps.values()) { - var dataSupplier = new LivePlayersDataSupplier( - serverInterface, - getConfigs().getPluginConfig(), - map.getWorldId(), - Predicate.not(pluginState::isPlayerHidden) - ); - try ( - OutputStream out = map.getStorage().writeMeta(map.getId(), BmMap.META_FILE_PLAYERS); - Writer writer = new OutputStreamWriter(out) - ) { - writer.write(dataSupplier.get()); - } catch (Exception ex) { - Logger.global.logError("Failed to save players for map '" + map.getId() + "'!", ex); - } + if (blueMap == null) return; + + var maps = blueMap.getMaps(); + for (BmMap map : maps.values()) { + var serverWorld = serverInterface.getServerWorld(map.getWorld()).orElse(null); + if (serverWorld == null) continue; + var dataSupplier = new LivePlayersDataSupplier( + serverInterface, + getBlueMap().getConfig().getPluginConfig(), + serverWorld, + Predicate.not(pluginState::isPlayerHidden) + ); + try ( + OutputStream out = map.getStorage().writeMeta(map.getId(), BmMap.META_FILE_PLAYERS); + Writer writer = new OutputStreamWriter(out) + ) { + writer.write(dataSupplier.get()); + } catch (Exception ex) { + Logger.global.logError("Failed to save players for map '" + map.getId() + "'!", ex); } } } @@ -558,7 +569,7 @@ public class Plugin implements ServerEventListener { } public boolean flushWorldUpdates(World world) throws IOException { - var implWorld = serverInterface.getWorld(world.getSaveFolder()).orElse(null); + var implWorld = serverInterface.getServerWorld(world).orElse(null); if (implWorld != null) return implWorld.persistWorldChanges(); return false; } @@ -588,8 +599,8 @@ public class Plugin implements ServerEventListener { } public boolean checkPausedByPlayerCount() { - CoreConfig coreConfig = getConfigs().getCoreConfig(); - PluginConfig pluginConfig = getConfigs().getPluginConfig(); + CoreConfig coreConfig = getBlueMap().getConfig().getCoreConfig(); + PluginConfig pluginConfig = getBlueMap().getConfig().getPluginConfig(); if ( pluginConfig.getPlayerRenderLimit() > 0 && @@ -604,7 +615,12 @@ public class Plugin implements ServerEventListener { } } - public ServerInterface getServerInterface() { + public @Nullable World getWorld(ServerWorld serverWorld) { + String id = MCAWorld.id(serverWorld.getWorldFolder(), serverWorld.getDimension()); + return getBlueMap().getWorlds().get(id); + } + + public Server getServerInterface() { return serverInterface; } @@ -612,22 +628,10 @@ public class Plugin implements ServerEventListener { return blueMap; } - public BlueMapConfigProvider getConfigs() { - return blueMap.getConfigs(); - } - public PluginState getPluginState() { return pluginState; } - public Map getWorlds(){ - return worlds; - } - - public Map getMaps(){ - return maps; - } - public RenderManager getRenderManager() { return renderManager; } @@ -649,9 +653,12 @@ public class Plugin implements ServerEventListener { } private void initFileWatcherTasks() { - for (BmMap map : maps.values()) { - if (pluginState.getMapState(map).isUpdateEnabled()) { - startWatchingMap(map); + var maps = blueMap.getMaps(); + if (maps != null) { + for (BmMap map : maps.values()) { + if (pluginState.getMapState(map).isUpdateEnabled()) { + startWatchingMap(map); + } } } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/RegionFileWatchService.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/RegionFileWatchService.java index f849b921..291574ae 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/RegionFileWatchService.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/RegionFileWatchService.java @@ -25,12 +25,14 @@ package de.bluecolored.bluemap.common.plugin; import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.common.rendermanager.RenderManager; import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask; -import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.util.FileHelper; +import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; import java.io.IOException; import java.nio.file.*; @@ -45,7 +47,7 @@ public class RegionFileWatchService extends Thread { private final RenderManager renderManager; private final WatchService watchService; - private boolean verbose; + private final boolean verbose; private volatile boolean closed; private Timer delayTimer; @@ -60,7 +62,9 @@ public class RegionFileWatchService extends Thread { this.closed = false; this.scheduledUpdates = new HashMap<>(); - Path folder = map.getWorld().getSaveFolder().resolve("region"); + World world = map.getWorld(); + if (!(world instanceof MCAWorld)) throw new UnsupportedOperationException("world-type is not supported"); + Path folder = ((MCAWorld) world).getRegionFolder(); FileHelper.createDirectories(folder); this.watchService = folder.getFileSystem().newWatchService(); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java index a3f8a3d8..27f4e50a 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/CommandHelper.java @@ -30,7 +30,6 @@ import de.bluecolored.bluemap.common.plugin.text.TextColor; import de.bluecolored.bluemap.common.plugin.text.TextFormat; import de.bluecolored.bluemap.common.rendermanager.RenderManager; import de.bluecolored.bluemap.common.rendermanager.RenderTask; -import de.bluecolored.bluemap.core.world.World; import org.apache.commons.lang3.time.DurationFormatUtils; import java.lang.ref.WeakReference; @@ -106,7 +105,7 @@ public class CommandHelper { if (plugin.checkPausedByPlayerCount()) { lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.GOLD, "paused"))); - lines.add(Text.of(TextColor.GRAY, TextFormat.ITALIC, "\u00A0\u00A0\u00A0(there are " + plugin.getConfigs().getPluginConfig().getPlayerRenderLimit() + " or more players online)")); + lines.add(Text.of(TextColor.GRAY, TextFormat.ITALIC, "\u00A0\u00A0\u00A0(there are " + plugin.getBlueMap().getConfig().getPluginConfig().getPlayerRenderLimit() + " or more players online)")); } else { lines.add(Text.of(TextColor.WHITE, " Render-Threads are ", Text.of(TextColor.RED, "stopped") @@ -134,20 +133,22 @@ public class CommandHelper { public Text worldHelperHover() { StringJoiner joiner = new StringJoiner("\n"); - for (World world : plugin.getWorlds().values()) { - joiner.add(world.getName()); + for (String worldId : plugin.getBlueMap().getWorlds().keySet()) { + joiner.add(worldId); } - return Text.of("world").setHoverText(Text.of(TextColor.WHITE, "Available worlds: \n", TextColor.GRAY, joiner.toString())); + return Text.of(TextFormat.UNDERLINED, "world") + .setHoverText(Text.of(TextColor.WHITE, "Available worlds: \n", TextColor.GRAY, joiner.toString())); } public Text mapHelperHover() { StringJoiner joiner = new StringJoiner("\n"); - for (String mapId : plugin.getMaps().keySet()) { + for (String mapId : plugin.getBlueMap().getMaps().keySet()) { joiner.add(mapId); } - return Text.of("map").setHoverText(Text.of(TextColor.WHITE, "Available maps: \n", TextColor.GRAY, joiner.toString())); + return Text.of(TextFormat.UNDERLINED, "map") + .setHoverText(Text.of(TextColor.WHITE, "Available maps: \n", TextColor.GRAY, joiner.toString())); } public synchronized Optional getTaskForRef(String ref) { diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java index 827c6438..c413e141 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java @@ -53,7 +53,7 @@ import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.map.MapRenderState; import de.bluecolored.bluemap.core.storage.Storage; -import de.bluecolored.bluemap.core.world.Block; +import de.bluecolored.bluemap.core.world.block.Block; import de.bluecolored.bluemap.core.world.World; import java.io.IOException; @@ -281,10 +281,10 @@ public class Commands { } } - private Optional parseWorld(String worldName) { - for (World world : plugin.getWorlds().values()) { - if (world.getName().equalsIgnoreCase(worldName)) { - return Optional.of(world); + private Optional parseWorld(String worldId) { + for (var entry : plugin.getBlueMap().getWorlds().entrySet()) { + if (entry.getKey().equals(worldId)) { + return Optional.of(entry.getValue()); } } @@ -292,8 +292,8 @@ public class Commands { } private Optional parseMap(String mapId) { - for (BmMap map : plugin.getMaps().values()) { - if (map.getId().equalsIgnoreCase(mapId)) { + for (BmMap map : plugin.getBlueMap().getMaps().values()) { + if (map.getId().equals(mapId)) { return Optional.of(map); } } @@ -416,7 +416,7 @@ public class Commands { public int debugClearCacheCommand(CommandContext context) { CommandSource source = commandSourceInterface.apply(context.getSource()); - for (World world : plugin.getWorlds().values()) { + for (World world : plugin.getBlueMap().getWorlds().values()) { world.invalidateChunkCache(); } @@ -436,7 +436,7 @@ public class Commands { world = parseWorld(worldName.get()).orElse(null); if (world == null) { - source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " with this name: ", TextColor.WHITE, worldName.get())); + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " with this id: ", TextColor.WHITE, worldName.get())); return 0; } } else { @@ -482,7 +482,7 @@ public class Commands { position = new Vector3d(x.get(), y.get(), z.get()); if (world == null) { - source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " with this name: ", TextColor.WHITE, worldName.get())); + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " with this id: ", TextColor.WHITE, worldName.get())); return 0; } } else { @@ -499,7 +499,7 @@ public class Commands { // collect and output debug info Vector3i blockPos = position.floor().toInt(); Block block = new Block<>(world, blockPos.getX(), blockPos.getY(), blockPos.getZ()); - Block blockBelow = new Block<>(null, 0, 0, 0).copy(block, 0, -1, 0); + Block blockBelow = new Block<>(world, blockPos.getX(), blockPos.getY() - 1, blockPos.getZ()); // populate lazy-loaded values block.getBlockState(); @@ -523,7 +523,7 @@ public class Commands { final CommandSource source = commandSourceInterface.apply(context.getSource()); try { - Path file = plugin.getConfigs().getCoreConfig().getData().resolve("dump.json"); + Path file = plugin.getBlueMap().getConfig().getCoreConfig().getData().resolve("dump.json"); StateDumper.global().dump(file); source.sendMessage(Text.of(TextColor.GREEN, "Dump created at: " + file)); @@ -562,7 +562,7 @@ public class Commands { new Thread(() -> { plugin.getPluginState().setRenderThreadsEnabled(true); - plugin.getRenderManager().start(plugin.getConfigs().getCoreConfig().resolveRenderThreadCount()); + plugin.getRenderManager().start(plugin.getBlueMap().getConfig().getCoreConfig().resolveRenderThreadCount()); source.sendMessage(Text.of(TextColor.GREEN, "Render-Threads started!")); plugin.save(); @@ -583,7 +583,7 @@ public class Commands { BmMap map = parseMap(mapString).orElse(null); if (map == null) { - source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString)); + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this id: ", TextColor.WHITE, mapString)); return 0; } @@ -624,7 +624,7 @@ public class Commands { BmMap map = parseMap(mapString).orElse(null); if (map == null) { - source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString)); + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this id: ", TextColor.WHITE, mapString)); return 0; } @@ -671,7 +671,8 @@ public class Commands { mapToRender = parseMap(worldOrMap.get()).orElse(null); if (mapToRender == null) { - source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " or ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, worldOrMap.get())); + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.worldHelperHover(), " or ", + helper.mapHelperHover(), " with this id: ", TextColor.WHITE, worldOrMap.get())); return 0; } } else { @@ -682,7 +683,8 @@ public class Commands { mapToRender = null; if (worldToRender == null) { - source.sendMessage(Text.of(TextColor.RED, "Can't detect a world from this command-source, you'll have to define a world or a map to update!").setHoverText(Text.of(TextColor.GRAY, "/bluemap " + (force ? "force-update" : "update") + " "))); + source.sendMessage(Text.of(TextColor.RED, "Can't detect a world from this command-source, you'll have to define a world or a map to update!") + .setHoverText(Text.of(TextColor.GRAY, "/bluemap " + (force ? "force-update" : "update") + " "))); return 0; } } @@ -699,7 +701,8 @@ public class Commands { } else { Vector3d position = source.getPosition().orElse(null); if (position == null) { - source.sendMessage(Text.of(TextColor.RED, "Can't detect a position from this command-source, you'll have to define x,z coordinates to update with a radius!").setHoverText(Text.of(TextColor.GRAY, "/bluemap " + (force ? "force-update" : "update") + " " + radius))); + source.sendMessage(Text.of(TextColor.RED, "Can't detect a position from this command-source, you'll have to define x,z coordinates to update with a radius!") + .setHoverText(Text.of(TextColor.GRAY, "/bluemap " + (force ? "force-update" : "update") + " " + radius))); return 0; } @@ -714,16 +717,12 @@ public class Commands { try { List maps = new ArrayList<>(); if (worldToRender != null) { - var world = plugin.getServerInterface().getWorld(worldToRender.getSaveFolder()).orElse(null); - if (world != null) world.persistWorldChanges(); - - for (BmMap map : plugin.getMaps().values()) { - if (map.getWorld().getSaveFolder().equals(worldToRender.getSaveFolder())) maps.add(map); + plugin.flushWorldUpdates(worldToRender); + for (BmMap map : plugin.getBlueMap().getMaps().values()) { + if (map.getWorld().equals(worldToRender)) maps.add(map); } } else { - var world = plugin.getServerInterface().getWorld(mapToRender.getWorld().getSaveFolder()).orElse(null); - if (world != null) world.persistWorldChanges(); - + plugin.flushWorldUpdates(mapToRender.getWorld()); maps.add(mapToRender); } @@ -741,7 +740,8 @@ public class Commands { updateTask.getRegions().forEach(region -> state.setRenderTime(region, -1)); } - source.sendMessage(Text.of(TextColor.GREEN, "Created new Update-Task for map '" + map.getId() + "' ", TextColor.GRAY, "(" + updateTask.getRegions().size() + " regions, ~" + updateTask.getRegions().size() * 1024L + " chunks)")); + source.sendMessage(Text.of(TextColor.GREEN, "Created new Update-Task for map '" + map.getId() + "' ", + TextColor.GRAY, "(" + updateTask.getRegions().size() + " regions, ~" + updateTask.getRegions().size() * 1024L + " chunks)")); } source.sendMessage(Text.of(TextColor.GREEN, "Use ", TextColor.GRAY, "/bluemap", TextColor.GREEN, " to see the progress.")); @@ -790,7 +790,7 @@ public class Commands { BmMap map = parseMap(mapString).orElse(null); if (map == null) { - source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString)); + source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this id: ", TextColor.WHITE, mapString)); return 0; } @@ -831,8 +831,8 @@ public class Commands { CommandSource source = commandSourceInterface.apply(context.getSource()); source.sendMessage(Text.of(TextColor.BLUE, "Worlds loaded by BlueMap:")); - for (var entry : plugin.getWorlds().entrySet()) { - source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, entry.getValue().getName()).setHoverText(Text.of(entry.getValue().getSaveFolder(), TextColor.GRAY, " (" + entry.getKey() + ")"))); + for (var entry : plugin.getBlueMap().getWorlds().entrySet()) { + source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, entry.getKey())); } return 1; @@ -842,7 +842,7 @@ public class Commands { List lines = new ArrayList<>(); lines.add(Text.of(TextColor.BLUE, "Maps loaded by BlueMap:")); - for (BmMap map : plugin.getMaps().values()) { + for (BmMap map : plugin.getBlueMap().getMaps().values()) { boolean frozen = !plugin.getPluginState().getMapState(map).isUpdateEnabled(); lines.add(Text.of(TextColor.GRAY, " - ", @@ -850,7 +850,7 @@ public class Commands { TextColor.GRAY, " (" + map.getName() + ")")); lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0\u00A0World: ", - TextColor.DARK_GRAY, map.getWorld().getName())); + TextColor.DARK_GRAY, map.getWorld().getId())); lines.add(Text.of(TextColor.GRAY, "\u00A0\u00A0\u00A0Last Update: ", TextColor.DARK_GRAY, helper.formatTime(map.getRenderState().getLatestRenderTime()))); @@ -868,7 +868,7 @@ public class Commands { CommandSource source = commandSourceInterface.apply(context.getSource()); source.sendMessage(Text.of(TextColor.BLUE, "Storages loaded by BlueMap:")); - for (var entry : plugin.getBlueMap().getConfigs().getStorageConfigs().entrySet()) { + for (var entry : plugin.getBlueMap().getConfig().getStorageConfigs().entrySet()) { source.sendMessage(Text.of(TextColor.GRAY, " - ", TextColor.WHITE, entry.getKey()) .setHoverText(Text.of(entry.getValue().getStorageType().name())) .setClickAction(Text.ClickAction.RUN_COMMAND, "/bluemap storages " + entry.getKey()) @@ -884,9 +884,10 @@ public class Commands { Storage storage; try { - storage = plugin.getBlueMap().getStorage(storageId); - } catch (ConfigurationException ex) { - source.sendMessage(Text.of(TextColor.RED, ex.getMessage())); + storage = plugin.getBlueMap().getOrLoadStorage(storageId); + } catch (ConfigurationException | InterruptedException ex) { + Logger.global.logError("Unexpected exception trying to load storage '" + storageId + "'!", ex); + source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to load this storage. Please check the console for more details...")); return 0; } @@ -894,8 +895,8 @@ public class Commands { try { mapIds = storage.collectMapIds(); } catch (IOException ex) { - source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to access this storage. Please check the console for more details...")); Logger.global.logError("Unexpected exception trying to load mapIds from storage '" + storageId + "'!", ex); + source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to access this storage. Please check the console for more details...")); return 0; } @@ -904,7 +905,7 @@ public class Commands { source.sendMessage(Text.of(TextColor.GRAY, " ")); } else { for (String mapId : mapIds) { - BmMap map = plugin.getMaps().get(mapId); + BmMap map = plugin.getBlueMap().getMaps().get(mapId); boolean isLoaded = map != null && map.getStorage().equals(storage); if (isLoaded) { @@ -925,13 +926,14 @@ public class Commands { Storage storage; try { - storage = plugin.getBlueMap().getStorage(storageId); - } catch (ConfigurationException ex) { - source.sendMessage(Text.of(TextColor.RED, ex.getMessage())); + storage = plugin.getBlueMap().getOrLoadStorage(storageId); + } catch (ConfigurationException | InterruptedException ex) { + Logger.global.logError("Unexpected exception trying to load storage '" + storageId + "'!", ex); + source.sendMessage(Text.of(TextColor.RED, "There was an unexpected exception trying to load this storage. Please check the console for more details...")); return 0; } - BmMap map = plugin.getMaps().get(mapId); + BmMap map = plugin.getBlueMap().getMaps().get(mapId); boolean isLoaded = map != null && map.getStorage().equals(storage); if (isLoaded) { Text purgeCommand = Text.of(TextColor.WHITE, "/bluemap purge " + mapId) diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java index bca6e84a..42906ec8 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/MapSuggestionProvider.java @@ -25,7 +25,6 @@ package de.bluecolored.bluemap.common.plugin.commands; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.core.map.BmMap; import java.util.Collection; import java.util.HashSet; @@ -40,7 +39,7 @@ public class MapSuggestionProvider extends AbstractSuggestionProvider { @Override public Collection getPossibleValues() { - return new HashSet<>(plugin.getMaps().keySet()); + return new HashSet<>(plugin.getBlueMap().getMaps().keySet()); } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java index 7e73d5b7..fc5a7839 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/StorageSuggestionProvider.java @@ -38,7 +38,7 @@ public class StorageSuggestionProvider extends AbstractSuggestionProvider @Override public Collection getPossibleValues() { - return plugin.getBlueMap().getConfigs().getStorageConfigs().keySet(); + return plugin.getBlueMap().getConfig().getStorageConfigs().keySet(); } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java index 4035c560..cc198d54 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldOrMapSuggestionProvider.java @@ -25,7 +25,6 @@ package de.bluecolored.bluemap.common.plugin.commands; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.core.world.World; import java.util.Collection; import java.util.HashSet; @@ -41,13 +40,8 @@ public class WorldOrMapSuggestionProvider extends AbstractSuggestionProvider< @Override public Collection getPossibleValues() { Collection values = new HashSet<>(); - - for (World world : plugin.getWorlds().values()) { - values.add(world.getName()); - } - - values.addAll(plugin.getMaps().keySet()); - + values.addAll(plugin.getBlueMap().getWorlds().keySet()); + values.addAll(plugin.getBlueMap().getMaps().keySet()); return values; } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java index a55cac9f..42a05cbf 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/WorldSuggestionProvider.java @@ -25,7 +25,6 @@ package de.bluecolored.bluemap.common.plugin.commands; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.core.world.World; import java.util.Collection; import java.util.HashSet; @@ -40,13 +39,7 @@ public class WorldSuggestionProvider extends AbstractSuggestionProvider { @Override public Collection getPossibleValues() { - Collection values = new HashSet<>(); - - for (World world : plugin.getWorlds().values()) { - values.add(world.getName()); - } - - return values; + return new HashSet<>(plugin.getBlueMap().getWorlds().keySet()); } } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java index 3df5e797..8774fd7a 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/skins/PlayerSkinUpdater.java @@ -83,7 +83,7 @@ public class PlayerSkinUpdater implements ServerEventListener { return; } - Map maps = plugin.getMaps(); + Map maps = plugin.getBlueMap().getMaps(); if (maps == null) { Logger.global.logDebug("Could not update skin, since the plugin seems not to be ready."); return; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapUpdateTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapUpdateTask.java index 70362ed9..d4f19739 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapUpdateTask.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapUpdateTask.java @@ -27,7 +27,7 @@ package de.bluecolored.bluemap.common.rendermanager; import com.flowpowered.math.vector.Vector2i; import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.core.map.BmMap; -import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.util.Grid; import de.bluecolored.bluemap.core.world.World; import java.util.ArrayList; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/WorldRegionRenderTask.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/WorldRegionRenderTask.java index f84fb993..e7e90c3d 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/WorldRegionRenderTask.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/WorldRegionRenderTask.java @@ -27,11 +27,14 @@ package de.bluecolored.bluemap.common.rendermanager; import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector2l; import de.bluecolored.bluemap.api.debug.DebugDump; +import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; +import de.bluecolored.bluemap.core.util.Grid; import de.bluecolored.bluemap.core.world.Chunk; -import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.world.ChunkConsumer; import de.bluecolored.bluemap.core.world.Region; +import java.io.IOException; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -71,13 +74,17 @@ public class WorldRegionRenderTask implements RenderTask { Set tileSet = new HashSet<>(); startTime = System.currentTimeMillis(); - //Logger.global.logInfo("Starting: " + worldRegion); - - long changesSince = 0; - if (!force) changesSince = map.getRenderState().getRenderTime(worldRegion); - + // collect chunks + long changesSince = force ? 0 : map.getRenderState().getRenderTime(worldRegion); Region region = map.getWorld().getRegion(worldRegion.getX(), worldRegion.getY()); - Collection chunks = region.listChunks(changesSince); + Collection chunks = new ArrayList<>(1024); + try { + region.iterateAllChunks((ChunkConsumer.ListOnly) (x, z, timestamp) -> { + if (timestamp >= changesSince) chunks.add(new Vector2i(x, z)); + }); + } catch (IOException ex) { + Logger.global.logWarning("Failed to read region " + worldRegion + " from world " + map.getWorld().getName() + " (" + ex + ")"); + } Grid tileGrid = map.getHiresModelManager().getTileGrid(); Grid chunkGrid = map.getWorld().getChunkGrid(); @@ -115,6 +122,10 @@ public class WorldRegionRenderTask implements RenderTask { .collect(Collectors.toCollection(ArrayDeque::new)); if (tiles.isEmpty()) complete(); + else { + // preload chunks + map.getWorld().preloadRegionChunks(worldRegion.getX(), worldRegion.getY()); + } } @Override @@ -132,7 +143,6 @@ public class WorldRegionRenderTask implements RenderTask { this.atWork++; } - //Logger.global.logInfo("Working on " + worldRegion + " - Tile " + tile); if (tileRenderPreconditions(tile)) { map.renderTile(tile); // <- actual work } @@ -163,6 +173,7 @@ public class WorldRegionRenderTask implements RenderTask { for (int z = minChunk.getY(); z <= maxChunk.getY(); z++) { Chunk chunk = map.getWorld().getChunk(x, z); if (!chunk.isGenerated()) return false; + if (!chunk.hasLightData() && !map.getMapSettings().isIgnoreMissingLightData()) return false; if (chunk.getInhabitedTime() >= minInhab) isInhabited = true; } } @@ -184,8 +195,6 @@ public class WorldRegionRenderTask implements RenderTask { private void complete() { map.getRenderState().setRenderTime(worldRegion, startTime); - - //Logger.global.logInfo("Done with: " + worldRegion); } @Override diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java index 065cff62..320f2da4 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Player.java @@ -35,7 +35,7 @@ public interface Player { Text getName(); - String getWorld(); + ServerWorld getWorld(); Vector3d getPosition(); @@ -48,8 +48,6 @@ public interface Player { int getBlockLight(); - boolean isOnline(); - /** * Return true if the player is sneaking. *

If the player is offline the value of this method is undetermined.

diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerInterface.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Server.java similarity index 64% rename from BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerInterface.java rename to BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Server.java index 33be55bf..1a853a13 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerInterface.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/Server.java @@ -24,44 +24,21 @@ */ package de.bluecolored.bluemap.common.serverinterface; -import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.api.debug.DebugDump; +import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.util.Tristate; +import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; import java.nio.file.Path; import java.util.Collection; import java.util.Optional; -import java.util.UUID; -public interface ServerInterface { +public interface Server { @DebugDump MinecraftVersion getMinecraftVersion(); - /** - * Registers a ServerEventListener, every method of this interface should be called on the specified events - */ - void registerListener(ServerEventListener listener); - - /** - * Removes all registered listeners - */ - void unregisterAllListeners(); - - default Optional getWorld(Path worldFolder) { - Path normalizedWorldFolder = worldFolder.toAbsolutePath().normalize(); - return getLoadedWorlds().stream() - .filter(world -> world.getSaveFolder().toAbsolutePath().normalize().equals(normalizedWorldFolder)) - .findAny(); - } - - default Optional getWorld(Object world) { - return Optional.empty(); - } - - @DebugDump - Collection getLoadedWorlds(); - /** * Returns the Folder containing the configurations for the plugin */ @@ -82,6 +59,40 @@ public interface ServerInterface { return Tristate.UNDEFINED; } + /** + * Returns the correct {@link ServerWorld} for a {@link World} if there is any. + */ + default Optional getServerWorld(World world) { + if (world instanceof MCAWorld) { + MCAWorld mcaWorld = (MCAWorld) world; + return getLoadedServerWorlds().stream() + .filter(serverWorld -> + serverWorld.getWorldFolder().toAbsolutePath().normalize() + .equals(mcaWorld.getWorldFolder().toAbsolutePath().normalize()) && + serverWorld.getDimension().equals(mcaWorld.getDimension()) + ) + .findAny(); + } + return Optional.empty(); + } + + /** + * Returns the correct {@link ServerWorld} for any Object if there is any, this should return the correct ServerWorld + * for any implementation-specific object that represent or identify a world in any way.
+ * Used for the API implementation. + */ + default Optional getServerWorld(Object world) { + if (world instanceof World) + return getServerWorld((World) world); + return Optional.empty(); + } + + /** + * Returns all loaded worlds of this server. + */ + @DebugDump + Collection getLoadedServerWorlds(); + /** * Returns a collection of the states of players that are currently online */ @@ -89,9 +100,13 @@ public interface ServerInterface { Collection getOnlinePlayers(); /** - * Returns the state of the player with that UUID if present
- * this method is only guaranteed to return a {@link Player} if the player is currently online. + * Registers a ServerEventListener, every method of this interface should be called on the specified events */ - Optional getPlayer(UUID uuid); + void registerListener(ServerEventListener listener); + + /** + * Removes all registered listeners + */ + void unregisterAllListeners(); } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java index fe627c14..69216b8d 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/serverinterface/ServerWorld.java @@ -25,40 +25,23 @@ package de.bluecolored.bluemap.common.serverinterface; import de.bluecolored.bluemap.api.debug.DebugDump; +import de.bluecolored.bluemap.core.util.Key; import java.io.IOException; import java.nio.file.Path; -import java.util.Optional; public interface ServerWorld { @DebugDump - default Optional getId() { - return Optional.empty(); - } + Path getWorldFolder(); @DebugDump - default Optional getName() { - return Optional.empty(); - } - - @DebugDump - Path getSaveFolder(); - - @DebugDump - default Dimension getDimension() { - Path saveFolder = getSaveFolder(); - String lastName = saveFolder.getFileName().toString(); - if (lastName.equals("DIM-1")) return Dimension.NETHER; - if (lastName.equals("DIM1")) return Dimension.END; - return Dimension.OVERWORLD; - } + Key getDimension(); /** * Attempts to persist all changes that have been made in a world to disk. * * @return true if the changes have been successfully persisted, false if this operation is not supported by the implementation - * * @throws IOException if something went wrong trying to persist the changes */ default boolean persistWorldChanges() throws IOException { diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java index c11d07f5..ce0c362a 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java @@ -24,12 +24,14 @@ */ package de.bluecolored.bluemap.common.web; +import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.common.web.http.HttpRequest; import de.bluecolored.bluemap.common.web.http.HttpRequestHandler; import de.bluecolored.bluemap.common.web.http.HttpResponse; import de.bluecolored.bluemap.common.web.http.HttpStatusCode; import de.bluecolored.bluemap.core.BlueMap; +@DebugDump public class BlueMapResponseModifier implements HttpRequestHandler { private final HttpRequestHandler delegate; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java index 0373ed5a..4d0a9589 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java @@ -24,6 +24,7 @@ */ package de.bluecolored.bluemap.common.web; +import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.common.web.http.*; import org.apache.commons.lang3.time.DateFormatUtils; @@ -38,6 +39,7 @@ import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; +@DebugDump public class FileRequestHandler implements HttpRequestHandler { private final Path webRoot; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/LoggingRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/LoggingRequestHandler.java index 06692347..e71ad860 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/LoggingRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/LoggingRequestHandler.java @@ -24,9 +24,11 @@ */ package de.bluecolored.bluemap.common.web; +import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.common.web.http.*; import de.bluecolored.bluemap.core.logger.Logger; +@DebugDump public class LoggingRequestHandler implements HttpRequestHandler { private final HttpRequestHandler delegate; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapRequestHandler.java index bc9e5160..c7efbab6 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapRequestHandler.java @@ -27,7 +27,8 @@ package de.bluecolored.bluemap.common.web; import de.bluecolored.bluemap.common.config.PluginConfig; import de.bluecolored.bluemap.common.live.LiveMarkersDataSupplier; import de.bluecolored.bluemap.common.live.LivePlayersDataSupplier; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.storage.Storage; import org.jetbrains.annotations.Nullable; @@ -38,9 +39,9 @@ import java.util.function.Supplier; public class MapRequestHandler extends RoutingRequestHandler { - public MapRequestHandler(BmMap map, ServerInterface serverInterface, PluginConfig pluginConfig, Predicate playerFilter) { + public MapRequestHandler(BmMap map, Server serverInterface, PluginConfig pluginConfig, Predicate playerFilter) { this(map.getId(), map.getStorage(), - new LivePlayersDataSupplier(serverInterface, pluginConfig, map.getWorldId(), playerFilter), + createPlayersDataSupplier(map, serverInterface, pluginConfig, playerFilter), new LiveMarkersDataSupplier(map.getMarkerSets())); } @@ -67,4 +68,10 @@ public class MapRequestHandler extends RoutingRequestHandler { } } + private static @Nullable LivePlayersDataSupplier createPlayersDataSupplier(BmMap map, Server serverInterface, PluginConfig pluginConfig, Predicate playerFilter) { + ServerWorld world = serverInterface.getServerWorld(map.getWorld()).orElse(null); + if (world == null) return null; + return new LivePlayersDataSupplier(serverInterface, pluginConfig, world, playerFilter); + } + } diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java index 586f343b..c0b726c5 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java @@ -26,6 +26,7 @@ package de.bluecolored.bluemap.common.web; import com.flowpowered.math.vector.Vector2i; import de.bluecolored.bluemap.api.ContentTypeRegistry; +import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.common.web.http.*; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; @@ -41,6 +42,7 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +@DebugDump public class MapStorageRequestHandler implements HttpRequestHandler { private static final Pattern TILE_PATTERN = Pattern.compile("tiles/([\\d/]+)/x(-?[\\d/]+)z(-?[\\d/]+).*"); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java index 30eeadb5..b21f6e31 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java @@ -24,6 +24,7 @@ */ package de.bluecolored.bluemap.common.web; +import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.common.web.http.HttpRequest; import de.bluecolored.bluemap.common.web.http.HttpRequestHandler; import de.bluecolored.bluemap.common.web.http.HttpResponse; @@ -34,6 +35,7 @@ import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; +@DebugDump public class RoutingRequestHandler implements HttpRequestHandler { public LinkedList routes; @@ -77,6 +79,7 @@ public class RoutingRequestHandler implements HttpRequestHandler { return new HttpResponse(HttpStatusCode.BAD_REQUEST); } + @DebugDump private static class Route { private final Pattern routePattern; diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/http/HttpServer.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/http/HttpServer.java index ca93d31f..562d15f9 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/http/HttpServer.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/http/HttpServer.java @@ -24,8 +24,11 @@ */ package de.bluecolored.bluemap.common.web.http; +import de.bluecolored.bluemap.api.debug.DebugDump; + import java.io.IOException; +@DebugDump public class HttpServer extends Server { private final HttpRequestHandler requestHandler; diff --git a/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf b/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf index 9b948c3a..ecd27f24 100644 --- a/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf +++ b/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf @@ -3,16 +3,20 @@ ## Map-Config ## ## ## -# The name of this map -# This defines the display name of this map, you can change this at any time. -# Default is the id of this map -name: "${name}" - # The path to the save-folder of the world to render. # (If this is not defined (commented out or removed), the map will be only registered to the web-server and the web-app # but not rendered or loaded by BlueMap. This can be used to display a map that has been rendered somewhere else.) world: "${world}" +# The dimension of the world. Can be "minecraft:overworld", "minecraft:the_nether", "minecraft:the_end" +# or any dimension-key introduced by a mod or datapack. +dimension: "${dimension}" + +# The display-name of this map -> how this map will be named on the webapp. +# You can change this at any time. +# Default is the id of this map +name: "${name}" + # A lower value makes the map sorted first (in lists and menus), a higher value makes it sorted later. # The value needs to be an integer but it can be negative. # You can change this at any time. @@ -40,14 +44,6 @@ void-color: "${void-color}" # Default is 0 ambient-light: ${ambient-light} -# Defines the skylight level that the sky of the world is emitting. -# This should always be equivalent to the maximum in-game sky-light for that world! -# If this is a normal overworld dimension, set this to 15 (max). -# If this is a normal nether or end dimension, set this to 0 (min). -# Changing this value requires a re-render of the map. -# Default is 15 -world-sky-light: ${world-sky-light} - # BlueMap tries to omit all blocks that are below this Y-level and are not visible from above-ground. # More specific: Block-Faces that have a sunlight/skylight value of 0 are removed. # This improves the performance of the map on slower devices by a lot, but might cause some blocks to disappear that should normally be visible. diff --git a/BlueMapCore/build.gradle.kts b/BlueMapCore/build.gradle.kts index 6cea1c48..da0d6399 100644 --- a/BlueMapCore/build.gradle.kts +++ b/BlueMapCore/build.gradle.kts @@ -27,7 +27,12 @@ fun String.runCommand(): String = ProcessBuilder(split("\\s(?=(?:[^'\"`]*(['\"`] } val gitHash = "git rev-parse --verify HEAD".runCommand() -val clean = "git status --porcelain".runCommand().isEmpty() +var clean = false; +try { + clean = "git status --porcelain".runCommand().isEmpty(); +} catch (ex: TimeoutException) { + println("Failed to run 'git status --porcelain', assuming dirty version.") +} val lastTag = if ("git tag".runCommand().isEmpty()) "" else "git describe --tags --abbrev=0".runCommand() val lastVersion = if (lastTag.isEmpty()) "dev" else lastTag.substring(1) // remove the leading 'v' val commits = "git rev-list --count $lastTag..HEAD".runCommand() @@ -61,16 +66,22 @@ dependencies { api ("commons-io:commons-io:2.5") api ("org.spongepowered:configurate-hocon:4.1.2") api ("org.spongepowered:configurate-gson:4.1.2") - api ("com.github.Querz:NBT:4.0") + api ("com.github.BlueMap-Minecraft:BlueNBT:v1.3.0") api ("org.apache.commons:commons-dbcp2:2.9.0") api ("io.airlift:aircompressor:0.24") + api ("org.lz4:lz4-java:1.8.0") api ("de.bluecolored.bluemap.api:BlueMapAPI") compileOnly ("org.jetbrains:annotations:23.0.0") + compileOnly ("org.projectlombok:lombok:1.18.28") + + annotationProcessor ("org.projectlombok:lombok:1.18.28") testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2") testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2") + testCompileOnly ("org.projectlombok:lombok:1.18.28") + testAnnotationProcessor ("org.projectlombok:lombok:1.18.28") } spotless { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/BmMap.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/BmMap.java index 47e00b14..32c7d802 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/BmMap.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/BmMap.java @@ -25,6 +25,8 @@ package de.bluecolored.bluemap.core.map; import com.flowpowered.math.vector.Vector2i; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; import com.google.gson.GsonBuilder; import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.api.gson.MarkerGson; @@ -35,7 +37,7 @@ import de.bluecolored.bluemap.core.map.lowres.LowresTileManager; import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson; import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.storage.Storage; -import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.util.Grid; import de.bluecolored.bluemap.core.world.World; import java.io.*; @@ -55,9 +57,13 @@ public class BmMap { public static final String META_FILE_MARKERS = "live/markers.json"; public static final String META_FILE_PLAYERS = "live/players.json"; + private static final Gson GSON = ResourcesGson.addAdapter(new GsonBuilder()) + .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) + .registerTypeAdapter(BmMap.class, new MapSettingsSerializer()) + .create(); + private final String id; private final String name; - private final String worldId; private final World world; private final Storage storage; private final MapSettings mapSettings; @@ -76,10 +82,9 @@ public class BmMap { private long renderTimeSumNanos; private long tilesRendered; - public BmMap(String id, String name, String worldId, World world, Storage storage, ResourcePack resourcePack, MapSettings settings) throws IOException { + public BmMap(String id, String name, World world, Storage storage, ResourcePack resourcePack, MapSettings settings) throws IOException { this.id = Objects.requireNonNull(id); this.name = Objects.requireNonNull(name); - this.worldId = Objects.requireNonNull(worldId); this.world = Objects.requireNonNull(world); this.storage = Objects.requireNonNull(storage); this.resourcePack = Objects.requireNonNull(resourcePack); @@ -197,10 +202,7 @@ public class BmMap { OutputStream out = storage.writeMeta(id, META_FILE_SETTINGS); Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8) ) { - ResourcesGson.addAdapter(new GsonBuilder()) - .registerTypeAdapter(BmMap.class, new MapSettingsSerializer()) - .create() - .toJson(this, writer); + GSON.toJson(this, writer); } catch (Exception ex) { Logger.global.logError("Failed to save settings for map '" + getId() + "'!", ex); } @@ -235,10 +237,6 @@ public class BmMap { return name; } - public String getWorldId() { - return worldId; - } - public World getWorld() { return world; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettings.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettings.java index ace9e3ca..bc76e7ff 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettings.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettings.java @@ -26,14 +26,13 @@ package de.bluecolored.bluemap.core.map; import com.flowpowered.math.vector.Vector2i; import de.bluecolored.bluemap.core.map.hires.RenderSettings; - -import java.util.Optional; +import org.jetbrains.annotations.Nullable; public interface MapSettings extends RenderSettings { int getSorting(); - Optional getStartPos(); + @Nullable Vector2i getStartPos(); String getSkyColor(); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettingsSerializer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettingsSerializer.java index 747fd26d..630d5134 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettingsSerializer.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/MapSettingsSerializer.java @@ -33,6 +33,7 @@ import de.bluecolored.bluemap.core.map.lowres.LowresTileManager; import de.bluecolored.bluemap.core.util.math.Color; import java.lang.reflect.Type; +import java.util.Optional; public class MapSettingsSerializer implements JsonSerializer { @@ -66,7 +67,7 @@ public class MapSettingsSerializer implements JsonSerializer { root.add("lowres", lowres); // startPos - Vector2i startPos = map.getMapSettings().getStartPos() + Vector2i startPos = Optional.ofNullable(map.getMapSettings().getStartPos()) .orElse(map.getWorld().getSpawnPoint().toVector2(true)); root.add("startPos", context.serialize(startPos)); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/TextureGallery.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/TextureGallery.java index 7e76f3c6..75f7f9d8 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/TextureGallery.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/TextureGallery.java @@ -24,6 +24,9 @@ */ package de.bluecolored.bluemap.core.map; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import de.bluecolored.bluemap.api.debug.DebugDump; @@ -43,6 +46,10 @@ import java.util.Map; @DebugDump public class TextureGallery { + private static final Gson GSON = ResourcesGson.addAdapter(new GsonBuilder()) + .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) + .create(); + private final Map, TextureMapping> textureMappings; private int nextId; @@ -93,7 +100,7 @@ public class TextureGallery { }); try (Writer writer = new OutputStreamWriter(out)) { - ResourcesGson.INSTANCE.toJson(textures, Texture[].class, writer); + GSON.toJson(textures, Texture[].class, writer); } catch (JsonIOException ex) { throw new IOException(ex); } @@ -102,7 +109,7 @@ public class TextureGallery { public static TextureGallery readTexturesFile(InputStream in) throws IOException { TextureGallery gallery = new TextureGallery(); try (Reader reader = new InputStreamReader(in)) { - Texture[] textures = ResourcesGson.INSTANCE.fromJson(reader, Texture[].class); + Texture[] textures = GSON.fromJson(reader, Texture[].class); if (textures == null) throw new IOException("Texture data is empty!"); gallery.nextId = textures.length; for (int ordinal = 0; ordinal < textures.length; ordinal++) { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java index 669cc71d..822a0d4f 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelManager.java @@ -31,7 +31,7 @@ import de.bluecolored.bluemap.core.map.TextureGallery; import de.bluecolored.bluemap.core.map.TileMetaConsumer; import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.storage.Storage; -import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.util.Grid; import de.bluecolored.bluemap.core.world.World; import java.io.IOException; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java index d7fc65e7..fbf0707b 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/HiresModelRenderer.java @@ -30,7 +30,8 @@ import de.bluecolored.bluemap.core.map.TileMetaConsumer; import de.bluecolored.bluemap.core.map.hires.blockmodel.BlockStateModelFactory; import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.util.math.Color; -import de.bluecolored.bluemap.core.world.BlockNeighborhood; +import de.bluecolored.bluemap.core.world.Chunk; +import de.bluecolored.bluemap.core.world.block.BlockNeighborhood; import de.bluecolored.bluemap.core.world.World; public class HiresModelRenderer { @@ -68,13 +69,14 @@ public class HiresModelRenderer { for (z = min.getZ(); z <= max.getZ(); z++){ maxHeight = 0; - topBlockLight = 0f; + topBlockLight = 0; columnColor.set(0, 0, 0, 0, true); if (renderSettings.isInsideRenderBoundaries(x, z)) { - minY = Math.max(min.getY(), world.getMinY(x, z)); - maxY = Math.min(max.getY(), world.getMaxY(x, z)); + Chunk chunk = world.getChunkAtBlock(x, z); + minY = Math.max(min.getY(), chunk.getMinY(x, z)); + maxY = Math.min(max.getY(), chunk.getMaxY(x, z)); for (y = minY; y <= maxY; y++) { block.set(x, y, z); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/RenderSettings.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/RenderSettings.java index bb3b03b2..7b8524ba 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/RenderSettings.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/RenderSettings.java @@ -65,11 +65,6 @@ public interface RenderSettings { */ float getAmbientLight(); - /** - * The sky-light level of this world (0-15) - */ - int getWorldSkyLight(); - /** * The same as the maximum height, but blocks that are above this value are treated as AIR.
* This leads to the top-faces being rendered instead of them being culled. @@ -78,6 +73,10 @@ public interface RenderSettings { return true; } + default boolean isIgnoreMissingLightData() { + return false; + } + default boolean isInsideRenderBoundaries(int x, int z) { Vector3i min = getMinPos(); Vector3i max = getMaxPos(); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModelFactory.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModelFactory.java index b094aefb..3539043c 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModelFactory.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/BlockStateModelFactory.java @@ -31,7 +31,7 @@ import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.BlockModel; import de.bluecolored.bluemap.core.resources.resourcepack.blockstate.Variant; import de.bluecolored.bluemap.core.util.math.Color; -import de.bluecolored.bluemap.core.world.BlockNeighborhood; +import de.bluecolored.bluemap.core.world.block.BlockNeighborhood; import de.bluecolored.bluemap.core.world.BlockState; import java.util.ArrayList; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java index e8717a65..fa827088 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/LiquidModelBuilder.java @@ -42,9 +42,9 @@ import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.MatrixM3f; import de.bluecolored.bluemap.core.util.math.VectorM2f; import de.bluecolored.bluemap.core.util.math.VectorM3f; -import de.bluecolored.bluemap.core.world.BlockNeighborhood; +import de.bluecolored.bluemap.core.world.block.BlockNeighborhood; import de.bluecolored.bluemap.core.world.BlockState; -import de.bluecolored.bluemap.core.world.ExtendedBlock; +import de.bluecolored.bluemap.core.world.block.ExtendedBlock; /** * A model builder for all liquid blocks @@ -71,7 +71,6 @@ public class LiquidModelBuilder { private BlockModel modelResource; private BlockModelView blockModel; private Color blockColor; - private boolean isCave; public LiquidModelBuilder(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings) { this.resourcePack = resourcePack; @@ -100,17 +99,19 @@ public class LiquidModelBuilder { this.blockModel = blockModel; this.blockColor = color; - this.isCave = - this.block.getY() < renderSettings.getRemoveCavesBelowY() && - this.block.getY() < block.getChunk().getOceanFloorY(block.getX(), block.getZ()) + renderSettings.getCaveDetectionOceanFloor(); - build(); } private final Color tintcolor = new Color(); private void build() { + int blockLight = block.getBlockLightLevel(); + int sunLight = block.getSunLightLevel(); + // filter out blocks that are in a "cave" that should not be rendered - if (this.isCave && (renderSettings.isCaveDetectionUsesBlockLight() ? block.getBlockLightLevel() : block.getSunLightLevel()) == 0f) return; + if ( + this.block.isRemoveIfCave() && + (renderSettings.isCaveDetectionUsesBlockLight() ? Math.max(blockLight, sunLight) : sunLight) == 0 + ) return; int level = blockState.getLiquidLevel(); if (level < 8 && !(level == 0 && isSameLiquid(block.getNeighborBlock(0, 1, 0)))){ @@ -165,7 +166,7 @@ public class LiquidModelBuilder { blockColor.multiply(tintcolor); // apply light - float combinedLight = Math.max(block.getSunLightLevel() / 15f, block.getBlockLightLevel() / 15f); + float combinedLight = Math.max(sunLight, blockLight) / 15f; combinedLight = (renderSettings.getAmbientLight() + combinedLight) / (renderSettings.getAmbientLight() + 1f); blockColor.r *= combinedLight; blockColor.g *= combinedLight; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java index d2eb1746..58965c5b 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/hires/blockmodel/ResourceModelBuilder.java @@ -45,9 +45,9 @@ import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.util.math.MatrixM4f; import de.bluecolored.bluemap.core.util.math.VectorM2f; import de.bluecolored.bluemap.core.util.math.VectorM3f; -import de.bluecolored.bluemap.core.world.BlockNeighborhood; +import de.bluecolored.bluemap.core.world.block.BlockNeighborhood; import de.bluecolored.bluemap.core.world.BlockProperties; -import de.bluecolored.bluemap.core.world.ExtendedBlock; +import de.bluecolored.bluemap.core.world.block.ExtendedBlock; import de.bluecolored.bluemap.core.world.LightData; /** @@ -74,7 +74,6 @@ public class ResourceModelBuilder { private BlockModelView blockModel; private Color blockColor; private float blockColorOpacity; - private boolean isCave; public ResourceModelBuilder(ResourcePack resourcePack, TextureGallery textureGallery, RenderSettings renderSettings) { this.resourcePack = resourcePack; @@ -95,10 +94,6 @@ public class ResourceModelBuilder { this.variant = variant; this.modelResource = variant.getModel().getResource(); - this.isCave = - this.block.getY() < renderSettings.getRemoveCavesBelowY() && - this.block.getY() < block.getChunk().getOceanFloorY(block.getX(), block.getZ()) + renderSettings.getCaveDetectionOceanFloor(); - this.tintColor.set(0, 0, 0, -1, true); // render model @@ -201,7 +196,10 @@ public class ResourceModelBuilder { int blockLight = Math.max(blockLightData.getBlockLight(), facedLightData.getBlockLight()); // filter out faces that are in a "cave" that should not be rendered - if (isCave && (renderSettings.isCaveDetectionUsesBlockLight() ? Math.max(blockLight, sunLight) : sunLight) == 0f) return; + if ( + block.isRemoveIfCave() && + (renderSettings.isCaveDetectionUsesBlockLight() ? Math.max(blockLight, sunLight) : sunLight) == 0 + ) return; // initialize the faces blockModel.initialize(); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresLayer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresLayer.java index f6296716..9b6b1034 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresLayer.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresLayer.java @@ -31,7 +31,7 @@ import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.util.Vector2iCache; import de.bluecolored.bluemap.core.util.math.Color; -import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.util.Grid; import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.Nullable; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresTileManager.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresTileManager.java index dbb01784..05be3dc6 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresTileManager.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresTileManager.java @@ -27,7 +27,7 @@ package de.bluecolored.bluemap.core.map.lowres; import de.bluecolored.bluemap.core.map.TileMetaConsumer; import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.util.math.Color; -import de.bluecolored.bluemap.core.world.Grid; +import de.bluecolored.bluemap.core.util.Grid; public class LowresTileManager implements TileMetaConsumer { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil113.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil113.java deleted file mode 100644 index 35752b08..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil113.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca; - -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.world.Biome; -import de.bluecolored.bluemap.core.world.BlockState; -import de.bluecolored.bluemap.core.world.LightData; -import net.querz.nbt.*; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -@SuppressWarnings("FieldMayBeFinal") -public class ChunkAnvil113 extends MCAChunk { - private static final long[] EMPTY_LONG_ARRAY = new long[0]; - - private boolean isGenerated; - private boolean hasLight; - private long inhabitedTime; - private Section[] sections; - private int[] biomes; - - private long[] oceanFloorHeights = EMPTY_LONG_ARRAY; - private long[] worldSurfaceHeights = EMPTY_LONG_ARRAY; - - @SuppressWarnings("unchecked") - public ChunkAnvil113(MCAWorld world, CompoundTag chunkTag) { - super(world, chunkTag); - - CompoundTag levelData = chunkTag.getCompoundTag("Level"); - - String status = levelData.getString("Status"); - this.isGenerated = status.equals("full") || - status.equals("fullchunk") || - status.equals("postprocessed"); - this.hasLight = isGenerated; - - this.inhabitedTime = levelData.getLong("InhabitedTime"); - - if (!isGenerated && getWorld().isIgnoreMissingLightData()) { - isGenerated = !status.equals("empty"); - } - - if (levelData.containsKey("Heightmaps")) { - CompoundTag heightmapsTag = levelData.getCompoundTag("Heightmaps"); - this.worldSurfaceHeights = heightmapsTag.getLongArray("WORLD_SURFACE"); - this.oceanFloorHeights = heightmapsTag.getLongArray("OCEAN_FLOOR"); - } - - sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe? - if (levelData.containsKey("Sections")) { - for (CompoundTag sectionTag : ((ListTag) levelData.getListTag("Sections"))) { - Section section = new Section(sectionTag); - if (section.getSectionY() >= 0 && section.getSectionY() < sections.length) sections[section.getSectionY()] = section; - } - } else { - sections = new Section[0]; - } - - Tag tag = levelData.get("Biomes"); //tag can be byte-array or int-array - if (tag instanceof ByteArrayTag) { - byte[] bs = ((ByteArrayTag) tag).getValue(); - biomes = new int[bs.length]; - - for (int i = 0; i < bs.length; i++) { - biomes[i] = bs[i] & 0xFF; - } - } - else if (tag instanceof IntArrayTag) { - biomes = ((IntArrayTag) tag).getValue(); - } - - if (biomes == null || biomes.length == 0) { - biomes = new int[256]; - } - - if (biomes.length < 256) { - biomes = Arrays.copyOf(biomes, 256); - } - } - - @Override - public boolean isGenerated() { - return isGenerated; - } - - @Override - public long getInhabitedTime() { - return inhabitedTime; - } - - @Override - public BlockState getBlockState(int x, int y, int z) { - int sectionY = y >> 4; - if (sectionY < 0 || sectionY >= this.sections.length) return BlockState.AIR; - - Section section = this.sections[sectionY]; - if (section == null) return BlockState.AIR; - - return section.getBlockState(x, y, z); - } - - @Override - public LightData getLightData(int x, int y, int z, LightData target) { - if (!hasLight) return target.set(getWorld().getSkyLight(), 0); - - int sectionY = y >> 4; - if (sectionY < 0 || sectionY >= this.sections.length) - return (y < 0) ? target.set(0, 0) : target.set(getWorld().getSkyLight(), 0); - - Section section = this.sections[sectionY]; - if (section == null) return target.set(getWorld().getSkyLight(), 0); - - return section.getLightData(x, y, z, target); - } - - @Override - public String getBiome(int x, int y, int z) { - x &= 0xF; z &= 0xF; - int biomeIntIndex = z * 16 + x; - - if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT.getFormatted(); - - return LegacyBiomes.idFor(biomes[biomeIntIndex]); - } - - @Override - public int getWorldSurfaceY(int x, int z) { - if (this.worldSurfaceHeights.length < 36) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongStream(this.worldSurfaceHeights, z * 16 + x, 9); - } - - @Override - public int getOceanFloorY(int x, int z) { - if (this.oceanFloorHeights.length < 36) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongStream(this.oceanFloorHeights, z * 16 + x, 9); - } - - private static class Section { - private static final String AIR_ID = "minecraft:air"; - - private int sectionY; - private byte[] blockLight; - private byte[] skyLight; - private long[] blocks; - private BlockState[] palette; - - private int bitsPerBlock; - - @SuppressWarnings("unchecked") - public Section(CompoundTag sectionData) { - this.sectionY = sectionData.get("Y", NumberTag.class).asInt(); - this.blockLight = sectionData.getByteArray("BlockLight"); - this.skyLight = sectionData.getByteArray("SkyLight"); - this.blocks = sectionData.getLongArray("BlockStates"); - - if (blocks.length < 256 && blocks.length > 0) blocks = Arrays.copyOf(blocks, 256); - if (blockLight.length < 2048 && blockLight.length > 0) blockLight = Arrays.copyOf(blockLight, 2048); - if (skyLight.length < 2048 && skyLight.length > 0) skyLight = Arrays.copyOf(skyLight, 2048); - - //read block palette - ListTag paletteTag = (ListTag) sectionData.getListTag("Palette"); - if (paletteTag != null) { - this.palette = new BlockState[paletteTag.size()]; - for (int i = 0; i < this.palette.length; i++) { - CompoundTag stateTag = paletteTag.get(i); - - String id = stateTag.getString("Name"); //shortcut to save time and memory - if (id.equals(AIR_ID)) { - palette[i] = BlockState.AIR; - continue; - } - - Map properties = new HashMap<>(); - - if (stateTag.containsKey("Properties")) { - CompoundTag propertiesTag = stateTag.getCompoundTag("Properties"); - for (Entry> property : propertiesTag) { - properties.put(property.getKey().toLowerCase(), ((StringTag) property.getValue()).getValue().toLowerCase()); - } - } - - palette[i] = new BlockState(id, properties); - } - } else { - this.palette = new BlockState[0]; - } - - this.bitsPerBlock = this.blocks.length >> 6; // available longs * 64 (bits per long) / 4096 (blocks per section) (floored result) - } - - public int getSectionY() { - return sectionY; - } - - public BlockState getBlockState(int x, int y, int z) { - if (palette.length == 1) return palette[0]; - if (blocks.length == 0) return BlockState.AIR; - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockIndex = y * 256 + z * 16 + x; - - long value = MCAMath.getValueFromLongStream(blocks, blockIndex, bitsPerBlock); - if (value >= palette.length) { - Logger.global.noFloodWarning("palettewarning", "Got palette value " + value + " but palette has size of " + palette.length + " (Future occasions of this error will not be logged)"); - return BlockState.MISSING; - } - - return palette[(int) value]; - } - - public LightData getLightData(int x, int y, int z, LightData target) { - if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockByteIndex = y * 256 + z * 16 + x; - int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2 - boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0 - - return target.set( - this.skyLight.length > 0 ? MCAMath.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, - this.blockLight.length > 0 ? MCAMath.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 - ); - } - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil115.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil115.java deleted file mode 100644 index e13cb685..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil115.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca; - -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.world.Biome; -import de.bluecolored.bluemap.core.world.BlockState; -import de.bluecolored.bluemap.core.world.LightData; -import net.querz.nbt.*; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -@SuppressWarnings("FieldMayBeFinal") -public class ChunkAnvil115 extends MCAChunk { - private static final long[] EMPTY_LONG_ARRAY = new long[0]; - - private boolean isGenerated; - private boolean hasLight; - private long inhabitedTime; - private Section[] sections; - private int[] biomes; - - private long[] oceanFloorHeights = EMPTY_LONG_ARRAY; - private long[] worldSurfaceHeights = EMPTY_LONG_ARRAY; - - @SuppressWarnings("unchecked") - public ChunkAnvil115(MCAWorld world, CompoundTag chunkTag) { - super(world, chunkTag); - - CompoundTag levelData = chunkTag.getCompoundTag("Level"); - - String status = levelData.getString("Status"); - this.isGenerated = status.equals("full"); - this.hasLight = isGenerated; - - this.inhabitedTime = levelData.getLong("InhabitedTime"); - - if (!isGenerated && getWorld().isIgnoreMissingLightData()) { - isGenerated = !status.equals("empty"); - } - - if (levelData.containsKey("Heightmaps")) { - CompoundTag heightmapsTag = levelData.getCompoundTag("Heightmaps"); - this.worldSurfaceHeights = heightmapsTag.getLongArray("WORLD_SURFACE"); - this.oceanFloorHeights = heightmapsTag.getLongArray("OCEAN_FLOOR"); - } - - sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe? - if (levelData.containsKey("Sections")) { - for (CompoundTag sectionTag : ((ListTag) levelData.getListTag("Sections"))) { - Section section = new Section(sectionTag); - if (section.getSectionY() >= 0 && section.getSectionY() < sections.length) sections[section.getSectionY()] = section; - } - } else { - sections = new Section[0]; - } - - Tag tag = levelData.get("Biomes"); //tag can be byte-array or int-array - if (tag instanceof ByteArrayTag) { - byte[] bs = ((ByteArrayTag) tag).getValue(); - biomes = new int[bs.length]; - - for (int i = 0; i < bs.length; i++) { - biomes[i] = bs[i] & 0xFF; - } - } - else if (tag instanceof IntArrayTag) { - biomes = ((IntArrayTag) tag).getValue(); - } - - if (biomes == null || biomes.length == 0) { - biomes = new int[1024]; - } - - if (biomes.length < 1024) { - biomes = Arrays.copyOf(biomes, 1024); - } - } - - @Override - public boolean isGenerated() { - return isGenerated; - } - - @Override - public long getInhabitedTime() { - return inhabitedTime; - } - - @Override - public BlockState getBlockState(int x, int y, int z) { - int sectionY = y >> 4; - if (sectionY < 0 || sectionY >= this.sections.length) return BlockState.AIR; - - Section section = this.sections[sectionY]; - if (section == null) return BlockState.AIR; - - return section.getBlockState(x, y, z); - } - - @Override - public LightData getLightData(int x, int y, int z, LightData target) { - if (!hasLight) return target.set(getWorld().getSkyLight(), 0); - - int sectionY = y >> 4; - if (sectionY < 0 || sectionY >= this.sections.length) - return (y < 0) ? target.set(0, 0) : target.set(getWorld().getSkyLight(), 0); - - Section section = this.sections[sectionY]; - if (section == null) return target.set(getWorld().getSkyLight(), 0); - - return section.getLightData(x, y, z, target); - } - - @Override - public String getBiome(int x, int y, int z) { - x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16) - z = (z & 0xF) / 4; - y = y / 4; - int biomeIntIndex = y * 16 + z * 4 + x; - - if (biomeIntIndex < 0) return Biome.DEFAULT.getFormatted(); - if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT.getFormatted(); - - return LegacyBiomes.idFor(biomes[biomeIntIndex]); - } - - @Override - public int getMaxY(int x, int z) { - return sections.length * 16 + 15; - } - - @Override - public int getWorldSurfaceY(int x, int z) { - if (this.worldSurfaceHeights.length < 36) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongStream(this.worldSurfaceHeights, z * 16 + x, 9); - } - - @Override - public int getOceanFloorY(int x, int z) { - if (this.oceanFloorHeights.length < 36) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongStream(this.oceanFloorHeights, z * 16 + x, 9); - } - - private static class Section { - private static final String AIR_ID = "minecraft:air"; - - private int sectionY; - private byte[] blockLight; - private byte[] skyLight; - private long[] blocks; - private BlockState[] palette; - - private int bitsPerBlock; - - @SuppressWarnings("unchecked") - public Section(CompoundTag sectionData) { - this.sectionY = sectionData.get("Y", NumberTag.class).asInt(); - this.blockLight = sectionData.getByteArray("BlockLight"); - this.skyLight = sectionData.getByteArray("SkyLight"); - this.blocks = sectionData.getLongArray("BlockStates"); - - if (blocks.length < 256 && blocks.length > 0) blocks = Arrays.copyOf(blocks, 256); - if (blockLight.length < 2048 && blockLight.length > 0) blockLight = Arrays.copyOf(blockLight, 2048); - if (skyLight.length < 2048 && skyLight.length > 0) skyLight = Arrays.copyOf(skyLight, 2048); - - //read block palette - ListTag paletteTag = (ListTag) sectionData.getListTag("Palette"); - if (paletteTag != null) { - this.palette = new BlockState[paletteTag.size()]; - for (int i = 0; i < this.palette.length; i++) { - CompoundTag stateTag = paletteTag.get(i); - - String id = stateTag.getString("Name"); //shortcut to save time and memory - if (id.equals(AIR_ID)) { - palette[i] = BlockState.AIR; - continue; - } - - Map properties = new HashMap<>(); - - if (stateTag.containsKey("Properties")) { - CompoundTag propertiesTag = stateTag.getCompoundTag("Properties"); - for (Entry> property : propertiesTag) { - properties.put(property.getKey().toLowerCase(), ((StringTag) property.getValue()).getValue().toLowerCase()); - } - } - - palette[i] = new BlockState(id, properties); - } - } else { - this.palette = new BlockState[0]; - } - - this.bitsPerBlock = this.blocks.length >> 6; // available longs * 64 (bits per long) / 4096 (blocks per section) (floored result) - } - - public int getSectionY() { - return sectionY; - } - - public BlockState getBlockState(int x, int y, int z) { - if (palette.length == 1) return palette[0]; - if (blocks.length == 0) return BlockState.AIR; - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockIndex = y * 256 + z * 16 + x; - - long value = MCAMath.getValueFromLongStream(blocks, blockIndex, bitsPerBlock); - if (value >= palette.length) { - Logger.global.noFloodWarning("palettewarning", "Got palette value " + value + " but palette has size of " + palette.length + " (Future occasions of this error will not be logged)"); - return BlockState.MISSING; - } - - return palette[(int) value]; - } - - public LightData getLightData(int x, int y, int z, LightData target) { - if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockByteIndex = y * 256 + z * 16 + x; - int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2 - boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0 - - return target.set( - this.skyLight.length > 0 ? MCAMath.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, - this.blockLight.length > 0 ? MCAMath.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 - ); - } - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil116.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil116.java deleted file mode 100644 index aaf4c429..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil116.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca; - -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.world.Biome; -import de.bluecolored.bluemap.core.world.BlockState; -import de.bluecolored.bluemap.core.world.LightData; -import net.querz.nbt.*; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -@SuppressWarnings("FieldMayBeFinal") -public class ChunkAnvil116 extends MCAChunk { - private static final long[] EMPTY_LONG_ARRAY = new long[0]; - - private boolean isGenerated; - private boolean hasLight; - - private long inhabitedTime; - - private int sectionMin, sectionMax; - private Section[] sections; - - private int[] biomes; - - private long[] oceanFloorHeights = EMPTY_LONG_ARRAY; - private long[] worldSurfaceHeights = EMPTY_LONG_ARRAY; - - @SuppressWarnings("unchecked") - public ChunkAnvil116(MCAWorld world, CompoundTag chunkTag) { - super(world, chunkTag); - - CompoundTag levelData = chunkTag.getCompoundTag("Level"); - - String status = levelData.getString("Status"); - this.isGenerated = status.equals("full"); - this.hasLight = isGenerated; - - this.inhabitedTime = levelData.getLong("InhabitedTime"); - - if (!isGenerated && getWorld().isIgnoreMissingLightData()) { - isGenerated = !status.equals("empty"); - } - - if (levelData.containsKey("Heightmaps")) { - CompoundTag heightmapsTag = levelData.getCompoundTag("Heightmaps"); - this.worldSurfaceHeights = heightmapsTag.getLongArray("WORLD_SURFACE"); - this.oceanFloorHeights = heightmapsTag.getLongArray("OCEAN_FLOOR"); - } - - if (levelData.containsKey("Sections")) { - this.sectionMin = Integer.MAX_VALUE; - this.sectionMax = Integer.MIN_VALUE; - - ListTag sectionsTag = (ListTag) levelData.getListTag("Sections"); - ArrayList
sectionList = new ArrayList<>(sectionsTag.size()); - - for (CompoundTag sectionTag : sectionsTag) { - if (sectionTag.getListTag("Palette") == null) continue; // ignore empty sections - - Section section = new Section(sectionTag); - int y = section.getSectionY(); - - if (sectionMin > y) sectionMin = y; - if (sectionMax < y) sectionMax = y; - - sectionList.add(section); - } - - sections = new Section[1 + sectionMax - sectionMin]; - for (Section section : sectionList) { - sections[section.sectionY - sectionMin] = section; - } - } else { - sections = new Section[0]; - } - - Tag tag = levelData.get("Biomes"); //tag can be byte-array or int-array - if (tag instanceof ByteArrayTag) { - byte[] bs = ((ByteArrayTag) tag).getValue(); - this.biomes = new int[bs.length]; - - for (int i = 0; i < bs.length; i++) { - biomes[i] = bs[i] & 0xFF; - } - } - else if (tag instanceof IntArrayTag) { - this.biomes = ((IntArrayTag) tag).getValue(); - } - - if (biomes == null) { - this.biomes = new int[0]; - } - } - - @Override - public boolean isGenerated() { - return isGenerated; - } - - @Override - public long getInhabitedTime() { - return inhabitedTime; - } - - @Override - public BlockState getBlockState(int x, int y, int z) { - int sectionY = y >> 4; - - Section section = getSection(sectionY); - if (section == null) return BlockState.AIR; - - return section.getBlockState(x, y, z); - } - - @Override - public LightData getLightData(int x, int y, int z, LightData target) { - if (!hasLight) return target.set(getWorld().getSkyLight(), 0); - - int sectionY = y >> 4; - - Section section = getSection(sectionY); - if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(getWorld().getSkyLight(), 0); - - return section.getLightData(x, y, z, target); - } - - @Override - public String getBiome(int x, int y, int z) { - if (biomes.length < 16) return Biome.DEFAULT.getFormatted(); - - x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16) - z = (z & 0xF) / 4; - y = y / 4; - int biomeIntIndex = y * 16 + z * 4 + x; // TODO: fix this for 1.17+ worlds with negative y? - - // shift y up/down if not in range - if (biomeIntIndex >= biomes.length) biomeIntIndex -= (((biomeIntIndex - biomes.length) >> 4) + 1) * 16; - if (biomeIntIndex < 0) biomeIntIndex -= (biomeIntIndex >> 4) * 16; - - return LegacyBiomes.idFor(biomes[biomeIntIndex]); - } - - @Override - public int getMinY(int x, int z) { - return sectionMin * 16; - } - - @Override - public int getMaxY(int x, int z) { - return sectionMax * 16 + 15; - } - - @Override - public int getWorldSurfaceY(int x, int z) { - if (this.worldSurfaceHeights.length < 37) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongArray(this.worldSurfaceHeights, z * 16 + x, 9); - } - - @Override - public int getOceanFloorY(int x, int z) { - if (this.oceanFloorHeights.length < 37) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongArray(this.oceanFloorHeights, z * 16 + x, 9); - } - - private Section getSection(int y) { - y -= sectionMin; - if (y < 0 || y >= this.sections.length) return null; - return this.sections[y]; - } - - private static class Section { - private static final String AIR_ID = "minecraft:air"; - - private int sectionY; - private byte[] blockLight; - private byte[] skyLight; - private long[] blocks; - private BlockState[] palette; - - private int bitsPerBlock; - - @SuppressWarnings("unchecked") - public Section(CompoundTag sectionData) { - this.sectionY = sectionData.get("Y", NumberTag.class).asInt(); - this.blockLight = sectionData.getByteArray("BlockLight"); - this.skyLight = sectionData.getByteArray("SkyLight"); - this.blocks = sectionData.getLongArray("BlockStates"); - - if (blocks.length < 256 && blocks.length > 0) blocks = Arrays.copyOf(blocks, 256); - if (blockLight.length < 2048 && blockLight.length > 0) blockLight = Arrays.copyOf(blockLight, 2048); - if (skyLight.length < 2048 && skyLight.length > 0) skyLight = Arrays.copyOf(skyLight, 2048); - - //read block palette - ListTag paletteTag = (ListTag) sectionData.getListTag("Palette"); - if (paletteTag != null) { - this.palette = new BlockState[paletteTag.size()]; - for (int i = 0; i < this.palette.length; i++) { - CompoundTag stateTag = paletteTag.get(i); - - String id = stateTag.getString("Name"); //shortcut to save time and memory - if (id.equals(AIR_ID)) { - palette[i] = BlockState.AIR; - continue; - } - - Map properties = new HashMap<>(); - - if (stateTag.containsKey("Properties")) { - CompoundTag propertiesTag = stateTag.getCompoundTag("Properties"); - for (Entry> property : propertiesTag) { - properties.put(property.getKey().toLowerCase(), ((StringTag) property.getValue()).getValue().toLowerCase()); - } - } - - palette[i] = new BlockState(id, properties); - } - } else { - this.palette = new BlockState[0]; - } - - this.bitsPerBlock = this.blocks.length >> 6; // available longs * 64 (bits per long) / 4096 (blocks per section) (floored result) - } - - public int getSectionY() { - return sectionY; - } - - public BlockState getBlockState(int x, int y, int z) { - if (palette.length == 1) return palette[0]; - if (blocks.length == 0) return BlockState.AIR; - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockIndex = y * 256 + z * 16 + x; - - long value = MCAMath.getValueFromLongArray(blocks, blockIndex, bitsPerBlock); - if (value >= palette.length) { - Logger.global.noFloodWarning("palettewarning", "Got palette value " + value + " but palette has size of " + palette.length + "! (Future occasions of this error will not be logged)"); - return BlockState.MISSING; - } - - return palette[(int) value]; - } - - public LightData getLightData(int x, int y, int z, LightData target) { - if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockByteIndex = y * 256 + z * 16 + x; - int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2 - boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0 - - return target.set( - this.skyLight.length > 0 ? MCAMath.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, - this.blockLight.length > 0 ? MCAMath.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 - ); - } - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil118.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil118.java deleted file mode 100644 index aaf9db56..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/ChunkAnvil118.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca; - -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.world.Biome; -import de.bluecolored.bluemap.core.world.BlockState; -import de.bluecolored.bluemap.core.world.LightData; -import net.querz.nbt.*; - -import java.util.*; -import java.util.Map.Entry; - -@SuppressWarnings("FieldMayBeFinal") -public class ChunkAnvil118 extends MCAChunk { - private static final long[] EMPTY_LONG_ARRAY = new long[0]; - private static final BlockState[] EMPTY_BLOCK_STATE_ARRAY = new BlockState[0]; - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - - private boolean isGenerated; - private boolean hasLight; - - private long inhabitedTime; - - private int sectionMin, sectionMax; - private Section[] sections; - - private long[] oceanFloorHeights = EMPTY_LONG_ARRAY; - private long[] worldSurfaceHeights = EMPTY_LONG_ARRAY; - - @SuppressWarnings("unchecked") - public ChunkAnvil118(MCAWorld world, CompoundTag chunkTag) { - super(world, chunkTag); - - String status = chunkTag.getString("Status"); - this.isGenerated = status.equals("full") || status.equals("minecraft:full"); - this.hasLight = isGenerated; - - this.inhabitedTime = chunkTag.getLong("InhabitedTime"); - - if (!isGenerated && getWorld().isIgnoreMissingLightData()) { - isGenerated = !status.equals("empty") && !status.equals("minecraft:empty"); - } - - if (chunkTag.containsKey("Heightmaps")) { - CompoundTag heightmapsTag = chunkTag.getCompoundTag("Heightmaps"); - this.worldSurfaceHeights = heightmapsTag.getLongArray("WORLD_SURFACE"); - this.oceanFloorHeights = heightmapsTag.getLongArray("OCEAN_FLOOR"); - } - - if (chunkTag.containsKey("sections")) { - this.sectionMin = Integer.MAX_VALUE; - this.sectionMax = Integer.MIN_VALUE; - - ListTag sectionsTag = (ListTag) chunkTag.getListTag("sections"); - ArrayList
sectionList = new ArrayList<>(sectionsTag.size()); - - for (CompoundTag sectionTag : sectionsTag) { - - Section section = new Section(sectionTag); - int y = section.getSectionY(); - - if (sectionMin > y) sectionMin = y; - if (sectionMax < y) sectionMax = y; - - sectionList.add(section); - } - - sections = new Section[1 + sectionMax - sectionMin]; - for (Section section : sectionList) { - sections[section.sectionY - sectionMin] = section; - } - } else { - sections = new Section[0]; - } - } - - @Override - public boolean isGenerated() { - return isGenerated; - } - - @Override - public long getInhabitedTime() { - return inhabitedTime; - } - - @Override - public BlockState getBlockState(int x, int y, int z) { - int sectionY = y >> 4; - - Section section = getSection(sectionY); - if (section == null) return BlockState.AIR; - - return section.getBlockState(x, y, z); - } - - @Override - public LightData getLightData(int x, int y, int z, LightData target) { - if (!hasLight) return target.set(getWorld().getSkyLight(), 0); - - int sectionY = y >> 4; - - Section section = getSection(sectionY); - if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(getWorld().getSkyLight(), 0); - - return section.getLightData(x, y, z, target); - } - - @Override - public String getBiome(int x, int y, int z) { - int sectionY = y >> 4; - - Section section = getSection(sectionY); - if (section == null) return Biome.DEFAULT.getFormatted(); - - return section.getBiome(x, y, z); - } - - @Override - public int getMinY(int x, int z) { - return sectionMin * 16; - } - - @Override - public int getMaxY(int x, int z) { - return sectionMax * 16 + 15; - } - - @Override - public int getWorldSurfaceY(int x, int z) { - if (this.worldSurfaceHeights.length < 37) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongArray(this.worldSurfaceHeights, z * 16 + x, 9) - 64; - } - - @Override - public int getOceanFloorY(int x, int z) { - if (this.oceanFloorHeights.length < 37) return 0; - - x &= 0xF; z &= 0xF; - return (int) MCAMath.getValueFromLongArray(this.oceanFloorHeights, z * 16 + x, 9) - 64; - } - - private Section getSection(int y) { - y -= sectionMin; - if (y < 0 || y >= this.sections.length) return null; - return this.sections[y]; - } - - private static class Section { - private int sectionY; - private byte[] blockLight; - private byte[] skyLight; - private long[] blocks = EMPTY_LONG_ARRAY; - private long[] biomes = EMPTY_LONG_ARRAY; - private BlockState[] blockPalette = EMPTY_BLOCK_STATE_ARRAY; - private String[] biomePalette = EMPTY_STRING_ARRAY; - - private int bitsPerBlock, bitsPerBiome; - - @SuppressWarnings("unchecked") - public Section(CompoundTag sectionData) { - this.sectionY = sectionData.get("Y", NumberTag.class).asInt(); - this.blockLight = sectionData.getByteArray("BlockLight"); - this.skyLight = sectionData.getByteArray("SkyLight"); - - // blocks - CompoundTag blockStatesTag = sectionData.getCompoundTag("block_states"); - if (blockStatesTag != null) { - // block data - this.blocks = blockStatesTag.getLongArray("data"); - - // block palette - ListTag paletteTag = (ListTag) blockStatesTag.getListTag("palette"); - if (paletteTag != null) { - this.blockPalette = new BlockState[paletteTag.size()]; - for (int i = 0; i < this.blockPalette.length; i++) { - blockPalette[i] = readBlockStatePaletteEntry(paletteTag.get(i)); - } - } - } - - // biomes - CompoundTag biomesTag = sectionData.getCompoundTag("biomes"); - if (biomesTag != null) { - // biomes data - this.biomes = biomesTag.getLongArray("data"); - - // biomes palette - ListTag paletteTag = (ListTag) biomesTag.getListTag("palette"); - if (paletteTag != null) { - this.biomePalette = new String[paletteTag.size()]; - for (int i = 0; i < this.biomePalette.length; i++) { - biomePalette[i] = paletteTag.get(i).getValue(); - } - } - } - - if (blocks.length < 256 && blocks.length > 0) blocks = Arrays.copyOf(blocks, 256); - if (blockLight.length < 2048 && blockLight.length > 0) blockLight = Arrays.copyOf(blockLight, 2048); - if (skyLight.length < 2048 && skyLight.length > 0) skyLight = Arrays.copyOf(skyLight, 2048); - - this.bitsPerBlock = this.blocks.length >> 6; // available longs * 64 (bits per long) / 4096 (blocks per section) (floored result) - this.bitsPerBiome = Integer.SIZE - Integer.numberOfLeadingZeros(this.biomePalette.length - 1); - } - - private BlockState readBlockStatePaletteEntry(CompoundTag paletteEntry) { - String id = paletteEntry.getString("Name"); - if (BlockState.AIR.getFormatted().equals(id)) return BlockState.AIR; //shortcut to save time and memory - - Map properties = new LinkedHashMap<>(); - if (paletteEntry.containsKey("Properties")) { - CompoundTag propertiesTag = paletteEntry.getCompoundTag("Properties"); - for (Entry> property : propertiesTag) { - properties.put(property.getKey().toLowerCase(), ((StringTag) property.getValue()).getValue().toLowerCase()); - } - } - - return new BlockState(id, properties); - } - - public int getSectionY() { - return sectionY; - } - - public BlockState getBlockState(int x, int y, int z) { - if (blockPalette.length == 1) return blockPalette[0]; - if (blocks.length == 0) return BlockState.AIR; - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockIndex = y * 256 + z * 16 + x; - - long value = MCAMath.getValueFromLongArray(blocks, blockIndex, bitsPerBlock); - if (value >= blockPalette.length) { - Logger.global.noFloodWarning("palettewarning", "Got block-palette value " + value + " but palette has size of " + blockPalette.length + "! (Future occasions of this error will not be logged)"); - return BlockState.MISSING; - } - - return blockPalette[(int) value]; - } - - public LightData getLightData(int x, int y, int z, LightData target) { - if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); - - x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16) - - int blockByteIndex = y * 256 + z * 16 + x; - int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2 - boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0 - - return target.set( - this.skyLight.length > 0 ? MCAMath.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, - this.blockLight.length > 0 ? MCAMath.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 - ); - } - - public String getBiome(int x, int y, int z) { - if (biomePalette.length == 0) return Biome.DEFAULT.getValue(); - if (biomePalette.length == 1 || biomes.length == 0) return biomePalette[0]; - - x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16) / 4 - z = (z & 0xF) / 4; - y = (y & 0xF) / 4; - int biomeIndex = y * 16 + z * 4 + x; - - long value = MCAMath.getValueFromLongArray(biomes, biomeIndex, bitsPerBiome); - if (value >= biomePalette.length) { - Logger.global.noFloodWarning("biomepalettewarning", "Got biome-palette value " + value + " but palette has size of " + biomePalette.length + "! (Future occasions of this error will not be logged)"); - return Biome.DEFAULT.getValue(); - } - - return biomePalette[(int) value]; - } - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAChunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAChunk.java deleted file mode 100644 index b85e3276..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAChunk.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca; - -import de.bluecolored.bluemap.core.world.BlockState; -import de.bluecolored.bluemap.core.world.Chunk; -import de.bluecolored.bluemap.core.world.LightData; -import net.querz.nbt.CompoundTag; - -import java.io.IOException; - -public abstract class MCAChunk implements Chunk { - - private final MCAWorld world; - private final int dataVersion; - - protected MCAChunk() { - this.world = null; - this.dataVersion = -1; - } - - protected MCAChunk(MCAWorld world) { - this.world = world; - this.dataVersion = -1; - } - - protected MCAChunk(MCAWorld world, CompoundTag chunkTag) { - this.world = world; - dataVersion = chunkTag.getInt("DataVersion"); - } - - @Override - public abstract boolean isGenerated(); - - public int getDataVersion() { - return dataVersion; - } - - @Override - public abstract long getInhabitedTime(); - - @Override - public abstract BlockState getBlockState(int x, int y, int z); - - @Override - public abstract LightData getLightData(int x, int y, int z, LightData target); - - @Override - public abstract String getBiome(int x, int y, int z); - - @Override - public int getMaxY(int x, int z) { - return 255; - } - - @Override - public int getMinY(int x, int z) { - return 0; - } - - @Override - public int getWorldSurfaceY(int x, int z) { return 0; } - - @Override - public int getOceanFloorY(int x, int z) { return 0; } - - protected MCAWorld getWorld() { - return world; - } - - public static MCAChunk create(MCAWorld world, CompoundTag chunkTag) throws IOException { - int version = chunkTag.getInt("DataVersion"); - - if (version < 2200) return new ChunkAnvil113(world, chunkTag); - if (version < 2500) return new ChunkAnvil115(world, chunkTag); - if (version < 2844) return new ChunkAnvil116(world, chunkTag); - return new ChunkAnvil118(world, chunkTag); - } - - @Override - public String toString() { - return "MCAChunk{" + - "world=" + world + - "dataVersion=" + dataVersion + - "isGenerated()=" + isGenerated() + - '}'; - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java deleted file mode 100644 index 0d681ced..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAWorld.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca; - -import com.flowpowered.math.vector.Vector2i; -import com.flowpowered.math.vector.Vector3i; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import de.bluecolored.bluemap.api.debug.DebugDump; -import de.bluecolored.bluemap.core.BlueMap; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.mca.region.RegionType; -import de.bluecolored.bluemap.core.util.Vector2iCache; -import de.bluecolored.bluemap.core.world.*; -import net.querz.nbt.CompoundTag; -import net.querz.nbt.NBTUtil; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -@DebugDump -public class MCAWorld implements World { - - private static final Grid CHUNK_GRID = new Grid(16); - private static final Grid REGION_GRID = new Grid(32).multiply(CHUNK_GRID); - - private static final Vector2iCache VECTOR_2_I_CACHE = new Vector2iCache(); - - private final Path worldFolder; - - private final String name; - private final Vector3i spawnPoint; - - private final int skyLight; - private final boolean ignoreMissingLightData; - - private final LoadingCache regionCache; - private final LoadingCache chunkCache; - - public MCAWorld(Path worldFolder, int skyLight, boolean ignoreMissingLightData) throws IOException { - this.worldFolder = worldFolder.toRealPath(); - this.skyLight = skyLight; - this.ignoreMissingLightData = ignoreMissingLightData; - - this.regionCache = Caffeine.newBuilder() - .executor(BlueMap.THREAD_POOL) - .maximumSize(100) - .expireAfterWrite(1, TimeUnit.MINUTES) - .build(this::loadRegion); - - this.chunkCache = Caffeine.newBuilder() - .executor(BlueMap.THREAD_POOL) - .maximumSize(500) - .expireAfterWrite(1, TimeUnit.MINUTES) - .build(this::loadChunk); - - try { - Path levelFile = resolveLevelFile(worldFolder); - CompoundTag level = (CompoundTag) NBTUtil.readTag(levelFile.toFile()); - CompoundTag levelData = level.getCompoundTag("Data"); - - this.name = levelData.getString("LevelName"); - - this.spawnPoint = new Vector3i( - levelData.getInt("SpawnX"), - levelData.getInt("SpawnY"), - levelData.getInt("SpawnZ") - ); - } catch (ClassCastException | NullPointerException ex) { - throw new IOException("Invalid level.dat format!", ex); - } - } - - @Override - public Chunk getChunkAtBlock(int x, int y, int z) { - return getChunk(x >> 4, z >> 4); - } - - @Override - public Chunk getChunk(int x, int z) { - return getChunk(VECTOR_2_I_CACHE.get(x, z)); - } - - private Chunk getChunk(Vector2i pos) { - return chunkCache.get(pos); - } - - @Override - public Region getRegion(int x, int z) { - return getRegion(VECTOR_2_I_CACHE.get(x, z)); - } - - private Region getRegion(Vector2i pos) { - return regionCache.get(pos); - } - - @Override - public Collection listRegions() { - File[] regionFiles = getRegionFolder().toFile().listFiles(); - if (regionFiles == null) return Collections.emptyList(); - - List regions = new ArrayList<>(regionFiles.length); - - for (File file : regionFiles) { - if (RegionType.forFileName(file.getName()) == null) continue; - if (file.length() <= 0) continue; - - try { - String[] filenameParts = file.getName().split("\\."); - int rX = Integer.parseInt(filenameParts[1]); - int rZ = Integer.parseInt(filenameParts[2]); - - regions.add(new Vector2i(rX, rZ)); - } catch (NumberFormatException ignore) {} - } - - return regions; - } - - @Override - public String getName() { - return name; - } - - @Override - public Path getSaveFolder() { - return worldFolder; - } - - @Override - public int getSkyLight() { - return skyLight; - } - - @Override - public int getMinY(int x, int z) { - return getChunk(x >> 4, z >> 4).getMinY(x, z); - } - - @Override - public int getMaxY(int x, int z) { - return getChunk(x >> 4, z >> 4).getMaxY(x, z); - } - - @Override - public Grid getChunkGrid() { - return CHUNK_GRID; - } - - @Override - public Grid getRegionGrid() { - return REGION_GRID; - } - - @Override - public Vector3i getSpawnPoint() { - return spawnPoint; - } - - @Override - public void invalidateChunkCache() { - chunkCache.invalidateAll(); - } - - @Override - public void invalidateChunkCache(int x, int z) { - chunkCache.invalidate(VECTOR_2_I_CACHE.get(x, z)); - } - - @Override - public void cleanUpChunkCache() { - chunkCache.cleanUp(); - } - - public Path getWorldFolder() { - return worldFolder; - } - - private Path getRegionFolder() { - return worldFolder.resolve("region"); - } - - public boolean isIgnoreMissingLightData() { - return ignoreMissingLightData; - } - - private Region loadRegion(Vector2i regionPos) { - return loadRegion(regionPos.getX(), regionPos.getY()); - } - - Region loadRegion(int x, int z) { - return RegionType.loadRegion(this, getRegionFolder(), x, z); - } - - private Chunk loadChunk(Vector2i chunkPos) { - return loadChunk(chunkPos.getX(), chunkPos.getY()); - } - - Chunk loadChunk(int x, int z) { - final int tries = 3; - final int tryInterval = 1000; - - Exception loadException = null; - for (int i = 0; i < tries; i++) { - try { - return getRegion(x >> 5, z >> 5) - .loadChunk(x, z, ignoreMissingLightData); - } catch (IOException | RuntimeException e) { - if (loadException != null) e.addSuppressed(loadException); - loadException = e; - - if (i + 1 < tries) { - try { - Thread.sleep(tryInterval); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - break; - } - } - } - } - - Logger.global.logDebug("Unexpected exception trying to load chunk (x:" + x + ", z:" + z + "):" + loadException); - return EmptyChunk.INSTANCE; - } - - - - @Override - public String toString() { - return "MCAWorld{" + - "worldFolder=" + worldFolder + - ", name='" + name + '\'' + - ", spawnPoint=" + spawnPoint + - ", skyLight=" + skyLight + - ", ignoreMissingLightData=" + ignoreMissingLightData + - '}'; - } - - private static Path resolveLevelFile(Path worldFolder) throws IOException { - Path levelFolder = worldFolder.toRealPath(); - Path levelFile = levelFolder.resolve("level.dat"); - int searchDepth = 0; - - while (!Files.isRegularFile(levelFile) && searchDepth < 4) { - searchDepth++; - levelFolder = levelFolder.getParent(); - if (levelFolder == null) break; - - levelFile = levelFolder.resolve("level.dat"); - } - - if (!Files.isRegularFile(levelFile)) - throw new FileNotFoundException("Could not find a level.dat file for this world!"); - - return levelFile; - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/LinearRegion.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/LinearRegion.java deleted file mode 100644 index 05d3cd98..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/LinearRegion.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca.region; - -import com.flowpowered.math.vector.Vector2i; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.mca.MCAChunk; -import de.bluecolored.bluemap.core.mca.MCAWorld; -import de.bluecolored.bluemap.core.world.Chunk; -import de.bluecolored.bluemap.core.world.EmptyChunk; -import de.bluecolored.bluemap.core.world.Region; -import io.airlift.compress.zstd.ZstdInputStream; -import net.querz.nbt.CompoundTag; -import net.querz.nbt.Tag; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; - -public class LinearRegion implements Region { - - public static final String FILE_SUFFIX = ".linear"; - - private static final List SUPPORTED_VERSIONS = Arrays.asList((byte) 1, (byte) 2); - private static final long SUPERBLOCK = -4323716122432332390L; - private static final int HEADER_SIZE = 32; - private static final int FOOTER_SIZE = 8; - - private final MCAWorld world; - private final Path regionFile; - private final Vector2i regionPos; - - - public LinearRegion(MCAWorld world, Path regionFile) throws IllegalArgumentException { - this.world = world; - this.regionFile = regionFile; - - String[] filenameParts = regionFile.getFileName().toString().split("\\."); - int rX = Integer.parseInt(filenameParts[1]); - int rZ = Integer.parseInt(filenameParts[2]); - - this.regionPos = new Vector2i(rX, rZ); - } - - @Override - public Chunk loadChunk(int chunkX, int chunkZ, boolean ignoreMissingLightData) throws IOException { - if (Files.notExists(regionFile)) return EmptyChunk.INSTANCE; - - long fileLength = Files.size(regionFile); - if (fileLength == 0) return EmptyChunk.INSTANCE; - - try (InputStream inputStream = Files.newInputStream(regionFile); - DataInputStream rawDataStream = new DataInputStream(inputStream)) { - - long superBlock = rawDataStream.readLong(); - if (superBlock != SUPERBLOCK) - throw new RuntimeException("Invalid superblock: " + superBlock + " file " + regionFile); - - byte version = rawDataStream.readByte(); - if (!SUPPORTED_VERSIONS.contains(version)) - throw new RuntimeException("Invalid version: " + version + " file " + regionFile); - - // Skip newestTimestamp (Long) + Compression level (Byte) + Chunk count (Short): Unused. - rawDataStream.skipBytes(11); - - int dataCount = rawDataStream.readInt(); - if (fileLength != HEADER_SIZE + dataCount + FOOTER_SIZE) - throw new RuntimeException("Invalid file length: " + this.regionFile + " " + fileLength + " " + (HEADER_SIZE + dataCount + FOOTER_SIZE)); - - // Skip data hash (Long): Unused. - rawDataStream.skipBytes(8); - - byte[] rawCompressed = new byte[dataCount]; - rawDataStream.readFully(rawCompressed, 0, dataCount); - - superBlock = rawDataStream.readLong(); - if (superBlock != SUPERBLOCK) - throw new RuntimeException("Invalid footer superblock: " + this.regionFile); - - try (DataInputStream dis = new DataInputStream(new ZstdInputStream(new ByteArrayInputStream(rawCompressed)))) { - int x = chunkX - (regionPos.getX() << 5); - int z = chunkZ - (regionPos.getY() << 5); - int pos = (z << 5) + x; - int skip = 0; - - for (int i = 0; i < pos; i++) { - skip += dis.readInt(); // Size of the chunk (bytes) to skip - dis.skipBytes(4); // Skip timestamps - } - - int size = dis.readInt(); - if (size <= 0) return EmptyChunk.INSTANCE; - - dis.skipBytes(((1024 - pos - 1) << 3) + 4); // Skip current chunk 0 and unneeded other chunks zero/size - dis.skipBytes(skip); // Skip unneeded chunks data - - Tag tag = Tag.deserialize(dis, Tag.DEFAULT_MAX_DEPTH); - if (tag instanceof CompoundTag) { - MCAChunk chunk = MCAChunk.create(world, (CompoundTag) tag); - if (!chunk.isGenerated()) return EmptyChunk.INSTANCE; - return chunk; - } else { - throw new IOException("Invalid data tag: " + (tag == null ? "null" : tag.getClass().getName())); - } - } - } catch (RuntimeException e) { - throw new IOException(e); - } - } - - @Override - public Collection listChunks(long modifiedSince) { - if (Files.notExists(regionFile)) return Collections.emptyList(); - - long fileLength; - try { - fileLength = Files.size(regionFile); - if (fileLength == 0) return Collections.emptyList(); - } catch (IOException ex) { - Logger.global.logWarning("Failed to read file-size for file: " + regionFile); - return Collections.emptyList(); - } - - List chunks = new ArrayList<>(1024); //1024 = 32 x 32 chunks per region-file - try (InputStream inputStream = Files.newInputStream(regionFile); - DataInputStream rawDataStream = new DataInputStream(inputStream)) { - - long superBlock = rawDataStream.readLong(); - if (superBlock != SUPERBLOCK) - throw new RuntimeException("Invalid superblock: " + superBlock + " file " + regionFile); - - byte version = rawDataStream.readByte(); - if (!SUPPORTED_VERSIONS.contains(version)) - throw new RuntimeException("Invalid version: " + version + " file " + regionFile); - - int date = (int) (modifiedSince / 1000); - - // If whole region is the same - skip. - long newestTimestamp = rawDataStream.readLong(); - if (newestTimestamp < date) return Collections.emptyList(); - - // Linear v1 files store whole region timestamp, not chunk timestamp. We need to render the whole region file. - if (version == 1) { - for(int i = 0 ; i < 1024; i++) - chunks.add(new Vector2i((regionPos.getX() << 5) + (i & 31), (regionPos.getY() << 5) + (i >> 5))); - return chunks; - } - // Linear v2: Chunk timestamps are here! - // Skip Compression level (Byte) + Chunk count (Short): Unused. - rawDataStream.skipBytes(3); - - int dataCount = rawDataStream.readInt(); - if (fileLength != HEADER_SIZE + dataCount + FOOTER_SIZE) - throw new RuntimeException("Invalid file length: " + this.regionFile + " " + fileLength + " " + (HEADER_SIZE + dataCount + FOOTER_SIZE)); - - // Skip data hash (Long): Unused. - rawDataStream.skipBytes(8); - - byte[] rawCompressed = new byte[dataCount]; - rawDataStream.readFully(rawCompressed, 0, dataCount); - - superBlock = rawDataStream.readLong(); - if (superBlock != SUPERBLOCK) - throw new RuntimeException("Invalid footer SuperBlock: " + this.regionFile); - - try (DataInputStream dis = new DataInputStream(new ZstdInputStream(new ByteArrayInputStream(rawCompressed)))) { - for (int i = 0; i < 1024; i++) { - dis.skipBytes(4); // Skip size of the chunk - int timestamp = dis.readInt(); - if (timestamp >= date) // Timestamps - chunks.add(new Vector2i((regionPos.getX() << 5) + (i & 31), (regionPos.getY() << 5) + (i >> 5))); - } - } - } catch (RuntimeException | IOException ex) { - Logger.global.logWarning("Failed to read .linear file: " + regionFile + " (" + ex + ")"); - } - return chunks; - } - - @Override - public Path getRegionFile() { - return regionFile; - } - - public static String getRegionFileName(int regionX, int regionZ) { - return "r." + regionX + "." + regionZ + FILE_SUFFIX; - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/MCARegion.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/MCARegion.java deleted file mode 100644 index 202ea0d1..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/MCARegion.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.mca.region; - -import com.flowpowered.math.vector.Vector2i; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.mca.MCAChunk; -import de.bluecolored.bluemap.core.mca.MCAWorld; -import de.bluecolored.bluemap.core.world.Chunk; -import de.bluecolored.bluemap.core.world.EmptyChunk; -import de.bluecolored.bluemap.core.world.Region; -import net.querz.nbt.CompoundTag; -import net.querz.nbt.Tag; -import net.querz.nbt.mca.CompressionType; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -public class MCARegion implements Region { - - public static final String FILE_SUFFIX = ".mca"; - - private final MCAWorld world; - private final Path regionFile; - private final Vector2i regionPos; - - public MCARegion(MCAWorld world, Path regionFile) throws IllegalArgumentException { - this.world = world; - this.regionFile = regionFile; - - String[] filenameParts = regionFile.getFileName().toString().split("\\."); - int rX = Integer.parseInt(filenameParts[1]); - int rZ = Integer.parseInt(filenameParts[2]); - - this.regionPos = new Vector2i(rX, rZ); - } - - @Override - public Chunk loadChunk(int chunkX, int chunkZ, boolean ignoreMissingLightData) throws IOException { - if (Files.notExists(regionFile)) return EmptyChunk.INSTANCE; - - long fileLength = Files.size(regionFile); - if (fileLength == 0) return EmptyChunk.INSTANCE; - - try (RandomAccessFile raf = new RandomAccessFile(regionFile.toFile(), "r")) { - - int xzChunk = Math.floorMod(chunkZ, 32) * 32 + Math.floorMod(chunkX, 32); - - raf.seek(xzChunk * 4L); - int offset = raf.read() << 16; - offset |= (raf.read() & 0xFF) << 8; - offset |= raf.read() & 0xFF; - offset *= 4096; - - int size = raf.readByte() * 4096; - if (size == 0) { - return EmptyChunk.INSTANCE; - } - - raf.seek(offset + 4); // +4 skip chunk size - - byte compressionTypeByte = raf.readByte(); - CompressionType compressionType = compressionTypeByte == 3 ? - CompressionType.NONE : - CompressionType.getFromID(compressionTypeByte); - if (compressionType == null) { - throw new IOException("Invalid compression type " + compressionTypeByte); - } - - DataInputStream dis = new DataInputStream(new BufferedInputStream(compressionType.decompress(new FileInputStream(raf.getFD())))); - Tag tag = Tag.deserialize(dis, Tag.DEFAULT_MAX_DEPTH); - if (tag instanceof CompoundTag) { - MCAChunk chunk = MCAChunk.create(world, (CompoundTag) tag); - if (!chunk.isGenerated()) return EmptyChunk.INSTANCE; - return chunk; - } else { - throw new IOException("Invalid data tag: " + (tag == null ? "null" : tag.getClass().getName())); - } - - } catch (RuntimeException e) { - throw new IOException(e); - } - } - - @Override - public Collection listChunks(long modifiedSince) { - if (Files.notExists(regionFile)) return Collections.emptyList(); - - try { - long fileLength = Files.size(regionFile); - if (fileLength == 0) return Collections.emptyList(); - } catch (IOException ex) { - Logger.global.logWarning("Failed to read file-size for file: " + regionFile); - return Collections.emptyList(); - } - - List chunks = new ArrayList<>(1024); //1024 = 32 x 32 chunks per region-file - - try (RandomAccessFile raf = new RandomAccessFile(regionFile.toFile(), "r")) { - for (int x = 0; x < 32; x++) { - for (int z = 0; z < 32; z++) { - Vector2i chunk = new Vector2i(regionPos.getX() * 32 + x, regionPos.getY() * 32 + z); - int xzChunk = z * 32 + x; - - raf.seek(xzChunk * 4 + 3); - int size = raf.readByte() * 4096; - - if (size == 0) continue; - - raf.seek(xzChunk * 4 + 4096); - int timestamp = raf.read() << 24; - timestamp |= (raf.read() & 0xFF) << 16; - timestamp |= (raf.read() & 0xFF) << 8; - timestamp |= raf.read() & 0xFF; - - if (timestamp >= (modifiedSince / 1000)) { - chunks.add(chunk); - } - } - } - } catch (RuntimeException | IOException ex) { - Logger.global.logWarning("Failed to read .mca file: " + regionFile + " (" + ex + ")"); - } - - return chunks; - } - - @Override - public Path getRegionFile() { - return regionFile; - } - - public static String getRegionFileName(int regionX, int regionZ) { - return "r." + regionX + "." + regionZ + FILE_SUFFIX; - } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java index 1719f2c0..e745c5e7 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java @@ -29,7 +29,7 @@ import com.google.gson.stream.JsonReader; import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.world.Biome; -import de.bluecolored.bluemap.core.world.BlockNeighborhood; +import de.bluecolored.bluemap.core.world.block.BlockNeighborhood; import java.awt.image.BufferedImage; import java.io.BufferedReader; @@ -42,6 +42,14 @@ import java.util.Map; @DebugDump public class BlockColorCalculatorFactory { + private static final int + AVERAGE_MIN_X = - 2, + AVERAGE_MAX_X = 2, + AVERAGE_MIN_Y = - 1, + AVERAGE_MAX_Y = 1, + AVERAGE_MIN_Z = - 2, + AVERAGE_MAX_Z = 2; + private final int[] foliageMap = new int[65536]; private final int[] grassMap = new int[65536]; @@ -133,18 +141,12 @@ public class BlockColorCalculatorFactory { public Color getWaterAverageColor(BlockNeighborhood block, Color target) { target.set(0, 0, 0, 0, true); - int x, y, z, - minX = - 2, - maxX = 2, - minY = - 1, - maxY = 1, - minZ = - 2, - maxZ = 2; + int x, y, z; Biome biome; - for (x = minX; x <= maxX; x++) { - for (y = minY; y <= maxY; y++) { - for (z = minZ; z <= maxZ; z++) { + for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) { + for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) { + for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) { biome = block.getNeighborBlock(x, y, z).getBiome(); target.add(biome.getWaterColor()); } @@ -157,18 +159,12 @@ public class BlockColorCalculatorFactory { public Color getFoliageAverageColor(BlockNeighborhood block, Color target) { target.set(0, 0, 0, 0, true); - int x, y, z, - minX = - 2, - maxX = 2, - minY = - 1, - maxY = 1, - minZ = - 2, - maxZ = 2; + int x, y, z; Biome biome; - for (y = minY; y <= maxY; y++) { - for (x = minX; x <= maxX; x++) { - for (z = minZ; z <= maxZ; z++) { + for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) { + for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) { + for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) { biome = block.getNeighborBlock(x, y, z).getBiome(); target.add(getFoliageColor(biome, tempColor)); } @@ -186,18 +182,12 @@ public class BlockColorCalculatorFactory { public Color getGrassAverageColor(BlockNeighborhood block, Color target) { target.set(0, 0, 0, 0, true); - int x, y, z, - minX = - 2, - maxX = 2, - minY = - 1, - maxY = 1, - minZ = - 2, - maxZ = 2; + int x, y, z; Biome biome; - for (y = minY; y <= maxY; y++) { - for (x = minX; x <= maxX; x++) { - for (z = minZ; z <= maxZ; z++) { + for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) { + for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) { + for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) { biome = block.getNeighborBlock(x, y, z).getBiome(); target.add(getGrassColor(biome, tempColor)); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/ResourcePath.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/ResourcePath.java index a5db0970..877628b1 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/ResourcePath.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/ResourcePath.java @@ -77,9 +77,6 @@ public class ResourcePath extends Key { if (filePath.getNameCount() < 4) throw new IllegalArgumentException("The provided filePath has less than 4 segments!"); - if (!filePath.getName(0).toString().equalsIgnoreCase("assets")) - throw new IllegalArgumentException("The provided filePath doesn't start with 'assets'!"); - String namespace = filePath.getName(1).toString(); String path = filePath.subpath(3, filePath.getNameCount()).toString().replace(filePath.getFileSystem().getSeparator(), "/"); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/adapter/ResourcesGson.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/adapter/ResourcesGson.java index 36f2ee67..5dfc46e1 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/adapter/ResourcesGson.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/adapter/ResourcesGson.java @@ -25,22 +25,21 @@ package de.bluecolored.bluemap.core.resources.adapter; import com.flowpowered.math.vector.*; +import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; import de.bluecolored.bluemap.core.resources.resourcepack.blockmodel.Face; import de.bluecolored.bluemap.core.util.Direction; import de.bluecolored.bluemap.core.util.math.Axis; import de.bluecolored.bluemap.core.util.math.Color; -import java.io.IOException; import java.util.EnumMap; public class ResourcesGson { public static final Gson INSTANCE = addAdapter(new GsonBuilder()) + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .setLenient() .create(); @@ -60,9 +59,4 @@ public class ResourcesGson { ); } - public static String nextStringOrBoolean(JsonReader in) throws IOException { - if (in.peek() == JsonToken.BOOLEAN) return Boolean.toString(in.nextBoolean()); - return in.nextString(); - } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiome.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiome.java index 2bfcdb0e..601b0700 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiome.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiome.java @@ -25,8 +25,10 @@ package de.bluecolored.bluemap.core.resources.biome.datapack; import de.bluecolored.bluemap.core.world.Biome; +import lombok.Getter; @SuppressWarnings("FieldMayBeFinal") +@Getter public class DpBiome { private DpBiomeEffects effects = new DpBiomeEffects(); @@ -44,16 +46,4 @@ public class DpBiome { ); } - public DpBiomeEffects getEffects() { - return effects; - } - - public double getTemperature() { - return temperature; - } - - public double getDownfall() { - return downfall; - } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiomeEffects.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiomeEffects.java index 9cf67379..807b80b6 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiomeEffects.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/biome/datapack/DpBiomeEffects.java @@ -26,24 +26,14 @@ package de.bluecolored.bluemap.core.resources.biome.datapack; import de.bluecolored.bluemap.core.util.math.Color; import de.bluecolored.bluemap.core.world.Biome; +import lombok.Getter; @SuppressWarnings("FieldMayBeFinal") +@Getter public class DpBiomeEffects { - private Color water_color = Biome.DEFAULT.getWaterColor(); - private Color foliage_color = Biome.DEFAULT.getOverlayFoliageColor(); - private Color grass_color = Biome.DEFAULT.getOverlayGrassColor(); - - public Color getWaterColor() { - return water_color; - } - - public Color getFoliageColor() { - return foliage_color; - } - - public Color getGrassColor() { - return grass_color; - } + private Color waterColor = Biome.DEFAULT.getWaterColor(); + private Color foliageColor = Biome.DEFAULT.getOverlayFoliageColor(); + private Color grassColor = Biome.DEFAULT.getOverlayGrassColor(); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/DataPack.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/DataPack.java new file mode 100644 index 00000000..cb60fc18 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/DataPack.java @@ -0,0 +1,117 @@ +package de.bluecolored.bluemap.core.resources.datapack; + +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.resources.ResourcePath; +import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson; +import de.bluecolored.bluemap.core.resources.datapack.dimension.DimensionTypeData; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.DimensionType; +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletionException; +import java.util.stream.Stream; + +public class DataPack { + + public static final Key DIMENSION_OVERWORLD = new Key("minecraft", "overworld"); + public static final Key DIMENSION_THE_NETHER = new Key("minecraft", "the_nether"); + public static final Key DIMENSION_THE_END = new Key("minecraft", "the_end"); + + public static final Key DIMENSION_TYPE_OVERWORLD = new Key("minecraft", "overworld"); + public static final Key DIMENSION_TYPE_OVERWORLD_CAVES = new Key("minecraft", "overworld_caves"); + public static final Key DIMENSION_TYPE_THE_NETHER = new Key("minecraft", "the_nether"); + public static final Key DIMENSION_TYPE_THE_END = new Key("minecraft", "the_end"); + + private final Map dimensionTypes = new HashMap<>(); + + @Nullable + public DimensionType getDimensionType(Key key) { + return dimensionTypes.get(key); + } + + public void load(Path root) throws InterruptedException { + Logger.global.logDebug("Loading datapack from: " + root + " ..."); + loadPath(root); + } + + private void loadPath(Path root) throws InterruptedException { + if (Thread.interrupted()) throw new InterruptedException(); + if (!Files.isDirectory(root)) { + try (FileSystem fileSystem = FileSystems.newFileSystem(root, (ClassLoader) null)) { + for (Path fsRoot : fileSystem.getRootDirectories()) { + if (!Files.isDirectory(fsRoot)) continue; + loadPath(fsRoot); + } + } catch (Exception ex) { + Logger.global.logDebug("Failed to read '" + root + "': " + ex); + } + return; + } + + list(root.resolve("data")) + .map(path -> path.resolve("dimension_type")) + .filter(Files::isDirectory) + .flatMap(DataPack::walk) + .filter(path -> path.getFileName().toString().endsWith(".json")) + .filter(Files::isRegularFile) + .forEach(file -> loadResource(root, file, () -> { + try (BufferedReader reader = Files.newBufferedReader(file)) { + return ResourcesGson.INSTANCE.fromJson(reader, DimensionTypeData.class); + } + }, dimensionTypes)); + } + + private void loadResource(Path root, Path file, Loader loader, Map resultMap) { + try { + ResourcePath resourcePath = new ResourcePath<>(root.relativize(file)); + if (resultMap.containsKey(resourcePath)) return; // don't load already present resources + + T resource = loader.load(); + if (resource == null) return; // don't load missing resources + + resourcePath.setResource(resource); + resultMap.put(resourcePath, resource); + } catch (Exception ex) { + Logger.global.logDebug("Failed to parse resource-file '" + file + "': " + ex); + } + } + + public void bake() { + dimensionTypes.putIfAbsent(DIMENSION_TYPE_OVERWORLD, DimensionType.OVERWORLD); + dimensionTypes.putIfAbsent(DIMENSION_TYPE_OVERWORLD_CAVES, DimensionType.OVERWORLD_CAVES); + dimensionTypes.putIfAbsent(DIMENSION_TYPE_THE_NETHER, DimensionType.NETHER); + dimensionTypes.putIfAbsent(DIMENSION_TYPE_THE_END, DimensionType.END); + } + + private static Stream list(Path root) { + if (!Files.isDirectory(root)) return Stream.empty(); + try { + return Files.list(root); + } catch (IOException ex) { + throw new CompletionException(ex); + } + } + + private static Stream walk(Path root) { + if (!Files.exists(root)) return Stream.empty(); + if (Files.isRegularFile(root)) return Stream.of(root); + try { + return Files.walk(root); + } catch (IOException ex) { + throw new CompletionException(ex); + } + } + + private interface Loader { + T load() throws IOException; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/dimension/DimensionTypeData.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/dimension/DimensionTypeData.java new file mode 100644 index 00000000..a7618d5a --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/dimension/DimensionTypeData.java @@ -0,0 +1,21 @@ +package de.bluecolored.bluemap.core.resources.datapack.dimension; + +import de.bluecolored.bluemap.api.debug.DebugDump; +import de.bluecolored.bluemap.core.world.DimensionType; +import lombok.*; +import lombok.experimental.Accessors; + +@Data +@DebugDump +public class DimensionTypeData implements DimensionType { + + private boolean natural; + @Accessors(fluent = true) private boolean hasSkylight; + @Accessors(fluent = true) private boolean hasCeiling; + private float ambientLight; + private int minY; + private int height; + private Long fixedTime; + private double coordinateScale; + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java index bdc13a30..e992ff5a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/ResourcePack.java @@ -183,20 +183,26 @@ public class ResourcePack { return props.build(); } - public synchronized void loadResources(Iterable roots) throws IOException { + public synchronized void loadResources(Iterable roots) throws IOException, InterruptedException { Logger.global.logInfo("Loading resources..."); for (Path root : roots) { + if (Thread.interrupted()) throw new InterruptedException(); + Logger.global.logDebug("Loading resources from: " + root + " ..."); loadResourcePath(root, this::loadResources); } Logger.global.logInfo("Loading textures..."); for (Path root : roots) { + if (Thread.interrupted()) throw new InterruptedException(); + Logger.global.logDebug("Loading textures from: " + root + " ..."); loadResourcePath(root, this::loadTextures); } + if (Thread.interrupted()) throw new InterruptedException(); + Logger.global.logInfo("Baking resources..."); bake(); @@ -204,7 +210,8 @@ public class ResourcePack { Logger.global.logInfo("Resources loaded."); } - private void loadResourcePath(Path root, PathLoader resourceLoader) throws IOException { + private void loadResourcePath(Path root, PathLoader resourceLoader) throws IOException, InterruptedException { + if (Thread.interrupted()) throw new InterruptedException(); if (!Files.isDirectory(root)) { try (FileSystem fileSystem = FileSystems.newFileSystem(root, (ClassLoader) null)) { for (Path fsRoot : fileSystem.getRootDirectories()) { @@ -299,6 +306,7 @@ public class ResourcePack { }, BlueMap.THREAD_POOL), // load biome configs + // TODO: move this to datapacks? CompletableFuture.runAsync(() -> { list(root.resolve("assets")) .map(path -> path.resolve("biomes.json")) @@ -386,7 +394,7 @@ public class ResourcePack { } } - private void bake() throws IOException { + private void bake() throws IOException, InterruptedException { // fill path maps blockStates.keySet().forEach(path -> blockStatePaths.put(path.getFormatted(), path)); @@ -398,11 +406,15 @@ public class ResourcePack { model.optimize(this); } + if (Thread.interrupted()) throw new InterruptedException(); + // apply model parents for (BlockModel model : blockModels.values()) { model.applyParent(this); } + if (Thread.interrupted()) throw new InterruptedException(); + // calculate model properties for (BlockModel model : blockModels.values()) { model.calculateProperties(this); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/blockstate/Multipart.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/blockstate/Multipart.java index bafa6829..bdcc2dc3 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/blockstate/Multipart.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/resourcepack/blockstate/Multipart.java @@ -27,9 +27,9 @@ package de.bluecolored.bluemap.core.resources.resourcepack.blockstate; import com.google.gson.Gson; import com.google.gson.annotations.JsonAdapter; import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.core.resources.AbstractTypeAdapterFactory; -import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson; import de.bluecolored.bluemap.core.world.BlockState; import org.apache.commons.lang3.StringUtils; @@ -125,7 +125,7 @@ public class Multipart { andConditions.add( BlockStateCondition.and(andArray.toArray(new BlockStateCondition[0]))); } else { - String[] values = StringUtils.split(ResourcesGson.nextStringOrBoolean(in), '|'); + String[] values = StringUtils.split(nextStringOrBoolean(in), '|'); andConditions.add(BlockStateCondition.property(name, values)); } } @@ -134,6 +134,11 @@ public class Multipart { return BlockStateCondition.and(andConditions.toArray(new BlockStateCondition[0])); } + private String nextStringOrBoolean(JsonReader in) throws IOException { + if (in.peek() == JsonToken.BOOLEAN) return Boolean.toString(in.nextBoolean()); + return in.nextString(); + } + } } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java index fa2df5cf..86063a0c 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/CompressedInputStream.java @@ -24,16 +24,16 @@ */ package de.bluecolored.bluemap.core.storage; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -public class CompressedInputStream extends InputStream { +public class CompressedInputStream extends FilterInputStream { - private final InputStream in; private final Compression compression; public CompressedInputStream(InputStream in, Compression compression) { - this.in = in; + super(in); this.compression = compression; } @@ -45,29 +45,4 @@ public class CompressedInputStream extends InputStream { return compression; } - @Override - public int read() throws IOException { - return in.read(); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - return in.read(b, off, len); - } - - @Override - public void close() throws IOException { - in.close(); - } - - @Override - public int available() throws IOException { - return in.available(); - } - - @Override - public void reset() throws IOException { - in.reset(); - } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Compression.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Compression.java index 463e7c68..6803b547 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Compression.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/Compression.java @@ -26,22 +26,25 @@ package de.bluecolored.bluemap.core.storage; import io.airlift.compress.zstd.ZstdInputStream; import io.airlift.compress.zstd.ZstdOutputStream; +import net.jpountz.lz4.LZ4FrameInputStream; +import net.jpountz.lz4.LZ4FrameOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.NoSuchElementException; -import java.util.zip.DeflaterInputStream; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import java.util.zip.InflaterInputStream; public enum Compression { NONE("none", "", out -> out, in -> in), GZIP("gzip", ".gz", GZIPOutputStream::new, GZIPInputStream::new), - DEFLATE("deflate", ".deflate", DeflaterOutputStream::new, DeflaterInputStream::new), - ZSTD("zstd", ".zst", ZstdOutputStream::new, ZstdInputStream::new); + DEFLATE("deflate", ".deflate", DeflaterOutputStream::new, InflaterInputStream::new), + ZSTD("zstd", ".zst", ZstdOutputStream::new, ZstdInputStream::new), + LZ4("lz4", ".lz4", LZ4FrameOutputStream::new, LZ4FrameInputStream::new); private final String typeId; private final String fileSuffix; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java index 7ac45c94..12035021 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/PostgreSQLStorage.java @@ -29,7 +29,7 @@ import de.bluecolored.bluemap.core.storage.CompressedInputStream; import de.bluecolored.bluemap.core.storage.Compression; import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect; import de.bluecolored.bluemap.core.storage.sql.dialect.PostgresDialect; -import de.bluecolored.bluemap.core.util.WrappedOutputStream; +import de.bluecolored.bluemap.core.util.OnCloseOutputStream; import java.io.*; import java.net.MalformedURLException; @@ -51,7 +51,7 @@ public class PostgreSQLStorage extends SQLStorage { public OutputStream writeMapTile(String mapId, int lod, Vector2i tile) throws IOException { Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE; ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - return new WrappedOutputStream(compression.compress(byteOut), () -> { + return new OnCloseOutputStream(compression.compress(byteOut), () -> { int mapFK = getMapFK(mapId); int tileCompressionFK = getMapTileCompressionFK(compression); @@ -71,7 +71,7 @@ public class PostgreSQLStorage extends SQLStorage { @Override public OutputStream writeMeta(String mapId, String name) { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - return new WrappedOutputStream(byteOut, () -> { + return new OnCloseOutputStream(byteOut, () -> { int mapFK = getMapFK(mapId); recoveringConnection(connection -> { executeUpdate(connection, this.dialect.writeMeta(), diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java index 3af604ac..7b050446 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/storage/sql/SQLStorage.java @@ -32,7 +32,7 @@ import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.storage.*; import de.bluecolored.bluemap.core.storage.sql.dialect.DialectType; import de.bluecolored.bluemap.core.storage.sql.dialect.Dialect; -import de.bluecolored.bluemap.core.util.WrappedOutputStream; +import de.bluecolored.bluemap.core.util.OnCloseOutputStream; import org.apache.commons.dbcp2.*; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool; @@ -108,7 +108,7 @@ public abstract class SQLStorage extends Storage { Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE; ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - return new WrappedOutputStream(compression.compress(byteOut), () -> { + return new OnCloseOutputStream(compression.compress(byteOut), () -> { int mapFK = getMapFK(mapId); int tileCompressionFK = getMapTileCompressionFK(compression); @@ -234,7 +234,7 @@ public abstract class SQLStorage extends Storage { @Override public OutputStream writeMeta(String mapId, String name) { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - return new WrappedOutputStream(byteOut, () -> { + return new OnCloseOutputStream(byteOut, () -> { int mapFK = getMapFK(mapId); recoveringConnection(connection -> { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java index 174c8a3e..0ce497ce 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java @@ -40,7 +40,7 @@ public class FileHelper { final Path partFile = getPartFile(file); FileHelper.createDirectories(partFile.getParent()); OutputStream os = Files.newOutputStream(partFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); - return new WrappedOutputStream(os, () -> { + return new OnCloseOutputStream(os, () -> { if (!Files.exists(partFile)) return; FileHelper.createDirectories(file.getParent()); FileHelper.move(partFile, file); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Grid.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Grid.java similarity index 99% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Grid.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Grid.java index 6d9777de..83fcd799 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Grid.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Grid.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.world; +package de.bluecolored.bluemap.core.util; import com.flowpowered.math.vector.Vector2i; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java index 9be2d1ec..a988fecf 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Key.java @@ -26,10 +26,15 @@ package de.bluecolored.bluemap.core.util; import de.bluecolored.bluemap.api.debug.DebugDump; -@DebugDump -public class Key { +import java.util.concurrent.ConcurrentHashMap; - private static final String MINECRAFT_NAMESPACE = "minecraft"; +@DebugDump +public class Key implements Keyed { + + private static final ConcurrentHashMap STRING_INTERN_POOL = new ConcurrentHashMap<>(); + + public static final String MINECRAFT_NAMESPACE = "minecraft"; + public static final String BLUEMAP_NAMESPACE = "bluemap"; private final String namespace; private final String value; @@ -44,15 +49,15 @@ public class Key { value = formatted.substring(namespaceSeparator + 1); } - this.namespace = namespace.intern(); - this.value = value.intern(); - this.formatted = (this.namespace + ":" + this.value).intern(); + this.namespace = intern(namespace); + this.value = intern(value); + this.formatted = intern(this.namespace + ":" + this.value); } public Key(String namespace, String value) { - this.namespace = namespace.intern(); - this.value = value.intern(); - this.formatted = (this.namespace + ":" + this.value).intern(); + this.namespace = intern(namespace); + this.value = intern(value); + this.formatted = intern(this.namespace + ":" + this.value); } public String getNamespace() { @@ -67,22 +72,52 @@ public class Key { return formatted; } + @Override + public Key getKey() { + return this; + } + @SuppressWarnings("StringEquality") @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key that = (Key) o; - return getFormatted() == that.getFormatted(); + return formatted == that.formatted; } @Override public int hashCode() { - return getFormatted().hashCode(); + return formatted.hashCode(); } @Override public String toString() { return formatted; } + + public static Key parse(String formatted) { + return new Key(formatted); + } + + public static Key parse(String formatted, String defaultNamespace) { + String namespace = defaultNamespace; + String value = formatted; + int namespaceSeparator = formatted.indexOf(':'); + if (namespaceSeparator > 0) { + namespace = formatted.substring(0, namespaceSeparator); + value = formatted.substring(namespaceSeparator + 1); + } + + return new Key(namespace, value); + } + + /** + * Using our own function instead of {@link String#intern()} since the ConcurrentHashMap is much faster. + */ + protected static String intern(String string) { + String interned = STRING_INTERN_POOL.putIfAbsent(string, string); + return interned != null ? interned : string; + } + } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Keyed.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Keyed.java new file mode 100644 index 00000000..f51082bf --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Keyed.java @@ -0,0 +1,7 @@ +package de.bluecolored.bluemap.core.util; + +public interface Keyed { + + Key getKey(); + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/WrappedInputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseInputStream.java similarity index 81% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/WrappedInputStream.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseInputStream.java index 46b772e0..f8fe29ad 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/WrappedInputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseInputStream.java @@ -24,34 +24,19 @@ */ package de.bluecolored.bluemap.core.util; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -public class WrappedInputStream extends InputStream { +public class OnCloseInputStream extends FilterInputStream { - private final InputStream in; private final AutoCloseable onClose; - public WrappedInputStream(InputStream in, AutoCloseable onClose) { - this.in = in; + public OnCloseInputStream(InputStream in, AutoCloseable onClose) { + super(in); this.onClose = onClose; } - @Override - public int read() throws IOException { - return in.read(); - } - - @Override - public int read(byte[] b) throws IOException { - return in.read(b); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - return in.read(b, off, len); - } - @Override public void close() throws IOException { IOException ioExcetion = null; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/WrappedOutputStream.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseOutputStream.java similarity index 78% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/WrappedOutputStream.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseOutputStream.java index 80d9f946..312496e0 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/WrappedOutputStream.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/OnCloseOutputStream.java @@ -24,39 +24,19 @@ */ package de.bluecolored.bluemap.core.util; +import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; -public class WrappedOutputStream extends OutputStream { +public class OnCloseOutputStream extends FilterOutputStream { - private final OutputStream out; private final AutoCloseable onClose; - public WrappedOutputStream(OutputStream out, AutoCloseable onClose) { - this.out = out; + public OnCloseOutputStream(OutputStream out, AutoCloseable onClose) { + super(out); this.onClose = onClose; } - @Override - public void write(int b) throws IOException { - out.write(b); - } - - @Override - public void write(byte[] b) throws IOException { - out.write(b); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - out.write(b, off, len); - } - - @Override - public void flush() throws IOException { - out.flush(); - } - @Override public void close() throws IOException { IOException ioExcetion = null; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Registry.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Registry.java new file mode 100644 index 00000000..d1805592 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/util/Registry.java @@ -0,0 +1,53 @@ +package de.bluecolored.bluemap.core.util; + +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class Registry { + + private final ConcurrentHashMap entries; + + public Registry() { + this.entries = new ConcurrentHashMap<>(); + } + + /** + * Registers a new entry, only if there is no entry with the same key registered already. + * Does nothing otherwise. + * @param entry The new entry to be added to this registry + * @return true if the entry has been added, false if there is already an entry with the same key registered + */ + public boolean register(T entry) { + Objects.requireNonNull(entry, "registry entry can not be null"); + return entries.putIfAbsent(entry.getKey(), entry) != null; + } + + /** + * Gets an entry from this registry for a key. + * @param key The key to search for + * @return The entry with the key, or null if there is no entry for this key + */ + public @Nullable T get(Key key) { + return entries.get(key); + } + + /** + * Returns an unmodifiable set of all keys this registry contains entries for + */ + public Set keys() { + return Collections.unmodifiableSet(entries.keySet()); + } + + /** + * Returns an unmodifiable collection of entries in this registry + */ + public Collection values() { + return Collections.unmodifiableCollection(entries.values()); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java index f39daaaa..3ea5e56c 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockState.java @@ -197,8 +197,8 @@ public class BlockState extends Key { private final String key, value; public Property(String key, String value) { - this.key = key.intern(); - this.value = value.intern(); + this.key = intern(key); + this.value = intern(value); } @SuppressWarnings("StringEquality") diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java index 2639b8b0..86b75df4 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java @@ -26,22 +26,50 @@ package de.bluecolored.bluemap.core.world; public interface Chunk { - boolean isGenerated(); + Chunk EMPTY_CHUNK = new Chunk() {}; - long getInhabitedTime(); + default boolean isGenerated() { + return false; + } - BlockState getBlockState(int x, int y, int z); + default boolean hasLightData() { + return false; + } - LightData getLightData(int x, int y, int z, LightData target); + default long getInhabitedTime() { + return 0; + } - String getBiome(int x, int y, int z); + default BlockState getBlockState(int x, int y, int z) { + return BlockState.AIR; + } - int getMaxY(int x, int z); + default LightData getLightData(int x, int y, int z, LightData target) { + return target.set(0, 0); + } - int getMinY(int x, int z); + default String getBiome(int x, int y, int z) { + return Biome.DEFAULT.getFormatted(); + } - int getWorldSurfaceY(int x, int z); + default int getMaxY(int x, int z) { + return 255; + } - int getOceanFloorY(int x, int z); + default int getMinY(int x, int z) { + return 0; + } + + default boolean hasWorldSurfaceHeights() { + return false; + } + + default int getWorldSurfaceY(int x, int z) { return 0; } + + default boolean hasOceanFloorHeights() { + return false; + } + + default int getOceanFloorY(int x, int z) { return 0; } } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/ChunkConsumer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/ChunkConsumer.java new file mode 100644 index 00000000..9fe0c3cf --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/ChunkConsumer.java @@ -0,0 +1,30 @@ +package de.bluecolored.bluemap.core.world; + +@FunctionalInterface +public interface ChunkConsumer { + + default boolean filter(int chunkX, int chunkZ, long lastModified) { + return true; + } + + void accept(int chunkX, int chunkZ, Chunk chunk); + + @FunctionalInterface + interface ListOnly extends ChunkConsumer { + + void accept(int chunkX, int chunkZ, long lastModified); + + @Override + default boolean filter(int chunkX, int chunkZ, long lastModified) { + accept(chunkX, chunkZ, lastModified); + return false; + } + + @Override + default void accept(int chunkX, int chunkZ, Chunk chunk) { + throw new IllegalStateException("Should never be called."); + } + + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/DimensionType.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/DimensionType.java new file mode 100644 index 00000000..1f4592d0 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/DimensionType.java @@ -0,0 +1,82 @@ +package de.bluecolored.bluemap.core.world; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.Accessors; + +public interface DimensionType { + + DimensionType OVERWORLD = new Builtin( + true, + true, + false, + 0f, + -64, + 384, + null, + 1.0 + ); + DimensionType OVERWORLD_CAVES = new Builtin( + true, + true, + true, + 0, + -64, + 384, + null, + 1.0 + ); + DimensionType NETHER = new Builtin( + false, + false, + true, + 0.1f, + 0, + 256, + 6000L, + 8.0 + ); + DimensionType END = new Builtin( + false, + false, + false, + 0, + 0, + 256, + 18000L, + 1.0 + ); + + boolean isNatural(); + + boolean hasSkylight(); + + boolean hasCeiling(); + + float getAmbientLight(); + + int getMinY(); + + int getHeight(); + + Long getFixedTime(); + + double getCoordinateScale(); + + @Getter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + class Builtin implements DimensionType { + + private final boolean natural; + @Accessors(fluent = true) private final boolean hasSkylight; + @Accessors(fluent = true) private final boolean hasCeiling; + private final float ambientLight; + private final int minY; + private final int height; + private final Long fixedTime; + private final double coordinateScale; + + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/EmptyChunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/EmptyChunk.java deleted file mode 100644 index 089dedd8..00000000 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/EmptyChunk.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of BlueMap, licensed under the MIT License (MIT). - * - * Copyright (c) Blue (Lukas Rieger) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package de.bluecolored.bluemap.core.world; - -public class EmptyChunk implements Chunk { - - public static final Chunk INSTANCE = new EmptyChunk(); - - @Override - public boolean isGenerated() { - return false; - } - - @Override - public long getInhabitedTime() { - return 0; - } - - @Override - public BlockState getBlockState(int x, int y, int z) { - return BlockState.AIR; - } - - @Override - public LightData getLightData(int x, int y, int z, LightData target) { - return target.set(0, 0); - } - - @Override - public String getBiome(int x, int y, int z) { - return Biome.DEFAULT.getFormatted(); - } - - @Override - public int getMaxY(int x, int z) { - return 255; - } - - @Override - public int getMinY(int x, int z) { - return 0; - } - - @Override - public int getWorldSurfaceY(int x, int z) { return 0; } - - @Override - public int getOceanFloorY(int x, int z) { return 0; } - -} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Region.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Region.java index def3e4fd..01da07c6 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Region.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Region.java @@ -24,34 +24,41 @@ */ package de.bluecolored.bluemap.core.world; -import com.flowpowered.math.vector.Vector2i; - import java.io.IOException; -import java.nio.file.Path; -import java.util.Collection; public interface Region { /** - * Returns a collection of all generated chunks.
- * (Be aware that the collection is not cached and recollected each time from the world-files!) + * Directly loads and returns the specified chunk.
+ * (implementations should consider overriding this method for a faster implementation) */ - default Collection listChunks(){ - return listChunks(0); + default Chunk loadChunk(int chunkX, int chunkZ) throws IOException { + class SingleChunkConsumer implements ChunkConsumer { + private Chunk foundChunk = Chunk.EMPTY_CHUNK; + + @Override + public boolean filter(int x, int z, long lastModified) { + return x == chunkX && z == chunkZ; + } + + @Override + public void accept(int chunkX, int chunkZ, Chunk chunk) { + this.foundChunk = chunk; + } + } + + SingleChunkConsumer singleChunkConsumer = new SingleChunkConsumer(); + iterateAllChunks(singleChunkConsumer); + return singleChunkConsumer.foundChunk; } /** - * Returns a collection of all chunks that have been modified at or after the specified timestamp.
- * (Be aware that the collection is not cached and recollected each time from the world-files!) + * Iterates over all chunks in this region and first calls {@link ChunkConsumer#filter(int, int, long)}.
+ * And if (any only if) that method returned true, the chunk will be loaded and {@link ChunkConsumer#accept(int, int, Chunk)} + * will be called with the loaded chunk. + * @param consumer the consumer choosing which chunks to load and accepting them + * @throws IOException if an IOException occurred trying to read the region */ - Collection listChunks(long modifiedSince); - - default Chunk loadChunk(int chunkX, int chunkZ) throws IOException { - return loadChunk(chunkX, chunkZ, false); - } - - Chunk loadChunk(int chunkX, int chunkZ, boolean ignoreMissingLightData) throws IOException; - - Path getRegionFile(); + void iterateAllChunks(ChunkConsumer consumer) throws IOException; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java index e9b650f2..66ba83c7 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/World.java @@ -26,29 +26,25 @@ package de.bluecolored.bluemap.core.world; import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3i; +import de.bluecolored.bluemap.core.util.Grid; -import java.nio.file.Path; import java.util.Collection; -import java.util.UUID; /** - * Represents a World on the Server
+ * Represents a World on the Server.
+ * This is usually one of the dimensions of a level.
*
* The implementation of this class has to be thread-save!
*/ public interface World { - Path getSaveFolder(); + String getId(); String getName(); - int getSkyLight(); - Vector3i getSpawnPoint(); - int getMaxY(int x, int z); - - int getMinY(int x, int z); + DimensionType getDimensionType(); Grid getChunkGrid(); @@ -57,7 +53,7 @@ public interface World { /** * Returns the {@link Chunk} on the specified block-position */ - Chunk getChunkAtBlock(int x, int y, int z); + Chunk getChunkAtBlock(int x, int z); /** * Returns the {@link Chunk} on the specified chunk-position @@ -75,6 +71,11 @@ public interface World { */ Collection listRegions(); + /** + * Loads all chunks from the specified region into the chunk cache (if there is a cache) + */ + void preloadRegionChunks(int x, int z); + /** * Invalidates the complete chunk cache (if there is a cache), so that every chunk has to be reloaded from disk */ diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Block.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/Block.java similarity index 91% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Block.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/Block.java index a3b9b14d..476061f9 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Block.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/Block.java @@ -22,7 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.world; +package de.bluecolored.bluemap.core.world.block; + +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.Chunk; +import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.World; public class Block> { @@ -98,22 +103,6 @@ public class Block> { return self(); } - /** - * copy with offset - */ - public T copy(Block source, int dx, int dy, int dz) { - this.world = source.world; - this.x = source.x + dx; - this.y = source.y + dy; - this.z = source.z + dz; - - this.chunk = null; - - reset(); - - return self(); - } - public World getWorld() { return world; } @@ -131,7 +120,7 @@ public class Block> { } public Chunk getChunk() { - if (chunk == null) chunk = world.getChunkAtBlock(x, y, z); + if (chunk == null) chunk = world.getChunkAtBlock(x, z); return chunk; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockNeighborhood.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/BlockNeighborhood.java similarity index 77% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockNeighborhood.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/BlockNeighborhood.java index 930f1548..24e08e42 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/BlockNeighborhood.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/BlockNeighborhood.java @@ -22,10 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.world; +package de.bluecolored.bluemap.core.world.block; import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; +import de.bluecolored.bluemap.core.world.World; public class BlockNeighborhood> extends ExtendedBlock { @@ -52,6 +53,19 @@ public class BlockNeighborhood> extends ExtendedB init(); } + @Override + public T set(int x, int y, int z) { + return copy(getBlock(x, y, z)); + } + + @Override + public T set(World world, int x, int y, int z) { + if (getWorld() == world) + return copy(getBlock(x, y, z)); + else + return super.set(world, x, y, z); + } + @Override protected void reset() { super.reset(); @@ -67,25 +81,28 @@ public class BlockNeighborhood> extends ExtendedB } public ExtendedBlock getNeighborBlock(int dx, int dy, int dz) { - int i = neighborIndex(dx, dy, dz); - if (i == thisIndex()) return this; - return neighborhood[i].set( - getWorld(), + return getBlock( getX() + dx, getY() + dy, getZ() + dz ); } + private ExtendedBlock getBlock(int x, int y, int z) { + int i = index(x, y, z); + if (i == thisIndex()) return this; + return neighborhood[i].set(getWorld(), x, y, z); + } + private int thisIndex() { - if (thisIndex == -1) thisIndex = neighborIndex(0, 0, 0); + if (thisIndex == -1) thisIndex = index(getX(), getY(), getZ()); return thisIndex; } - private int neighborIndex(int dx, int dy, int dz) { - return ((getX() + dx) & DIAMETER_MASK) * DIAMETER_SQUARED + - ((getY() + dy) & DIAMETER_MASK) * DIAMETER + - ((getZ() + dz) & DIAMETER_MASK); + private int index(int x, int y, int z) { + return (x & DIAMETER_MASK) * DIAMETER_SQUARED + + (y & DIAMETER_MASK) * DIAMETER + + (z & DIAMETER_MASK); } } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/ExtendedBlock.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/ExtendedBlock.java similarity index 73% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/ExtendedBlock.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/ExtendedBlock.java index 5e7e2009..50e1b181 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/ExtendedBlock.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/ExtendedBlock.java @@ -22,20 +22,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.world; +package de.bluecolored.bluemap.core.world.block; import de.bluecolored.bluemap.core.map.hires.RenderSettings; import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack; +import de.bluecolored.bluemap.core.world.*; import java.util.Objects; public class ExtendedBlock> extends Block { private final ResourcePack resourcePack; private final RenderSettings renderSettings; + private BlockProperties properties; private Biome biome; private boolean insideRenderBoundsCalculated, insideRenderBounds; + private boolean isCaveCalculated, isCave; public ExtendedBlock(ResourcePack resourcePack, RenderSettings renderSettings, World world, int x, int y, int z) { super(world, x, y, z); @@ -51,6 +54,22 @@ public class ExtendedBlock> extends Block { this.biome = null; this.insideRenderBoundsCalculated = false; + this.isCaveCalculated = false; + } + + public T copy(ExtendedBlock source) { + super.copy(source); + + this.properties = source.properties; + this.biome = source.biome; + + this.insideRenderBoundsCalculated = source.insideRenderBoundsCalculated; + this.insideRenderBounds = source.insideRenderBounds; + + this.isCaveCalculated = source.isCaveCalculated; + this.isCave = source.isCave; + + return self(); } @Override @@ -62,7 +81,7 @@ public class ExtendedBlock> extends Block { @Override public LightData getLightData() { LightData ld = super.getLightData(); - if (renderSettings.isRenderEdges() && !isInsideRenderBounds()) ld.set(getWorld().getSkyLight(), ld.getBlockLight()); + if (renderSettings.isRenderEdges() && !isInsideRenderBounds()) ld.set(getWorld().getDimensionType().hasSkylight() ? 16 : 0, ld.getBlockLight()); return ld; } @@ -90,6 +109,20 @@ public class ExtendedBlock> extends Block { return insideRenderBounds; } + public boolean isRemoveIfCave() { + if (!isCaveCalculated) { + isCave = getY() < renderSettings.getRemoveCavesBelowY() && + ( + !getChunk().hasOceanFloorHeights() || + getY() < getChunk().getOceanFloorY(getX(), getZ()) + + renderSettings.getCaveDetectionOceanFloor() + ); + isCaveCalculated = true; + } + + return isCave; + } + public ResourcePack getResourcePack() { return resourcePack; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAMath.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAUtil.java similarity index 69% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAMath.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAUtil.java index b10cacdd..9c1a616c 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/MCAMath.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAUtil.java @@ -22,9 +22,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.mca; +package de.bluecolored.bluemap.core.world.mca; -public class MCAMath { +import com.google.gson.reflect.TypeToken; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.mca.data.BlockStateDeserializer; +import de.bluecolored.bluemap.core.world.mca.data.KeyDeserializer; +import de.bluecolored.bluenbt.BlueNBT; + +public class MCAUtil { + + public static final BlueNBT BLUENBT = new BlueNBT(); + static { + BLUENBT.register(TypeToken.get(BlockState.class), new BlockStateDeserializer()); + BLUENBT.register(TypeToken.get(Key.class), new KeyDeserializer()); + } /** * Having a long array where each long contains as many values as fit in it without overflowing, returning the "valueIndex"-th value when each value has "bitsPerValue" bits. @@ -34,6 +47,7 @@ public class MCAMath { int longIndex = valueIndex / valuesPerLong; int bitIndex = (valueIndex % valuesPerLong) * bitsPerValue; + if (longIndex >= data.length) return 0; long value = data[longIndex] >>> bitIndex; return value & (0xFFFFFFFFFFFFFFFFL >>> -bitsPerValue); @@ -42,16 +56,18 @@ public class MCAMath { /** * Treating the long array "data" as a continuous stream of bits, returning the "valueIndex"-th value when each value has "bitsPerValue" bits. */ + @SuppressWarnings("ShiftOutOfRange") public static long getValueFromLongStream(long[] data, int valueIndex, int bitsPerValue) { int bitIndex = valueIndex * bitsPerValue; int firstLong = bitIndex >> 6; // index / 64 - int bitoffset = bitIndex & 0x3F; // Math.floorMod(index, 64) + int bitOffset = bitIndex & 0x3F; // Math.floorMod(index, 64) - long value = data[firstLong] >>> bitoffset; + if (firstLong >= data.length) return 0; + long value = data[firstLong] >>> bitOffset; - if (bitoffset > 0 && firstLong + 1 < data.length) { + if (bitOffset > 0 && firstLong + 1 < data.length) { long value2 = data[firstLong + 1]; - value2 = value2 << -bitoffset; + value2 = value2 << -bitOffset; value = value | value2; } @@ -63,12 +79,12 @@ public class MCAMath { * The value is treated as an unsigned byte. */ public static int getByteHalf(int value, boolean largeHalf) { - value = value & 0xFF; - if (largeHalf) { - value = value >> 4; - } - value = value & 0xF; - return value; + if (largeHalf) return value >> 4 & 0xF; + return value & 0xF; + } + + public static int ceilLog2(int n) { + return Integer.SIZE - Integer.numberOfLeadingZeros(n - 1); } } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java new file mode 100644 index 00000000..f79e2531 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java @@ -0,0 +1,275 @@ +package de.bluecolored.bluemap.core.world.mca; + +import com.flowpowered.math.vector.Vector2i; +import com.flowpowered.math.vector.Vector3i; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import de.bluecolored.bluemap.api.debug.DebugDump; +import de.bluecolored.bluemap.core.BlueMap; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.resources.datapack.DataPack; +import de.bluecolored.bluemap.core.util.Grid; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.util.Vector2iCache; +import de.bluecolored.bluemap.core.world.Chunk; +import de.bluecolored.bluemap.core.world.DimensionType; +import de.bluecolored.bluemap.core.world.Region; +import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.core.world.mca.chunk.ChunkLoader; +import de.bluecolored.bluemap.core.world.mca.data.LevelData; +import de.bluecolored.bluemap.core.world.mca.region.RegionType; +import lombok.Getter; +import lombok.ToString; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; + +@Getter +@ToString +@DebugDump +public class MCAWorld implements World { + + private static final Grid CHUNK_GRID = new Grid(16); + private static final Grid REGION_GRID = new Grid(32).multiply(CHUNK_GRID); + + private static final Vector2iCache VECTOR_2_I_CACHE = new Vector2iCache(); + + private final String id; + private final Path worldFolder; + private final Key dimension; + private final LevelData levelData; + private final DataPack dataPack; + + private final DimensionType dimensionType; + private final Vector3i spawnPoint; + private final Path dimensionFolder; + private final Path regionFolder; + + private final ChunkLoader chunkLoader = new ChunkLoader(); + private final LoadingCache regionCache = Caffeine.newBuilder() + .executor(BlueMap.THREAD_POOL) + .maximumSize(64) + .expireAfterWrite(10, TimeUnit.MINUTES) + .build(this::loadRegion); + private final LoadingCache chunkCache = Caffeine.newBuilder() + .executor(BlueMap.THREAD_POOL) + .maximumSize(10240) // 10 regions worth of chunks + .expireAfterWrite(10, TimeUnit.MINUTES) + .build(this::loadChunk); + + private MCAWorld(Path worldFolder, Key dimension, LevelData levelData, DataPack dataPack) { + this.id = id(worldFolder, dimension); + this.worldFolder = worldFolder; + this.dimension = dimension; + this.levelData = levelData; + this.dataPack = dataPack; + + LevelData.Dimension dimensionData = levelData.getData().getWorldGenSettings().getDimensions().get(dimension.getFormatted()); + if (dimensionData == null) { + if (DataPack.DIMENSION_OVERWORLD.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_OVERWORLD.getFormatted()); + else if (DataPack.DIMENSION_THE_NETHER.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_THE_NETHER.getFormatted()); + else if (DataPack.DIMENSION_THE_END.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_THE_END.getFormatted()); + else { + Logger.global.logWarning("The level-data does not contain any dimension with the id '" + dimension + + "', using fallback."); + dimensionData = new LevelData.Dimension(); + } + } + + DimensionType dimensionType = dataPack.getDimensionType(new Key(dimensionData.getType())); + if (dimensionType == null) { + Logger.global.logWarning("The data-pack for world '" + worldFolder + + "' does not contain any dimension-type with the id '" + dimensionData.getType() + "', using fallback."); + dimensionType = DimensionType.OVERWORLD; + } + + this.dimensionType = dimensionType; + this.spawnPoint = new Vector3i( + levelData.getData().getSpawnX(), + levelData.getData().getSpawnY(), + levelData.getData().getSpawnZ() + ); + this.dimensionFolder = resolveDimensionFolder(worldFolder, dimension); + this.regionFolder = dimensionFolder.resolve("region"); + } + + @Override + public String getName() { + return levelData.getData().getLevelName(); + } + + @Override + public Grid getChunkGrid() { + return CHUNK_GRID; + } + + @Override + public Grid getRegionGrid() { + return REGION_GRID; + } + + @Override + public Chunk getChunkAtBlock(int x, int z) { + return getChunk(x >> 4, z >> 4); + } + + @Override + public Chunk getChunk(int x, int z) { + return getChunk(VECTOR_2_I_CACHE.get(x, z)); + } + + private Chunk getChunk(Vector2i pos) { + return chunkCache.get(pos); + } + + @Override + public Region getRegion(int x, int z) { + return getRegion(VECTOR_2_I_CACHE.get(x, z)); + } + + private Region getRegion(Vector2i pos) { + return regionCache.get(pos); + } + + @Override + public Collection listRegions() { + File[] regionFiles = getRegionFolder().toFile().listFiles(); + if (regionFiles == null) return Collections.emptyList(); + + List regions = new ArrayList<>(regionFiles.length); + + for (File file : regionFiles) { + if (RegionType.forFileName(file.getName()) == null) continue; + if (file.length() <= 0) continue; + + try { + String[] filenameParts = file.getName().split("\\."); + int rX = Integer.parseInt(filenameParts[1]); + int rZ = Integer.parseInt(filenameParts[2]); + + regions.add(new Vector2i(rX, rZ)); + } catch (NumberFormatException ignore) {} + } + + return regions; + } + + @Override + public void preloadRegionChunks(int x, int z) { + try { + getRegion(x, z).iterateAllChunks((cx, cz, chunk) -> { + Vector2i chunkPos = VECTOR_2_I_CACHE.get(cx, cz); + chunkCache.put(chunkPos, chunk); + }); + } catch (IOException ex) { + Logger.global.logDebug("Unexpected exception trying to load preload region (x:" + x + ", z:" + z + "):" + ex); + } + } + + @Override + public void invalidateChunkCache() { + chunkCache.invalidateAll(); + } + + @Override + public void invalidateChunkCache(int x, int z) { + chunkCache.invalidate(VECTOR_2_I_CACHE.get(x, z)); + } + + @Override + public void cleanUpChunkCache() { + chunkCache.cleanUp(); + } + + private Region loadRegion(Vector2i regionPos) { + return loadRegion(regionPos.getX(), regionPos.getY()); + } + + private Region loadRegion(int x, int z) { + return RegionType.loadRegion(this, getRegionFolder(), x, z); + } + + private Chunk loadChunk(Vector2i chunkPos) { + return loadChunk(chunkPos.getX(), chunkPos.getY()); + } + + private Chunk loadChunk(int x, int z) { + final int tries = 3; + final int tryInterval = 1000; + + Exception loadException = null; + for (int i = 0; i < tries; i++) { + try { + return getRegion(x >> 5, z >> 5) + .loadChunk(x, z); + } catch (IOException | RuntimeException e) { + if (loadException != null) e.addSuppressed(loadException); + loadException = e; + + if (i + 1 < tries) { + try { + Thread.sleep(tryInterval); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + break; + } + } + } + } + + Logger.global.logDebug("Unexpected exception trying to load chunk (x:" + x + ", z:" + z + "):" + loadException); + return Chunk.EMPTY_CHUNK; + } + + public static MCAWorld load(Path worldFolder, Key dimension) throws IOException, InterruptedException { + // load level.dat + Path levelFile = worldFolder.resolve("level.dat"); + InputStream levelFileIn = new GZIPInputStream(new BufferedInputStream(Files.newInputStream(levelFile))); + LevelData levelData = MCAUtil.BLUENBT.read(levelFileIn, LevelData.class); + + // load datapacks + DataPack dataPack = new DataPack(); + Path dataPackFolder = worldFolder.resolve("datapacks"); + if (Files.exists(dataPackFolder)) { + List roots; + try (var stream = Files.list(dataPackFolder)) { + roots = stream + .sorted(Comparator.reverseOrder()) + .collect(Collectors.toList()); + } + for (Path root : roots) { + dataPack.load(root); + } + } + dataPack.bake(); + + // create world + return new MCAWorld(worldFolder, dimension, levelData, dataPack); + } + + public static String id(Path worldFolder, Key dimension) { + worldFolder = worldFolder.toAbsolutePath().normalize(); + + Path workingDir = Path.of("").toAbsolutePath().normalize(); + if (worldFolder.startsWith(workingDir)) + worldFolder = workingDir.relativize(worldFolder); + + return "MCA#" + worldFolder + "#" + dimension.getFormatted(); + } + + public static Path resolveDimensionFolder(Path worldFolder, Key dimension) { + if (DataPack.DIMENSION_OVERWORLD.equals(dimension)) return worldFolder; + if (DataPack.DIMENSION_THE_NETHER.equals(dimension)) return worldFolder.resolve("DIM-1"); + if (DataPack.DIMENSION_THE_END.equals(dimension)) return worldFolder.resolve("DIM1"); + return worldFolder.resolve("dimensions").resolve(dimension.getNamespace()).resolve(dimension.getValue()); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/PackedIntArrayAccess.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/PackedIntArrayAccess.java new file mode 100644 index 00000000..e32aedfa --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/PackedIntArrayAccess.java @@ -0,0 +1,120 @@ +package de.bluecolored.bluemap.core.world.mca; + +public class PackedIntArrayAccess { + + // magic constants for fast division + private static final int[] DIVISION_MAGIC = new int[]{ + // + -1, -1, 0, + Integer.MIN_VALUE, 0, 0, + 1431655765, 1431655765, 0, + Integer.MIN_VALUE, 0, 1, + 858993459, 858993459, 0, + 715827882, 715827882, 0, + 613566756, 613566756, 0, + Integer.MIN_VALUE, 0, 2, + 477218588, 477218588, 0, + 429496729, 429496729, 0, + 390451572, 390451572, 0, + 357913941, 357913941, 0, + 330382099, 330382099, 0, + 306783378, 306783378, 0, + 286331153, 286331153, 0, + Integer.MIN_VALUE, 0, 3, + 252645135, 252645135, 0, + 238609294, 238609294, 0, + 226050910, 226050910, 0, + 214748364, 214748364, 0, + 204522252, 204522252, 0, + 195225786, 195225786, 0, + 186737708, 186737708, 0, + 178956970, 178956970, 0, + 171798691, 171798691, 0, + 165191049, 165191049, 0, + 159072862, 159072862, 0, + 153391689, 153391689, 0, + 148102320, 148102320, 0, + 143165576, 143165576, 0, + 138547332, 138547332, 0, + Integer.MIN_VALUE, 0, 4, + 130150524, 130150524, 0, + 126322567, 126322567, 0, + 122713351, 122713351, 0, + 119304647, 119304647, 0, + 116080197, 116080197, 0, + 113025455, 113025455, 0, + 110127366, 110127366, 0, + 107374182, 107374182, 0, + 104755299, 104755299, 0, + 102261126, 102261126, 0, + 99882960, 99882960, 0, + 97612893, 97612893, 0, + 95443717, 95443717, 0, + 93368854, 93368854, 0, + 91382282, 91382282, 0, + 89478485, 89478485, 0, + 87652393, 87652393, 0, + 85899345, 85899345, 0, + 84215045, 84215045, 0, + 82595524, 82595524, 0, + 81037118, 81037118, 0, + 79536431, 79536431, 0, + 78090314, 78090314, 0, + 76695844, 76695844, 0, + 75350303, 75350303, 0, + 74051160, 74051160, 0, + 72796055, 72796055, 0, + 71582788, 71582788, 0, + 70409299, 70409299, 0, + 69273666, 69273666, 0, + 68174084, 68174084, 0, + Integer.MIN_VALUE, 0, 5 + // + }; + + private final int bitsPerElement; + private final long[] data; + + private final int elementsPerLong, indexShift; + private final long maxValue, indexScale, indexOffset; + + public PackedIntArrayAccess(long[] data, int elementCount) { + this(Math.max(data.length * Long.SIZE / elementCount, 1), data); + } + + public PackedIntArrayAccess(int bitsPerElement, long[] data) { + this.bitsPerElement = bitsPerElement; + this.data = data; + + this.maxValue = (1L << this.bitsPerElement) - 1L; + this.elementsPerLong = 64 / this.bitsPerElement; + + int i = 3 * (this.elementsPerLong - 1); + this.indexScale = Integer.toUnsignedLong(DIVISION_MAGIC[i]); + this.indexOffset = Integer.toUnsignedLong(DIVISION_MAGIC[i + 1]); + this.indexShift = DIVISION_MAGIC[i + 2] + 32; + } + + public int get(int i) { + int storageIndex = this.storageIndex(i); + if (storageIndex >= this.data.length) return 0; + long l = this.data[storageIndex]; + int offset = (i - storageIndex * this.elementsPerLong) * this.bitsPerElement; + return (int)(l >> offset & this.maxValue); + } + + private int storageIndex(int i) { + // this is the same as doing: floor(i / elementsPerLong) + return (int) ((long) i * this.indexScale + this.indexOffset >> this.indexShift); + } + + public int getCapacity() { + return data.length * elementsPerLong; + } + + public boolean isCorrectSize(int expectedSize) { + int capacity = getCapacity(); + return expectedSize <= capacity && expectedSize + elementsPerLong > capacity; + } + +} \ No newline at end of file diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java new file mode 100644 index 00000000..d0d1c986 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java @@ -0,0 +1,79 @@ +package de.bluecolored.bluemap.core.world.mca.chunk; + +import de.bluecolored.bluemap.core.storage.Compression; +import de.bluecolored.bluemap.core.world.mca.MCAUtil; +import de.bluecolored.bluemap.core.world.mca.region.MCARegion; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.Nullable; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.function.BiFunction; + +public class ChunkLoader { + + // sorted list of chunk-versions, loaders at the start of the list are preferred over loaders at the end + private static final List> CHUNK_VERSION_LOADERS = List.of( + new ChunkVersionLoader<>(Chunk_1_18.Data.class, Chunk_1_18::new, 2844), + new ChunkVersionLoader<>(Chunk_1_16.Data.class, Chunk_1_16::new, 2500), + new ChunkVersionLoader<>(Chunk_1_15.Data.class, Chunk_1_15::new, 2200), + new ChunkVersionLoader<>(Chunk_1_13.Data.class, Chunk_1_13::new, 0) + ); + + private ChunkVersionLoader lastUsedLoader = CHUNK_VERSION_LOADERS.get(0); + + public MCAChunk load(MCARegion region, byte[] data, int offset, int length, Compression compression) throws IOException { + InputStream in = new ByteArrayInputStream(data, offset, length); + in.mark(-1); + + // try last used version + ChunkVersionLoader usedLoader = lastUsedLoader; + MCAChunk chunk; + try (InputStream decompressedIn = new BufferedInputStream(compression.decompress(in))) { + chunk = usedLoader.load(region, decompressedIn); + } + + // check version and reload chunk if the wrong loader has been used and a better one has been found + ChunkVersionLoader actualLoader = findBestLoaderForVersion(chunk.getDataVersion()); + if (actualLoader != null && usedLoader != actualLoader) { + in.reset(); // reset read position + try (InputStream decompressedIn = new BufferedInputStream(compression.decompress(in))) { + chunk = actualLoader.load(region, decompressedIn); + } + lastUsedLoader = actualLoader; + } + + return chunk; + } + + private @Nullable ChunkVersionLoader findBestLoaderForVersion(int version) { + for (ChunkVersionLoader loader : CHUNK_VERSION_LOADERS) { + if (loader.mightSupport(version)) return loader; + } + return null; + } + + @RequiredArgsConstructor + @Getter + private static class ChunkVersionLoader { + + private final Class dataType; + private final BiFunction constructor; + private final int dataVersion; + + public MCAChunk load(MCARegion region, InputStream in) throws IOException { + D data = MCAUtil.BLUENBT.read(in, dataType); + return mightSupport(data.getDataVersion()) ? constructor.apply(region, data) : new MCAChunk(region, data) {}; + } + + public boolean mightSupport(int dataVersion) { + return dataVersion >= this.dataVersion; + } + + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java new file mode 100644 index 00000000..083b3a5c --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java @@ -0,0 +1,274 @@ +package de.bluecolored.bluemap.core.world.mca.chunk; + +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.Biome; +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.DimensionType; +import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.mca.MCAUtil; +import de.bluecolored.bluemap.core.world.mca.region.MCARegion; +import de.bluecolored.bluenbt.NBTName; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +public class Chunk_1_13 extends MCAChunk { + + private static final Level EMPTY_LEVEL = new Level(); + private static final HeightmapsData EMPTY_HEIGHTMAPS_DATA = new HeightmapsData(); + + private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); + private static final Key STATUS_FULL = new Key("minecraft", "full"); + private static final Key STATUS_FULLCHUNK = new Key("minecraft", "fullchunk"); + private static final Key STATUS_POSTPROCESSED = new Key("minecraft", "postprocessed"); + + private final boolean generated; + private final boolean hasLightData; + private final long inhabitedTime; + + private final int skyLight; + + private final boolean hasWorldSurfaceHeights; + private final long[] worldSurfaceHeights; + private final boolean hasOceanFloorHeights; + private final long[] oceanFloorHeights; + + private final Section[] sections; + private final int sectionMin, sectionMax; + + final int[] biomes; + + public Chunk_1_13(MCARegion region, Data data) { + super(region, data); + + Level level = data.level; + + this.generated = !STATUS_EMPTY.equals(level.status); + this.hasLightData = + STATUS_FULL.equals(level.status) || + STATUS_FULLCHUNK.equals(level.status) || + STATUS_POSTPROCESSED.equals(level.status); + this.inhabitedTime = level.inhabitedTime; + + DimensionType dimensionType = getRegion().getWorld().getDimensionType(); + this.skyLight = dimensionType.hasSkylight() ? 16 : 0; + + this.worldSurfaceHeights = level.heightmaps.worldSurface; + this.oceanFloorHeights = level.heightmaps.oceanFloor; + + this.hasWorldSurfaceHeights = this.worldSurfaceHeights.length >= 36; + this.hasOceanFloorHeights = this.oceanFloorHeights.length >= 36; + + this.biomes = level.biomes; + + SectionData[] sectionsData = level.sections; + if (sectionsData != null && sectionsData.length > 0) { + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + + // find section min/max y + for (SectionData sectionData : sectionsData) { + int y = sectionData.getY(); + if (min > y) min = y; + if (max < y) max = y; + } + + // load sections into ordered array + this.sections = new Section[1 + max - min]; + for (SectionData sectionData : sectionsData) { + Section section = new Section(sectionData); + int y = section.getSectionY(); + + if (min > y) min = y; + if (max < y) max = y; + + this.sections[section.sectionY - min] = section; + } + + this.sectionMin = min; + this.sectionMax = max; + } else { + this.sections = new Section[0]; + this.sectionMin = 0; + this.sectionMax = 0; + } + } + + @Override + public boolean isGenerated() { + return generated; + } + + @Override + public boolean hasLightData() { + return hasLightData; + } + + @Override + public long getInhabitedTime() { + return inhabitedTime; + } + + @Override + public BlockState getBlockState(int x, int y, int z) { + Section section = getSection(y >> 4); + if (section == null) return BlockState.AIR; + + return section.getBlockState(x, y, z); + } + + @Override + public String getBiome(int x, int y, int z) { + if (this.biomes.length < 256) return Biome.DEFAULT.getFormatted(); + + int biomeIntIndex = (z & 0xF) << 4 | x & 0xF; + return LegacyBiomes.idFor(biomes[biomeIntIndex]); + } + + @Override + public LightData getLightData(int x, int y, int z, LightData target) { + if (!hasLightData) return target.set(skyLight, 0); + + int sectionY = y >> 4; + Section section = getSection(sectionY); + if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(skyLight, 0); + + return section.getLightData(x, y, z, target); + } + + @Override + public int getMinY(int x, int z) { + return sectionMin * 16; + } + + @Override + public int getMaxY(int x, int z) { + return sectionMax * 16 + 15; + } + + @Override + public boolean hasWorldSurfaceHeights() { + return hasWorldSurfaceHeights; + } + + @Override + public int getWorldSurfaceY(int x, int z) { + return (int) MCAUtil.getValueFromLongStream( + worldSurfaceHeights, + (z & 0xF) << 4 | x & 0xF, + 9 + ); + } + + @Override + public boolean hasOceanFloorHeights() { + return hasOceanFloorHeights; + } + + @Override + public int getOceanFloorY(int x, int z) { + return (int) MCAUtil.getValueFromLongStream( + oceanFloorHeights, + (z & 0xF) << 4 | x & 0xF, + 9 + ); + } + + private @Nullable Section getSection(int y) { + y -= sectionMin; + if (y < 0 || y >= this.sections.length) return null; + return this.sections[y]; + } + + protected static class Section { + + private final int sectionY; + private final BlockState[] blockPalette; + private final long[] blocks; + private final byte[] blockLight; + private final byte[] skyLight; + + private final int bitsPerBlock; + + public Section(SectionData sectionData) { + this.sectionY = sectionData.y; + + this.blockPalette = sectionData.palette; + this.blocks = sectionData.blockStates; + + this.blockLight = sectionData.getBlockLight(); + this.skyLight = sectionData.getSkyLight(); + + this.bitsPerBlock = this.blocks.length >> 6; // available longs * 64 (bits per long) / 4096 (blocks per section) (floored result) + } + + public BlockState getBlockState(int x, int y, int z) { + if (blockPalette.length == 1) return blockPalette[0]; + if (blockPalette.length == 0) return BlockState.AIR; + + int id = (int) MCAUtil.getValueFromLongStream( + blocks, + (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF, + bitsPerBlock + ); + if (id >= blockPalette.length) { + Logger.global.noFloodWarning("palette-warning", "Got block-palette id " + id + " but palette has size of " + blockPalette.length + "! (Future occasions of this error will not be logged)"); + return BlockState.MISSING; + } + + return blockPalette[id]; + } + + public LightData getLightData(int x, int y, int z, LightData target) { + if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); + + int blockByteIndex = (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF; + int blockHalfByteIndex = blockByteIndex >> 1; + boolean largeHalf = (blockByteIndex & 0x1) != 0; + + return target.set( + this.skyLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, + this.blockLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 + ); + } + + public int getSectionY() { + return sectionY; + } + + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + public static class Data extends MCAChunk.Data { + private Level level = EMPTY_LEVEL; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + public static class Level { + private Key status = STATUS_EMPTY; + private long inhabitedTime = 0; + private HeightmapsData heightmaps = EMPTY_HEIGHTMAPS_DATA; + private SectionData @Nullable [] sections = null; + private int[] biomes = EMPTY_INT_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class HeightmapsData { + @NBTName("WORLD_SURFACE") private long[] worldSurface = EMPTY_LONG_ARRAY; + @NBTName("OCEAN_FLOOR") private long[] oceanFloor = EMPTY_LONG_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class SectionData { + private int y = 0; + private byte[] blockLight = EMPTY_BYTE_ARRAY; + private byte[] skyLight = EMPTY_BYTE_ARRAY; + private BlockState[] palette = EMPTY_BLOCKSTATE_ARRAY; + private long[] blockStates = EMPTY_LONG_ARRAY; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_15.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_15.java new file mode 100644 index 00000000..474ef263 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_15.java @@ -0,0 +1,25 @@ +package de.bluecolored.bluemap.core.world.mca.chunk; + +import de.bluecolored.bluemap.core.world.Biome; +import de.bluecolored.bluemap.core.world.mca.region.MCARegion; + +public class Chunk_1_15 extends Chunk_1_13 { + + public Chunk_1_15(MCARegion region, Data data) { + super(region, data); + } + + @Override + public String getBiome(int x, int y, int z) { + if (this.biomes.length < 16) return Biome.DEFAULT.getFormatted(); + + int biomeIntIndex = (y & 0b1100) << 2 | z & 0b1100 | (x & 0b1100) >> 2; + + // shift y up/down if not in range + if (biomeIntIndex >= biomes.length) biomeIntIndex -= (((biomeIntIndex - biomes.length) >> 4) + 1) * 16; + if (biomeIntIndex < 0) biomeIntIndex -= (biomeIntIndex >> 4) * 16; + + return LegacyBiomes.idFor(biomes[biomeIntIndex]); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_16.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_16.java new file mode 100644 index 00000000..1fbffba7 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_16.java @@ -0,0 +1,262 @@ +package de.bluecolored.bluemap.core.world.mca.chunk; + +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.Biome; +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.DimensionType; +import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.mca.MCAUtil; +import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess; +import de.bluecolored.bluemap.core.world.mca.region.MCARegion; +import de.bluecolored.bluenbt.NBTName; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +public class Chunk_1_16 extends MCAChunk { + + private static final Level EMPTY_LEVEL = new Level(); + private static final HeightmapsData EMPTY_HEIGHTMAPS_DATA = new HeightmapsData(); + + private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); + private static final Key STATUS_FULL = new Key("minecraft", "full"); + + private final boolean generated; + private final boolean hasLightData; + private final long inhabitedTime; + + private final int skyLight; + + private final boolean hasWorldSurfaceHeights; + private final PackedIntArrayAccess worldSurfaceHeights; + private final boolean hasOceanFloorHeights; + private final PackedIntArrayAccess oceanFloorHeights; + + private final Section[] sections; + private final int sectionMin, sectionMax; + + private final int[] biomes; + + public Chunk_1_16(MCARegion region, Data data) { + super(region, data); + + Level level = data.level; + + this.generated = !STATUS_EMPTY.equals(level.status); + this.hasLightData = STATUS_FULL.equals(level.status); + this.inhabitedTime = level.inhabitedTime; + + DimensionType dimensionType = getRegion().getWorld().getDimensionType(); + this.skyLight = dimensionType.hasSkylight() ? 16 : 0; + + int worldHeight = dimensionType.getHeight(); + int bitsPerHeightmapElement = MCAUtil.ceilLog2(worldHeight + 1); + + this.worldSurfaceHeights = new PackedIntArrayAccess(bitsPerHeightmapElement, level.heightmaps.worldSurface); + this.oceanFloorHeights = new PackedIntArrayAccess(bitsPerHeightmapElement, level.heightmaps.oceanFloor); + + this.hasWorldSurfaceHeights = this.worldSurfaceHeights.isCorrectSize(VALUES_PER_HEIGHTMAP); + this.hasOceanFloorHeights = this.oceanFloorHeights.isCorrectSize(VALUES_PER_HEIGHTMAP); + + this.biomes = level.biomes; + + SectionData[] sectionsData = level.sections; + if (sectionsData != null && sectionsData.length > 0) { + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + + // find section min/max y + for (SectionData sectionData : sectionsData) { + int y = sectionData.getY(); + if (min > y) min = y; + if (max < y) max = y; + } + + // load sections into ordered array + this.sections = new Section[1 + max - min]; + for (SectionData sectionData : sectionsData) { + Section section = new Section(sectionData); + int y = section.getSectionY(); + + if (min > y) min = y; + if (max < y) max = y; + + this.sections[section.sectionY - min] = section; + } + + this.sectionMin = min; + this.sectionMax = max; + } else { + this.sections = new Section[0]; + this.sectionMin = 0; + this.sectionMax = 0; + } + } + + @Override + public boolean isGenerated() { + return generated; + } + + @Override + public boolean hasLightData() { + return hasLightData; + } + + @Override + public long getInhabitedTime() { + return inhabitedTime; + } + + @Override + public BlockState getBlockState(int x, int y, int z) { + Section section = getSection(y >> 4); + if (section == null) return BlockState.AIR; + + return section.getBlockState(x, y, z); + } + + @Override + public String getBiome(int x, int y, int z) { + if (this.biomes.length < 16) return Biome.DEFAULT.getFormatted(); + + int biomeIntIndex = (y & 0b1100) << 2 | z & 0b1100 | (x & 0b1100) >> 2; + + // shift y up/down if not in range + if (biomeIntIndex >= biomes.length) biomeIntIndex -= (((biomeIntIndex - biomes.length) >> 4) + 1) * 16; + if (biomeIntIndex < 0) biomeIntIndex -= (biomeIntIndex >> 4) * 16; + + return LegacyBiomes.idFor(biomes[biomeIntIndex]); + } + + @Override + public LightData getLightData(int x, int y, int z, LightData target) { + if (!hasLightData) return target.set(skyLight, 0); + + int sectionY = y >> 4; + Section section = getSection(sectionY); + if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(skyLight, 0); + + return section.getLightData(x, y, z, target); + } + + @Override + public int getMinY(int x, int z) { + return sectionMin * 16; + } + + @Override + public int getMaxY(int x, int z) { + return sectionMax * 16 + 15; + } + + @Override + public boolean hasWorldSurfaceHeights() { + return hasWorldSurfaceHeights; + } + + @Override + public int getWorldSurfaceY(int x, int z) { + return worldSurfaceHeights.get((z & 0xF) << 4 | x & 0xF); + } + + @Override + public boolean hasOceanFloorHeights() { + return hasOceanFloorHeights; + } + + @Override + public int getOceanFloorY(int x, int z) { + return oceanFloorHeights.get((z & 0xF) << 4 | x & 0xF); + } + + private @Nullable Section getSection(int y) { + y -= sectionMin; + if (y < 0 || y >= this.sections.length) return null; + return this.sections[y]; + } + + protected static class Section { + + private final int sectionY; + private final BlockState[] blockPalette; + private final PackedIntArrayAccess blocks; + private final byte[] blockLight; + private final byte[] skyLight; + + public Section(SectionData sectionData) { + this.sectionY = sectionData.y; + + this.blockPalette = sectionData.palette; + this.blocks = new PackedIntArrayAccess(sectionData.blockStates, BLOCKS_PER_SECTION); + + this.blockLight = sectionData.getBlockLight(); + this.skyLight = sectionData.getSkyLight(); + } + + public BlockState getBlockState(int x, int y, int z) { + if (blockPalette.length == 1) return blockPalette[0]; + if (blockPalette.length == 0) return BlockState.AIR; + + int id = blocks.get((y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF); + if (id >= blockPalette.length) { + Logger.global.noFloodWarning("palette-warning", "Got block-palette id " + id + " but palette has size of " + blockPalette.length + "! (Future occasions of this error will not be logged)"); + return BlockState.MISSING; + } + + return blockPalette[id]; + } + + public LightData getLightData(int x, int y, int z, LightData target) { + if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); + + int blockByteIndex = (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF; + int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2 + boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0 + + return target.set( + this.skyLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, + this.blockLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 + ); + } + + public int getSectionY() { + return sectionY; + } + + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + public static class Data extends MCAChunk.Data { + private Level level = EMPTY_LEVEL; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + public static class Level { + private Key status = STATUS_EMPTY; + private long inhabitedTime = 0; + private HeightmapsData heightmaps = EMPTY_HEIGHTMAPS_DATA; + private SectionData @Nullable [] sections = null; + private int[] biomes = EMPTY_INT_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class HeightmapsData { + @NBTName("WORLD_SURFACE") private long[] worldSurface = EMPTY_LONG_ARRAY; + @NBTName("OCEAN_FLOOR") private long[] oceanFloor = EMPTY_LONG_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class SectionData { + private int y = 0; + private byte[] blockLight = EMPTY_BYTE_ARRAY; + private byte[] skyLight = EMPTY_BYTE_ARRAY; + private BlockState[] palette = EMPTY_BLOCKSTATE_ARRAY; + private long[] blockStates = EMPTY_LONG_ARRAY; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_18.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_18.java new file mode 100644 index 00000000..20dc9221 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_18.java @@ -0,0 +1,279 @@ +package de.bluecolored.bluemap.core.world.mca.chunk; + +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.Biome; +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.DimensionType; +import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.mca.MCAUtil; +import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess; +import de.bluecolored.bluemap.core.world.mca.region.MCARegion; +import de.bluecolored.bluenbt.NBTName; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +public class Chunk_1_18 extends MCAChunk { + + private static final BlockStatesData EMPTY_BLOCKSTATESDATA = new BlockStatesData(); + private static final BiomesData EMPTY_BIOMESDATA = new BiomesData(); + private static final HeightmapsData EMPTY_HEIGHTMAPS_DATA = new HeightmapsData(); + + private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); + private static final Key STATUS_FULL = new Key("minecraft", "full"); + + private final boolean generated; + private final boolean hasLightData; + private final long inhabitedTime; + + private final int skyLight; + private final int worldMinY; + + private final boolean hasWorldSurfaceHeights; + private final PackedIntArrayAccess worldSurfaceHeights; + private final boolean hasOceanFloorHeights; + private final PackedIntArrayAccess oceanFloorHeights; + + private final Section[] sections; + private final int sectionMin, sectionMax; + + public Chunk_1_18(MCARegion region, Data data) { + super(region, data); + + this.generated = !STATUS_EMPTY.equals(data.status); + this.hasLightData = STATUS_FULL.equals(data.status); + this.inhabitedTime = data.inhabitedTime; + + DimensionType dimensionType = getRegion().getWorld().getDimensionType(); + this.worldMinY = dimensionType.getMinY(); + this.skyLight = dimensionType.hasSkylight() ? 16 : 0; + + int worldHeight = dimensionType.getHeight(); + int bitsPerHeightmapElement = MCAUtil.ceilLog2(worldHeight + 1); + + this.worldSurfaceHeights = new PackedIntArrayAccess(bitsPerHeightmapElement, data.heightmaps.worldSurface); + this.oceanFloorHeights = new PackedIntArrayAccess(bitsPerHeightmapElement, data.heightmaps.oceanFloor); + + this.hasWorldSurfaceHeights = this.worldSurfaceHeights.isCorrectSize(VALUES_PER_HEIGHTMAP); + this.hasOceanFloorHeights = this.oceanFloorHeights.isCorrectSize(VALUES_PER_HEIGHTMAP); + + SectionData[] sectionsData = data.sections; + if (sectionsData != null && sectionsData.length > 0) { + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + + // find section min/max y + for (SectionData sectionData : sectionsData) { + int y = sectionData.getY(); + if (min > y) min = y; + if (max < y) max = y; + } + + // load sections into ordered array + this.sections = new Section[1 + max - min]; + for (SectionData sectionData : sectionsData) { + Section section = new Section(sectionData); + int y = section.getSectionY(); + + if (min > y) min = y; + if (max < y) max = y; + + this.sections[section.sectionY - min] = section; + } + + this.sectionMin = min; + this.sectionMax = max; + } else { + this.sections = new Section[0]; + this.sectionMin = 0; + this.sectionMax = 0; + } + } + + @Override + public boolean isGenerated() { + return generated; + } + + @Override + public boolean hasLightData() { + return hasLightData; + } + + @Override + public long getInhabitedTime() { + return inhabitedTime; + } + + @Override + public BlockState getBlockState(int x, int y, int z) { + Section section = getSection(y >> 4); + if (section == null) return BlockState.AIR; + + return section.getBlockState(x, y, z); + } + + @Override + public String getBiome(int x, int y, int z) { + Section section = getSection(y >> 4); + if (section == null) return Biome.DEFAULT.getFormatted(); + + return section.getBiome(x, y, z); + } + + @Override + public LightData getLightData(int x, int y, int z, LightData target) { + if (!hasLightData) return target.set(skyLight, 0); + + int sectionY = y >> 4; + Section section = getSection(sectionY); + if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(skyLight, 0); + + return section.getLightData(x, y, z, target); + } + + @Override + public int getMinY(int x, int z) { + return sectionMin * 16; + } + + @Override + public int getMaxY(int x, int z) { + return sectionMax * 16 + 15; + } + + @Override + public boolean hasWorldSurfaceHeights() { + return hasWorldSurfaceHeights; + } + + @Override + public int getWorldSurfaceY(int x, int z) { + return worldSurfaceHeights.get((z & 0xF) << 4 | x & 0xF) + worldMinY; + } + + @Override + public boolean hasOceanFloorHeights() { + return hasOceanFloorHeights; + } + + @Override + public int getOceanFloorY(int x, int z) { + return oceanFloorHeights.get((z & 0xF) << 4 | x & 0xF) + worldMinY; + } + + private @Nullable Section getSection(int y) { + y -= sectionMin; + if (y < 0 || y >= this.sections.length) return null; + return this.sections[y]; + } + + protected static class Section { + + private final int sectionY; + private final BlockState[] blockPalette; + private final String[] biomePalette; + private final PackedIntArrayAccess blocks; + private final PackedIntArrayAccess biomes; + private final byte[] blockLight; + private final byte[] skyLight; + + public Section(SectionData sectionData) { + this.sectionY = sectionData.y; + + this.blockPalette = sectionData.blockStates.palette; + this.biomePalette = sectionData.biomes.palette; + + this.blocks = new PackedIntArrayAccess(sectionData.blockStates.data, BLOCKS_PER_SECTION); + this.biomes = new PackedIntArrayAccess(sectionData.biomes.data, BIOMES_PER_SECTION); + + this.blockLight = sectionData.blockLight; + this.skyLight = sectionData.skyLight; + } + + public BlockState getBlockState(int x, int y, int z) { + if (blockPalette.length == 1) return blockPalette[0]; + if (blockPalette.length == 0) return BlockState.AIR; + + int id = blocks.get((y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF); + if (id >= blockPalette.length) { + Logger.global.noFloodWarning("palette-warning", "Got block-palette id " + id + " but palette has size of " + blockPalette.length + "! (Future occasions of this error will not be logged)"); + return BlockState.MISSING; + } + + return blockPalette[id]; + } + + public String getBiome(int x, int y, int z) { + if (biomePalette.length == 1) return biomePalette[0]; + if (biomePalette.length == 0) return Biome.DEFAULT.getValue(); + + int id = biomes.get((y & 0b1100) << 2 | z & 0b1100 | (x & 0b1100) >> 2); + if (id >= biomePalette.length) { + Logger.global.noFloodWarning("biome-palette-warning", "Got biome-palette id " + id + " but palette has size of " + biomePalette.length + "! (Future occasions of this error will not be logged)"); + return Biome.DEFAULT.getValue(); + } + + return biomePalette[id]; + } + + public LightData getLightData(int x, int y, int z, LightData target) { + if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); + + int blockByteIndex = (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF; + int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2 + boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0 + + return target.set( + this.skyLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, + this.blockLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 + ); + } + + public int getSectionY() { + return sectionY; + } + + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + public static class Data extends MCAChunk.Data { + private Key status = STATUS_EMPTY; + private long inhabitedTime = 0; + private HeightmapsData heightmaps = EMPTY_HEIGHTMAPS_DATA; + private SectionData @Nullable [] sections = null; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class HeightmapsData { + @NBTName("WORLD_SURFACE") private long[] worldSurface = EMPTY_LONG_ARRAY; + @NBTName("OCEAN_FLOOR") private long[] oceanFloor = EMPTY_LONG_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class SectionData { + private int y = 0; + private byte[] blockLight = EMPTY_BYTE_ARRAY; + private byte[] skyLight = EMPTY_BYTE_ARRAY; + @NBTName("block_states") private BlockStatesData blockStates = EMPTY_BLOCKSTATESDATA; + private BiomesData biomes = EMPTY_BIOMESDATA; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class BlockStatesData { + private BlockState[] palette = EMPTY_BLOCKSTATE_ARRAY; + private long[] data = EMPTY_LONG_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class BiomesData { + private String[] palette = EMPTY_STRING_ARRAY; + private long[] data = EMPTY_LONG_ARRAY; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/LegacyBiomes.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/LegacyBiomes.java similarity index 99% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/LegacyBiomes.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/LegacyBiomes.java index 35330aa5..a998ee1a 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/LegacyBiomes.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/LegacyBiomes.java @@ -22,7 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.mca; +package de.bluecolored.bluemap.core.world.mca.chunk; import java.util.Arrays; diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/MCAChunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/MCAChunk.java new file mode 100644 index 00000000..77274236 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/MCAChunk.java @@ -0,0 +1,37 @@ +package de.bluecolored.bluemap.core.world.mca.chunk; + +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.Chunk; +import de.bluecolored.bluemap.core.world.mca.region.MCARegion; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public abstract class MCAChunk implements Chunk { + + protected static final int BLOCKS_PER_SECTION = 16 * 16 * 16; + protected static final int BIOMES_PER_SECTION = 4 * 4 * 4; + protected static final int VALUES_PER_HEIGHTMAP = 16 * 16; + + protected static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + protected static final int[] EMPTY_INT_ARRAY = new int[0]; + protected static final long[] EMPTY_LONG_ARRAY = new long[0]; + protected static final String[] EMPTY_STRING_ARRAY = new String[0]; + protected static final BlockState[] EMPTY_BLOCKSTATE_ARRAY = new BlockState[0]; + + private final MCARegion region; + private final int dataVersion; + + public MCAChunk(MCARegion region, Data chunkData) { + this.region = region; + this.dataVersion = chunkData.getDataVersion(); + } + + @SuppressWarnings("FieldMayBeFinal") + @Getter + public static class Data { + private int dataVersion = 0; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/BlockStateDeserializer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/BlockStateDeserializer.java new file mode 100644 index 00000000..ae0e39a5 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/BlockStateDeserializer.java @@ -0,0 +1,40 @@ +package de.bluecolored.bluemap.core.world.mca.data; + +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluenbt.NBTReader; +import de.bluecolored.bluenbt.TypeDeserializer; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +public class BlockStateDeserializer implements TypeDeserializer { + + @Override + public BlockState read(NBTReader reader) throws IOException { + reader.beginCompound(); + + String id = null; + Map properties = null; + + while (reader.hasNext()) { + switch (reader.name()) { + case "Name" : id = reader.nextString(); break; + case "Properties" : + properties = new LinkedHashMap<>(); + reader.beginCompound(); + while (reader.hasNext()) + properties.put(reader.name(), reader.nextString()); + reader.endCompound(); + break; + default : reader.skip(); + } + } + + reader.endCompound(); + + if (id == null) throw new IOException("Invalid BlockState, Name is missing!"); + return properties == null ? new BlockState(id) : new BlockState(id, properties); + } + +} \ No newline at end of file diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/KeyDeserializer.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/KeyDeserializer.java new file mode 100644 index 00000000..951b7b49 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/KeyDeserializer.java @@ -0,0 +1,16 @@ +package de.bluecolored.bluemap.core.world.mca.data; + +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluenbt.NBTReader; +import de.bluecolored.bluenbt.TypeDeserializer; + +import java.io.IOException; + +public class KeyDeserializer implements TypeDeserializer { + + @Override + public Key read(NBTReader reader) throws IOException { + return new Key(reader.nextString()); + } + +} \ No newline at end of file diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/LevelData.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/LevelData.java new file mode 100644 index 00000000..7f89e344 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/LevelData.java @@ -0,0 +1,40 @@ +package de.bluecolored.bluemap.core.world.mca.data; + +import de.bluecolored.bluemap.api.debug.DebugDump; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; + +@Getter +@SuppressWarnings("FieldMayBeFinal") +@DebugDump +public class LevelData { + + private Data data = new Data(); + + @Getter + @DebugDump + public static class Data { + private String levelName = "world"; + private int spawnX = 0, spawnY = 0, spawnZ = 0; + private WGSettings worldGenSettings = new WGSettings(); + } + + @Getter + @DebugDump + public static class WGSettings { + private Map dimensions = new HashMap<>(); + } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + @DebugDump + public static class Dimension { + private String type = "minecraft:overworld"; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/region/MCARegion.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/region/MCARegion.java new file mode 100644 index 00000000..70afac78 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/region/MCARegion.java @@ -0,0 +1,197 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package de.bluecolored.bluemap.core.world.mca.region; + +import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.core.storage.Compression; +import de.bluecolored.bluemap.core.world.Chunk; +import de.bluecolored.bluemap.core.world.ChunkConsumer; +import de.bluecolored.bluemap.core.world.Region; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; +import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunk; +import lombok.Getter; +import lombok.ToString; + +import java.io.EOFException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +@Getter +@ToString +public class MCARegion implements Region { + + public static final String FILE_SUFFIX = ".mca"; + + private final MCAWorld world; + private final Path regionFile; + private final Vector2i regionPos; + + public MCARegion(MCAWorld world, Path regionFile) throws IllegalArgumentException { + this.world = world; + this.regionFile = regionFile; + + String[] filenameParts = regionFile.getFileName().toString().split("\\."); + int rX = Integer.parseInt(filenameParts[1]); + int rZ = Integer.parseInt(filenameParts[2]); + + this.regionPos = new Vector2i(rX, rZ); + } + + public MCARegion(MCAWorld world, Vector2i regionPos) throws IllegalArgumentException { + this.world = world; + this.regionPos = regionPos; + this.regionFile = world.getRegionFolder().resolve(getRegionFileName(regionPos.getX(), regionPos.getY())); + } + + @Override + public Chunk loadChunk(int chunkX, int chunkZ) throws IOException { + if (Files.notExists(regionFile)) return Chunk.EMPTY_CHUNK; + + long fileLength = Files.size(regionFile); + if (fileLength == 0) return Chunk.EMPTY_CHUNK; + + try (FileChannel channel = FileChannel.open(regionFile, StandardOpenOption.READ)) { + int xzChunk = (chunkZ & 0b11111) << 5 | (chunkX & 0b11111); + + byte[] header = new byte[4]; + channel.position(xzChunk * 4); + readFully(channel, header, 0, 4); + + int offset = header[0] << 16; + offset |= (header[1] & 0xFF) << 8; + offset |= header[2] & 0xFF; + offset *= 4096; + int size = header[3] * 4096; + + if (size == 0) return Chunk.EMPTY_CHUNK; + + byte[] chunkDataBuffer = new byte[size]; + + channel.position(offset); + readFully(channel, chunkDataBuffer, 0, size); + + return loadChunk(chunkDataBuffer, size); + } + } + + @Override + public void iterateAllChunks(ChunkConsumer consumer) throws IOException { + if (Files.notExists(regionFile)) return; + + long fileLength = Files.size(regionFile); + if (fileLength == 0) return; + + int chunkStartX = regionPos.getX() * 32; + int chunkStartZ = regionPos.getY() * 32; + + try (FileChannel channel = FileChannel.open(regionFile, StandardOpenOption.READ)) { + byte[] header = new byte[1024 * 8]; + byte[] chunkDataBuffer = null; + + // read the header + readFully(channel, header, 0, header.length); + + // iterate over all chunks + for (int x = 0; x < 32; x++) { + for (int z = 0; z < 32; z++) { + int xzChunk = z * 32 + x; + + int size = header[xzChunk * 4 + 3] * 4096; + if (size == 0) continue; + + int chunkX = chunkStartX + x; + int chunkZ = chunkStartZ + z; + + int i = xzChunk * 4 + 4096; + int timestamp = header[i++] << 24; + timestamp |= (header[i++] & 0xFF) << 16; + timestamp |= (header[i++] & 0xFF) << 8; + timestamp |= header[i] & 0xFF; + + // load chunk only if consumers filter returns true + if (consumer.filter(chunkX, chunkZ, timestamp)) { + i = xzChunk * 4; + int offset = header[i++] << 16; + offset |= (header[i++] & 0xFF) << 8; + offset |= header[i] & 0xFF; + offset *= 4096; + + if (chunkDataBuffer == null || chunkDataBuffer.length < size) + chunkDataBuffer = new byte[size]; + + channel.position(offset); + readFully(channel, chunkDataBuffer, 0, size); + + MCAChunk chunk = loadChunk(chunkDataBuffer, size); + consumer.accept(chunkX, chunkZ, chunk); + } + } + } + } + } + + private MCAChunk loadChunk(byte[] data, int size) throws IOException { + int compressionTypeId = data[4]; + Compression compression; + switch (compressionTypeId) { + case 0 : + case 3 : compression = Compression.NONE; break; + case 1 : compression = Compression.GZIP; break; + case 2 : compression = Compression.DEFLATE; break; + case 4 : compression = Compression.LZ4; break; + default: throw new IOException("Unknown chunk compression-id: " + compressionTypeId); + } + + return world.getChunkLoader().load(this, data, 5, size - 5, compression); + } + + public static String getRegionFileName(int regionX, int regionZ) { + return "r." + regionX + "." + regionZ + FILE_SUFFIX; + } + + @SuppressWarnings("SameParameterValue") + private static void readFully(ReadableByteChannel src, byte[] dst, int off, int len) throws IOException { + readFully(src, ByteBuffer.wrap(dst), off, len); + } + + private static void readFully(ReadableByteChannel src, ByteBuffer bb, int off, int len) throws IOException { + int limit = off + len; + if (limit > bb.capacity()) throw new IllegalArgumentException("buffer too small"); + + bb.limit(limit); + bb.position(off); + + do { + int read = src.read(bb); + if (read < 0) throw new EOFException(); + } while (bb.remaining() > 0); + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/RegionType.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/region/RegionType.java similarity index 94% rename from BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/RegionType.java rename to BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/region/RegionType.java index 63896faa..434da5f0 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/mca/region/RegionType.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/region/RegionType.java @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package de.bluecolored.bluemap.core.mca.region; +package de.bluecolored.bluemap.core.world.mca.region; -import de.bluecolored.bluemap.core.mca.MCAWorld; import de.bluecolored.bluemap.core.world.Region; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,8 +34,8 @@ import java.nio.file.Path; public enum RegionType { - MCA (MCARegion::new, MCARegion.FILE_SUFFIX, MCARegion::getRegionFileName), - LINEAR (LinearRegion::new, LinearRegion.FILE_SUFFIX, LinearRegion::getRegionFileName); + MCA (MCARegion::new, MCARegion.FILE_SUFFIX, MCARegion::getRegionFileName); + //LINEAR (LinearRegion::new, LinearRegion.FILE_SUFFIX, LinearRegion::getRegionFileName); // we do this to improve performance, as calling values() creates a new array each time private final static RegionType[] VALUES = values(); diff --git a/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/GridTest.java b/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/GridTest.java index 4eed1307..85eb1524 100644 --- a/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/GridTest.java +++ b/BlueMapCore/src/test/java/de/bluecolored/bluemap/core/world/GridTest.java @@ -25,6 +25,7 @@ package de.bluecolored.bluemap.core.world; import com.flowpowered.math.vector.Vector2i; +import de.bluecolored.bluemap.core.util.Grid; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java b/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java index 255fd03d..0599b17f 100644 --- a/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java +++ b/implementations/cli/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java @@ -24,10 +24,10 @@ */ package de.bluecolored.bluemap.cli; -import de.bluecolored.bluemap.common.BlueMapConfigProvider; +import de.bluecolored.bluemap.common.BlueMapConfiguration; import de.bluecolored.bluemap.common.BlueMapService; import de.bluecolored.bluemap.common.MissingResourcesException; -import de.bluecolored.bluemap.common.config.BlueMapConfigs; +import de.bluecolored.bluemap.common.config.BlueMapConfigManager; import de.bluecolored.bluemap.common.config.ConfigurationException; import de.bluecolored.bluemap.common.config.CoreConfig; import de.bluecolored.bluemap.common.config.WebserverConfig; @@ -35,10 +35,6 @@ import de.bluecolored.bluemap.common.plugin.RegionFileWatchService; import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask; import de.bluecolored.bluemap.common.rendermanager.RenderManager; import de.bluecolored.bluemap.common.rendermanager.RenderTask; -import de.bluecolored.bluemap.common.serverinterface.Player; -import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; -import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.common.web.*; import de.bluecolored.bluemap.common.web.http.HttpRequestHandler; import de.bluecolored.bluemap.common.web.http.HttpServer; @@ -67,22 +63,22 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.regex.Pattern; -public class BlueMapCLI implements ServerInterface { +public class BlueMapCLI { private MinecraftVersion minecraftVersion = MinecraftVersion.LATEST_SUPPORTED; - private Path configFolder; + private Path configFolder = Path.of("config"); public void renderMaps(BlueMapService blueMap, boolean watch, boolean forceRender, boolean forceGenerateWebapp, @Nullable String mapsToRender) throws ConfigurationException, IOException, InterruptedException { //metrics report - if (blueMap.getConfigs().getCoreConfig().isMetrics()) Metrics.sendReportAsync("cli"); + if (blueMap.getConfig().getCoreConfig().isMetrics()) Metrics.sendReportAsync("cli"); - if (blueMap.getConfigs().getWebappConfig().isEnabled()) + if (blueMap.getConfig().getWebappConfig().isEnabled()) blueMap.createOrUpdateWebApp(forceGenerateWebapp); //try load resources - blueMap.getResourcePack(); + blueMap.getOrLoadResourcePack(); //create renderManager RenderManager renderManager = new RenderManager(); @@ -93,7 +89,7 @@ public class BlueMapCLI implements ServerInterface { Set mapsToRenderSet = Set.of(mapsToRender.split(",")); mapFilter = mapsToRenderSet::contains; } - Map maps = blueMap.getMaps(mapFilter); + Map maps = blueMap.getOrLoadMaps(mapFilter); //watcher List regionFileWatchServices = new ArrayList<>(); @@ -121,7 +117,7 @@ public class BlueMapCLI implements ServerInterface { Logger.global.logInfo("Start updating " + maps.size() + " maps (" + totalRegions + " regions, ~" + totalRegions * 1024L + " chunks)..."); // start rendering - renderManager.start(blueMap.getConfigs().getCoreConfig().resolveRenderThreadCount()); + renderManager.start(blueMap.getConfig().getCoreConfig().resolveRenderThreadCount()); Timer timer = new Timer("BlueMap-CLI-Timer", true); TimerTask updateInfoTask = new TimerTask() { @@ -195,7 +191,7 @@ public class BlueMapCLI implements ServerInterface { public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOException, ConfigurationException, InterruptedException { Logger.global.logInfo("Starting webserver ..."); - WebserverConfig config = blueMap.getConfigs().getWebserverConfig(); + WebserverConfig config = blueMap.getConfig().getWebserverConfig(); FileHelper.createDirectories(config.getWebroot()); RoutingRequestHandler routingRequestHandler = new RoutingRequestHandler(); @@ -204,8 +200,8 @@ public class BlueMapCLI implements ServerInterface { routingRequestHandler.register(".*", new FileRequestHandler(config.getWebroot())); // map route - for (var mapConfigEntry : blueMap.getConfigs().getMapConfigs().entrySet()) { - Storage storage = blueMap.getStorage(mapConfigEntry.getValue().getStorage()); + for (var mapConfigEntry : blueMap.getConfig().getMapConfigs().entrySet()) { + Storage storage = blueMap.getOrLoadStorage(mapConfigEntry.getValue().getStorage()); routingRequestHandler.register( "maps/" + Pattern.quote(mapConfigEntry.getKey()) + "/(.*)", @@ -252,47 +248,6 @@ public class BlueMapCLI implements ServerInterface { } } - @Override - public MinecraftVersion getMinecraftVersion() { - return minecraftVersion; - } - - @Override - public void registerListener(ServerEventListener listener) {} - - @Override - public void unregisterAllListeners() {} - - @Override - public Optional getWorld(Path worldFolder) { - return Optional.empty(); - } - - @Override - public Collection getLoadedWorlds() { - return Collections.emptyList(); - } - - @Override - public Path getConfigFolder() { - return configFolder; - } - - @Override - public Optional getModsFolder() { - return Optional.empty(); - } - - @Override - public Collection getOnlinePlayers() { - return Collections.emptyList(); - } - - @Override - public Optional getPlayer(UUID uuid) { - return Optional.empty(); - } - public static void main(String[] args) { CommandLineParser parser = new DefaultParser(); @@ -324,7 +279,6 @@ public class BlueMapCLI implements ServerInterface { } //config folder - cli.configFolder = Path.of("config"); if (cmd.hasOption("c")) { cli.configFolder = Path.of(cmd.getOptionValue("c")); FileHelper.createDirectories(cli.configFolder); @@ -342,7 +296,13 @@ public class BlueMapCLI implements ServerInterface { } } - BlueMapConfigs configs = new BlueMapConfigs(cli, Path.of("data"), Path.of("web"), false); + BlueMapConfigManager configs = BlueMapConfigManager.builder() + .minecraftVersion(cli.minecraftVersion) + .configRoot(cli.configFolder) + .usePluginConfig(false) + .defaultDataFolder(Path.of("data")) + .defaultWebroot(Path.of("web")) + .build(); //apply new file-logger config CoreConfig coreConfig = configs.getCoreConfig(); @@ -354,7 +314,7 @@ public class BlueMapCLI implements ServerInterface { )); } - blueMap = new BlueMapService(cli, configs); + blueMap = new BlueMapService(configs); boolean noActions = true; if (cmd.hasOption("w")) { @@ -399,9 +359,9 @@ public class BlueMapCLI implements ServerInterface { Logger.global.logWarning("BlueMap is missing important resources!"); Logger.global.logWarning("You must accept the required file download in order for BlueMap to work!"); if (blueMap != null) { - BlueMapConfigProvider configProvider = blueMap.getConfigs(); - if (configProvider instanceof BlueMapConfigs) { - Logger.global.logWarning("Please check: " + ((BlueMapConfigs) configProvider).getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize()); + BlueMapConfiguration configProvider = blueMap.getConfig(); + if (configProvider instanceof BlueMapConfigManager) { + Logger.global.logWarning("Please check: " + ((BlueMapConfigManager) configProvider).getConfigManager().findConfigPath(Path.of("core")).toAbsolutePath().normalize()); } } System.exit(2); @@ -479,23 +439,7 @@ public class BlueMapCLI implements ServerInterface { private static void printHelp() { HelpFormatter formatter = new HelpFormatter(); - String filename = "bluemap-cli.jar"; - try { - File file = new File(BlueMapCLI.class.getProtectionDomain() - .getCodeSource() - .getLocation() - .getPath()); - - if (file.isFile()) { - try { - filename = "." + File.separator + new File("").getCanonicalFile().toPath().relativize(file.toPath()); - } catch (IllegalArgumentException ex) { - filename = file.getAbsolutePath(); - } - } - } catch (IOException ignore) {} - - String command = "java -jar " + filename; + String command = getCliCommand(); @SuppressWarnings("StringBufferReplaceableByString") StringBuilder footer = new StringBuilder(); @@ -512,6 +456,25 @@ public class BlueMapCLI implements ServerInterface { formatter.printHelp(command + " [options]", "\nOptions:", createOptions(), "\n" + footer); } + private static String getCliCommand() { + String filename = "bluemap-cli.jar"; + try { + File file = new File(BlueMapCLI.class.getProtectionDomain() + .getCodeSource() + .getLocation() + .getPath()); + + if (file.isFile()) { + try { + filename = "." + File.separator + new File("").getCanonicalFile().toPath().relativize(file.toPath()); + } catch (IllegalArgumentException ex) { + filename = file.getAbsolutePath(); + } + } + } catch (IOException ignore) {} + return "java -jar " + filename; + } + private static void printVersion() { System.out.printf("%s\n%s\n", BlueMap.VERSION, BlueMap.GIT_HASH); } diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java index 96497a24..be2e1ae9 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java @@ -26,14 +26,14 @@ package de.bluecolored.bluemap.fabric; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.math.Vec3d; -import java.io.IOException; import java.util.Optional; public class FabricCommandSource implements CommandSource { @@ -75,13 +75,8 @@ public class FabricCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index 7513c125..98ad8c55 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -29,17 +29,17 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.logger.Logger; -import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; @@ -51,7 +51,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class FabricMod implements ModInitializer, ServerInterface { +public class FabricMod implements ModInitializer, Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -141,7 +141,7 @@ public class FabricMod implements ModInitializer, ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -150,9 +150,7 @@ public class FabricMod implements ModInitializer, ServerInterface { } @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { Identifier identifier = Identifier.tryParse((String) world); @@ -170,12 +168,12 @@ public class FabricMod implements ModInitializer, ServerInterface { } if (world instanceof net.minecraft.server.world.ServerWorld) - return Optional.of(getWorld((net.minecraft.server.world.ServerWorld) world)); + return Optional.of(getServerWorld((net.minecraft.server.world.ServerWorld) world)); return Optional.empty(); } - public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) { + public ServerWorld getServerWorld(net.minecraft.server.world.ServerWorld serverWorld) { return worlds.get(serverWorld); } @@ -220,11 +218,6 @@ public class FabricMod implements ModInitializer, ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index d94be1e8..36737389 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.server.MinecraftServer; @@ -36,7 +37,6 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.LightType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -54,12 +54,11 @@ public class FabricPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -84,7 +83,7 @@ public class FabricPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -108,11 +107,6 @@ public class FabricPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -133,16 +127,10 @@ public class FabricPlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -151,7 +139,6 @@ public class FabricPlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); @@ -161,12 +148,7 @@ public class FabricPlayer implements Player { this.skyLight = player.getServerWorld().getLightingProvider().get(LightType.SKY).getLightLevel(player.getBlockPos()); this.blockLight = player.getServerWorld().getLightingProvider().get(LightType.BLOCK).getLightLevel(player.getBlockPos()); - try { - var world = mod.getWorld(player.getServerWorld()); - this.world = mod.getPluginInstance().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getServerWorld()); } } diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java index 9335ff05..6a6de8c5 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java @@ -24,9 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; -import net.minecraft.world.dimension.DimensionType; +import de.bluecolored.bluemap.core.resources.datapack.DataPack; +import de.bluecolored.bluemap.core.util.Key; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; import java.io.IOException; import java.lang.ref.WeakReference; @@ -38,25 +40,18 @@ import java.util.concurrent.ExecutionException; public class FabricWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public FabricWorld(net.minecraft.server.world.ServerWorld delegate) { this.delegate = new WeakReference<>(delegate); - this.saveFolder = delegate.getDimension().getType() - .getSaveDirectory(delegate.getSaveHandler().getWorldDir()).toPath() - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getSaveHandler().getWorldDir().toPath(); - @Override - public Dimension getDimension() { - net.minecraft.server.world.ServerWorld world = delegate.get(); - if (world != null) { - if (world.getDimension().getType().equals(DimensionType.THE_NETHER)) return Dimension.NETHER; - if (world.getDimension().getType().equals(DimensionType.THE_END)) return Dimension.END; - if (world.getDimension().getType().equals(DimensionType.OVERWORLD)) return Dimension.OVERWORLD; - } - return ServerWorld.super.getDimension(); + Identifier id = Registry.DIMENSION_TYPE.getId(delegate.getDimension().getType()); + this.dimension = id != null ? + new Key(id.getNamespace(), id.getPath()) : + DataPack.DIMENSION_OVERWORLD; } @Override @@ -87,8 +82,29 @@ public class FabricWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FabricWorld that = (FabricWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java index 3b868cae..9e60f395 100644 --- a/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java +++ b/implementations/fabric-1.15.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.core.logger.AbstractLogger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java index 96497a24..be2e1ae9 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java @@ -26,14 +26,14 @@ package de.bluecolored.bluemap.fabric; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.math.Vec3d; -import java.io.IOException; import java.util.Optional; public class FabricCommandSource implements CommandSource { @@ -75,13 +75,8 @@ public class FabricCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index 5ade2a8a..94fc9b67 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -35,8 +35,8 @@ import java.util.UUID; public class FabricEventForwarder { - private FabricMod mod; - private Collection eventListeners; + private final FabricMod mod; + private final Collection eventListeners; public FabricEventForwarder(FabricMod mod) { this.mod = mod; diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index 85c72445..0a6a2628 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -29,17 +29,17 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; import de.bluecolored.bluemap.core.logger.Logger; -import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; @@ -53,7 +53,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class FabricMod implements ModInitializer, ServerInterface { +public class FabricMod implements ModInitializer, Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -143,7 +143,7 @@ public class FabricMod implements ModInitializer, ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -153,9 +153,7 @@ public class FabricMod implements ModInitializer, ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { Identifier identifier = Identifier.tryParse((String) world); @@ -169,12 +167,12 @@ public class FabricMod implements ModInitializer, ServerInterface { } if (world instanceof net.minecraft.server.world.ServerWorld) - return Optional.of(getWorld((net.minecraft.server.world.ServerWorld) world)); + return Optional.of(getServerWorld((net.minecraft.server.world.ServerWorld) world)); return Optional.empty(); } - public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) { + public ServerWorld getServerWorld(net.minecraft.server.world.ServerWorld serverWorld) { return worlds.get(serverWorld); } @@ -219,11 +217,6 @@ public class FabricMod implements ModInitializer, ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index d94be1e8..36737389 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.server.MinecraftServer; @@ -36,7 +37,6 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.LightType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -54,12 +54,11 @@ public class FabricPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -84,7 +83,7 @@ public class FabricPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -108,11 +107,6 @@ public class FabricPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -133,16 +127,10 @@ public class FabricPlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -151,7 +139,6 @@ public class FabricPlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); @@ -161,12 +148,7 @@ public class FabricPlayer implements Player { this.skyLight = player.getServerWorld().getLightingProvider().get(LightType.SKY).getLightLevel(player.getBlockPos()); this.blockLight = player.getServerWorld().getLightingProvider().get(LightType.BLOCK).getLightLevel(player.getBlockPos()); - try { - var world = mod.getWorld(player.getServerWorld()); - this.world = mod.getPluginInstance().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getServerWorld()); } } diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java index e248fe97..ff4468d9 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; import net.minecraft.util.WorldSavePath; -import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import java.io.IOException; import java.lang.ref.WeakReference; @@ -41,27 +40,18 @@ import java.util.concurrent.ExecutionException; public class FabricWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public FabricWorld(net.minecraft.server.world.ServerWorld delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getRunDirectory().toPath().resolve(server.getSavePath(WorldSavePath.ROOT)); - this.saveFolder = DimensionType.getSaveDirectory(delegate.getRegistryKey(), worldFolder.toFile()).toPath() - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getRunDirectory().toPath() + .resolve(server.getSavePath(WorldSavePath.ROOT)); - @Override - public Dimension getDimension() { - net.minecraft.server.world.ServerWorld world = delegate.get(); - if (world != null) { - if (world.getRegistryKey().equals(World.NETHER)) return Dimension.NETHER; - if (world.getRegistryKey().equals(World.END)) return Dimension.END; - if (world.getRegistryKey().equals(World.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + Identifier id = delegate.getRegistryKey().getValue(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -92,8 +82,29 @@ public class FabricWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FabricWorld that = (FabricWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java index 3b868cae..9e60f395 100644 --- a/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java +++ b/implementations/fabric-1.16.2/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.core.logger.AbstractLogger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java index 96497a24..be2e1ae9 100644 --- a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java +++ b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java @@ -26,14 +26,14 @@ package de.bluecolored.bluemap.fabric; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.math.Vec3d; -import java.io.IOException; import java.util.Optional; public class FabricCommandSource implements CommandSource { @@ -75,13 +75,8 @@ public class FabricCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index b3de24cf..411e1c34 100644 --- a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -35,8 +35,8 @@ import java.util.UUID; public class FabricEventForwarder { - private FabricMod mod; - private Collection eventListeners; + private final FabricMod mod; + private final Collection eventListeners; public FabricEventForwarder(FabricMod mod) { this.mod = mod; diff --git a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index 05d9363c..09ab1ba6 100644 --- a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -53,7 +53,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class FabricMod implements ModInitializer, ServerInterface { +public class FabricMod implements ModInitializer, Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -143,7 +143,7 @@ public class FabricMod implements ModInitializer, ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -153,9 +153,7 @@ public class FabricMod implements ModInitializer, ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { Identifier identifier = Identifier.tryParse((String) world); @@ -169,12 +167,12 @@ public class FabricMod implements ModInitializer, ServerInterface { } if (world instanceof net.minecraft.server.world.ServerWorld) - return Optional.of(getWorld((net.minecraft.server.world.ServerWorld) world)); + return Optional.of(getServerWorld((net.minecraft.server.world.ServerWorld) world)); return Optional.empty(); } - public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) { + public ServerWorld getServerWorld(net.minecraft.server.world.ServerWorld serverWorld) { return worlds.get(serverWorld); } @@ -219,11 +217,6 @@ public class FabricMod implements ModInitializer, ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index 1775124f..7025ccda 100644 --- a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.server.MinecraftServer; @@ -36,7 +37,6 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.LightType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -53,12 +53,11 @@ public class FabricPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -83,7 +82,7 @@ public class FabricPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -107,11 +106,6 @@ public class FabricPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -132,16 +126,10 @@ public class FabricPlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -150,7 +138,6 @@ public class FabricPlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); @@ -160,12 +147,7 @@ public class FabricPlayer implements Player { this.skyLight = player.getServerWorld().getLightingProvider().get(LightType.SKY).getLightLevel(player.getBlockPos()); this.blockLight = player.getServerWorld().getLightingProvider().get(LightType.BLOCK).getLightLevel(player.getBlockPos()); - try { - var world = mod.getWorld(player.getServerWorld()); - this.world = mod.getPluginInstance().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getServerWorld()); } } diff --git a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java index e248fe97..ff4468d9 100644 --- a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java +++ b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; import net.minecraft.util.WorldSavePath; -import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import java.io.IOException; import java.lang.ref.WeakReference; @@ -41,27 +40,18 @@ import java.util.concurrent.ExecutionException; public class FabricWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public FabricWorld(net.minecraft.server.world.ServerWorld delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getRunDirectory().toPath().resolve(server.getSavePath(WorldSavePath.ROOT)); - this.saveFolder = DimensionType.getSaveDirectory(delegate.getRegistryKey(), worldFolder.toFile()).toPath() - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getRunDirectory().toPath() + .resolve(server.getSavePath(WorldSavePath.ROOT)); - @Override - public Dimension getDimension() { - net.minecraft.server.world.ServerWorld world = delegate.get(); - if (world != null) { - if (world.getRegistryKey().equals(World.NETHER)) return Dimension.NETHER; - if (world.getRegistryKey().equals(World.END)) return Dimension.END; - if (world.getRegistryKey().equals(World.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + Identifier id = delegate.getRegistryKey().getValue(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -92,8 +82,29 @@ public class FabricWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FabricWorld that = (FabricWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java index 3b868cae..9e60f395 100644 --- a/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java +++ b/implementations/fabric-1.17/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.core.logger.AbstractLogger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java index 96497a24..be2e1ae9 100644 --- a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java +++ b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java @@ -26,14 +26,14 @@ package de.bluecolored.bluemap.fabric; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.math.Vec3d; -import java.io.IOException; import java.util.Optional; public class FabricCommandSource implements CommandSource { @@ -75,13 +75,8 @@ public class FabricCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index b3de24cf..411e1c34 100644 --- a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -35,8 +35,8 @@ import java.util.UUID; public class FabricEventForwarder { - private FabricMod mod; - private Collection eventListeners; + private final FabricMod mod; + private final Collection eventListeners; public FabricEventForwarder(FabricMod mod) { this.mod = mod; diff --git a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index c3a0011b..bea3506b 100644 --- a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -53,7 +53,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class FabricMod implements ModInitializer, ServerInterface { +public class FabricMod implements ModInitializer, Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -143,7 +143,7 @@ public class FabricMod implements ModInitializer, ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -153,9 +153,7 @@ public class FabricMod implements ModInitializer, ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { Identifier identifier = Identifier.tryParse((String) world); @@ -169,12 +167,12 @@ public class FabricMod implements ModInitializer, ServerInterface { } if (world instanceof net.minecraft.server.world.ServerWorld) - return Optional.of(getWorld((net.minecraft.server.world.ServerWorld) world)); + return Optional.of(getServerWorld((net.minecraft.server.world.ServerWorld) world)); return Optional.empty(); } - public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) { + public ServerWorld getServerWorld(net.minecraft.server.world.ServerWorld serverWorld) { return worlds.get(serverWorld); } @@ -219,11 +217,6 @@ public class FabricMod implements ModInitializer, ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index addba0fd..5ddf3259 100644 --- a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.server.MinecraftServer; @@ -36,7 +37,6 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.LightType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -53,12 +53,11 @@ public class FabricPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -83,7 +82,7 @@ public class FabricPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -107,11 +106,6 @@ public class FabricPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -132,16 +126,10 @@ public class FabricPlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -150,7 +138,6 @@ public class FabricPlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); @@ -160,12 +147,7 @@ public class FabricPlayer implements Player { this.skyLight = player.getWorld().getLightingProvider().get(LightType.SKY).getLightLevel(player.getBlockPos()); this.blockLight = player.getWorld().getLightingProvider().get(LightType.BLOCK).getLightLevel(player.getBlockPos()); - try { - var world = mod.getWorld(player.getWorld()); - this.world = mod.getPluginInstance().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getWorld()); } } diff --git a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java index 6647b152..ff4468d9 100644 --- a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java +++ b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; import net.minecraft.util.WorldSavePath; -import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import java.io.IOException; import java.lang.ref.WeakReference; @@ -41,27 +40,18 @@ import java.util.concurrent.ExecutionException; public class FabricWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public FabricWorld(net.minecraft.server.world.ServerWorld delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getRunDirectory().toPath().resolve(server.getSavePath(WorldSavePath.ROOT)); - this.saveFolder = DimensionType.getSaveDirectory(delegate.getRegistryKey(), worldFolder) - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getRunDirectory().toPath() + .resolve(server.getSavePath(WorldSavePath.ROOT)); - @Override - public Dimension getDimension() { - net.minecraft.server.world.ServerWorld world = delegate.get(); - if (world != null) { - if (world.getRegistryKey().equals(World.NETHER)) return Dimension.NETHER; - if (world.getRegistryKey().equals(World.END)) return Dimension.END; - if (world.getRegistryKey().equals(World.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + Identifier id = delegate.getRegistryKey().getValue(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -92,8 +82,29 @@ public class FabricWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FabricWorld that = (FabricWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java index 3b868cae..9e60f395 100644 --- a/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java +++ b/implementations/fabric-1.18/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.core.logger.AbstractLogger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java index 96497a24..be2e1ae9 100644 --- a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java +++ b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java @@ -26,14 +26,14 @@ package de.bluecolored.bluemap.fabric; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.math.Vec3d; -import java.io.IOException; import java.util.Optional; public class FabricCommandSource implements CommandSource { @@ -75,13 +75,8 @@ public class FabricCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index b3de24cf..411e1c34 100644 --- a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -35,8 +35,8 @@ import java.util.UUID; public class FabricEventForwarder { - private FabricMod mod; - private Collection eventListeners; + private final FabricMod mod; + private final Collection eventListeners; public FabricEventForwarder(FabricMod mod) { this.mod = mod; diff --git a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index dcd82b63..c0e9eeb8 100644 --- a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -54,7 +54,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class FabricMod implements ModInitializer, ServerInterface { +public class FabricMod implements ModInitializer, Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -148,7 +148,7 @@ public class FabricMod implements ModInitializer, ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -158,9 +158,7 @@ public class FabricMod implements ModInitializer, ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { Identifier identifier = Identifier.tryParse((String) world); @@ -174,12 +172,12 @@ public class FabricMod implements ModInitializer, ServerInterface { } if (world instanceof net.minecraft.server.world.ServerWorld) - return Optional.of(getWorld((net.minecraft.server.world.ServerWorld) world)); + return Optional.of(getServerWorld((net.minecraft.server.world.ServerWorld) world)); return Optional.empty(); } - public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) { + public ServerWorld getServerWorld(net.minecraft.server.world.ServerWorld serverWorld) { return worlds.get(serverWorld); } @@ -224,11 +222,6 @@ public class FabricMod implements ModInitializer, ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index 69aecb43..708a87fa 100644 --- a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.server.MinecraftServer; @@ -36,7 +37,6 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.LightType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -53,12 +53,11 @@ public class FabricPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -83,7 +82,7 @@ public class FabricPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -107,11 +106,6 @@ public class FabricPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -132,16 +126,10 @@ public class FabricPlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -150,7 +138,6 @@ public class FabricPlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); @@ -160,12 +147,7 @@ public class FabricPlayer implements Player { this.skyLight = player.getWorld().getLightingProvider().get(LightType.SKY).getLightLevel(player.getBlockPos()); this.blockLight = player.getWorld().getLightingProvider().get(LightType.BLOCK).getLightLevel(player.getBlockPos()); - try { - var world = mod.getWorld(player.getWorld()); - this.world = mod.getPluginInstance().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getWorld()); } } diff --git a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java index 6647b152..ff4468d9 100644 --- a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java +++ b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; import net.minecraft.util.WorldSavePath; -import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import java.io.IOException; import java.lang.ref.WeakReference; @@ -41,27 +40,18 @@ import java.util.concurrent.ExecutionException; public class FabricWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public FabricWorld(net.minecraft.server.world.ServerWorld delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getRunDirectory().toPath().resolve(server.getSavePath(WorldSavePath.ROOT)); - this.saveFolder = DimensionType.getSaveDirectory(delegate.getRegistryKey(), worldFolder) - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getRunDirectory().toPath() + .resolve(server.getSavePath(WorldSavePath.ROOT)); - @Override - public Dimension getDimension() { - net.minecraft.server.world.ServerWorld world = delegate.get(); - if (world != null) { - if (world.getRegistryKey().equals(World.NETHER)) return Dimension.NETHER; - if (world.getRegistryKey().equals(World.END)) return Dimension.END; - if (world.getRegistryKey().equals(World.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + Identifier id = delegate.getRegistryKey().getValue(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -92,8 +82,29 @@ public class FabricWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FabricWorld that = (FabricWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java index 3b868cae..9e60f395 100644 --- a/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java +++ b/implementations/fabric-1.19.4/src/main/java/de/bluecolored/bluemap/fabric/Log4jLogger.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.core.logger.AbstractLogger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/fabric-1.20/build.gradle.kts b/implementations/fabric-1.20/build.gradle.kts index 7eecf917..66450173 100644 --- a/implementations/fabric-1.20/build.gradle.kts +++ b/implementations/fabric-1.20/build.gradle.kts @@ -106,7 +106,7 @@ tasks.shadowJar { //relocate ("com.flowpowered.math", "de.bluecolored.shadow.flowpowered.math") //DON"T relocate this, because the API depends on it relocate ("com.typesafe.config", "de.bluecolored.shadow.typesafe.config") - relocate ("net.querz.nbt", "de.bluecolored.shadow.querz.nbt") + relocate ("de.bluecolored.bluenbt", "de.bluecolored.shadow.bluecolored.bluenbt") relocate ("org.spongepowered.configurate", "de.bluecolored.shadow.configurate") relocate ("com.github.benmanes.caffeine", "de.bluecolored.shadow.benmanes.caffeine") relocate ("org.aopalliance", "de.bluecolored.shadow.aopalliance") diff --git a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java index c2483efa..5c641c7c 100644 --- a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java +++ b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricCommandSource.java @@ -26,14 +26,14 @@ package de.bluecolored.bluemap.fabric; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.util.math.Vec3d; -import java.io.IOException; import java.util.Optional; public class FabricCommandSource implements CommandSource { @@ -75,13 +75,8 @@ public class FabricCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java index b3de24cf..411e1c34 100644 --- a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java +++ b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricEventForwarder.java @@ -35,8 +35,8 @@ import java.util.UUID; public class FabricEventForwarder { - private FabricMod mod; - private Collection eventListeners; + private final FabricMod mod; + private final Collection eventListeners; public FabricEventForwarder(FabricMod mod) { this.mod = mod; diff --git a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java index be254032..f964fe3c 100644 --- a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java +++ b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricMod.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -54,7 +54,7 @@ import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class FabricMod implements ModInitializer, ServerInterface { +public class FabricMod implements ModInitializer, Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -148,7 +148,7 @@ public class FabricMod implements ModInitializer, ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -158,9 +158,7 @@ public class FabricMod implements ModInitializer, ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { Identifier identifier = Identifier.tryParse((String) world); @@ -174,12 +172,12 @@ public class FabricMod implements ModInitializer, ServerInterface { } if (world instanceof net.minecraft.server.world.ServerWorld) - return Optional.of(getWorld((net.minecraft.server.world.ServerWorld) world)); + return Optional.of(getServerWorld((net.minecraft.server.world.ServerWorld) world)); return Optional.empty(); } - public ServerWorld getWorld(net.minecraft.server.world.ServerWorld serverWorld) { + public ServerWorld getServerWorld(net.minecraft.server.world.ServerWorld serverWorld) { return worlds.get(serverWorld); } @@ -224,11 +222,6 @@ public class FabricMod implements ModInitializer, ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java index 39b5ee53..6dcf2428 100644 --- a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java +++ b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricPlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.server.MinecraftServer; @@ -36,7 +37,6 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.LightType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -53,12 +53,11 @@ public class FabricPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -83,7 +82,7 @@ public class FabricPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -107,11 +106,6 @@ public class FabricPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -132,16 +126,10 @@ public class FabricPlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -150,7 +138,6 @@ public class FabricPlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3d pos = player.getPos(); this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ()); @@ -160,12 +147,7 @@ public class FabricPlayer implements Player { this.skyLight = player.getWorld().getLightingProvider().get(LightType.SKY).getLightLevel(player.getBlockPos()); this.blockLight = player.getWorld().getLightingProvider().get(LightType.BLOCK).getLightLevel(player.getBlockPos()); - try { - var world = mod.getWorld(player.getWorld()).orElse(null); - this.world = world != null ? mod.getPluginInstance().getBlueMap().getWorldId(world.getSaveFolder()) : "unknown"; - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getServerWorld()); } } diff --git a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java index 6647b152..ff4468d9 100644 --- a/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java +++ b/implementations/fabric-1.20/src/main/java/de/bluecolored/bluemap/fabric/FabricWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.fabric; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; import net.minecraft.util.WorldSavePath; -import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import java.io.IOException; import java.lang.ref.WeakReference; @@ -41,27 +40,18 @@ import java.util.concurrent.ExecutionException; public class FabricWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public FabricWorld(net.minecraft.server.world.ServerWorld delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getRunDirectory().toPath().resolve(server.getSavePath(WorldSavePath.ROOT)); - this.saveFolder = DimensionType.getSaveDirectory(delegate.getRegistryKey(), worldFolder) - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getRunDirectory().toPath() + .resolve(server.getSavePath(WorldSavePath.ROOT)); - @Override - public Dimension getDimension() { - net.minecraft.server.world.ServerWorld world = delegate.get(); - if (world != null) { - if (world.getRegistryKey().equals(World.NETHER)) return Dimension.NETHER; - if (world.getRegistryKey().equals(World.END)) return Dimension.END; - if (world.getRegistryKey().equals(World.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + Identifier id = delegate.getRegistryKey().getValue(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -92,8 +82,29 @@ public class FabricWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FabricWorld that = (FabricWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java index eadcd607..b19ca3af 100644 --- a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java +++ b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java @@ -26,13 +26,13 @@ package de.bluecolored.bluemap.forge; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.TextComponent; -import java.io.IOException; import java.util.Optional; public class ForgeCommandSource implements CommandSource { @@ -67,13 +67,8 @@ public class ForgeCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getLevel()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getLevel()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index f50a19e9..5fc046e9 100644 --- a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -63,7 +63,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Mod(Plugin.PLUGIN_ID) -public class ForgeMod implements ServerInterface { +public class ForgeMod implements Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -155,7 +155,7 @@ public class ForgeMod implements ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (ServerLevel serverWorld : serverInstance.getAllLevels()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -165,9 +165,7 @@ public class ForgeMod implements ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { ResourceLocation resourceLocation = ResourceLocation.tryParse((String) world); @@ -181,12 +179,12 @@ public class ForgeMod implements ServerInterface { } if (world instanceof ServerLevel) - return Optional.of(getWorld((ServerLevel) world)); + return Optional.of(getServerWorld((ServerLevel) world)); return Optional.empty(); } - public ServerWorld getWorld(ServerLevel world) { + public ServerWorld getServerWorld(ServerLevel world) { return worlds.get(world); } @@ -235,11 +233,6 @@ public class ForgeMod implements ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index a739b0da..07d9fdcd 100644 --- a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.core.BlockPos; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -37,7 +38,6 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.LightLayer; import net.minecraft.world.phys.Vec3; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -54,12 +54,11 @@ public class ForgePlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -84,7 +83,7 @@ public class ForgePlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -108,11 +107,6 @@ public class ForgePlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -133,16 +127,10 @@ public class ForgePlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayer player = server.getPlayerList().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.getOrDefault(player.gameMode.getGameModeForPlayer(), Gamemode.SURVIVAL); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -151,7 +139,6 @@ public class ForgePlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3 pos = player.getPosition(1f); this.position = new Vector3d(pos.x(), pos.y(), pos.z()); @@ -161,12 +148,7 @@ public class ForgePlayer implements Player { this.skyLight = player.getLevel().getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(new BlockPos(player.getX(), player.getY(), player.getZ())); this.blockLight = player.getLevel().getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getLightValue(new BlockPos(player.getX(), player.getY(), player.getZ())); - try { - var world = mod.getWorld(player.getLevel()); - this.world = mod.getPlugin().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getLevel()); } } diff --git a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java index bdbba84c..dc2284be 100644 --- a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java +++ b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.forge; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelResource; import java.io.IOException; @@ -42,27 +41,18 @@ import java.util.concurrent.ExecutionException; public class ForgeWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public ForgeWorld(ServerLevel delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getServerDirectory().toPath().resolve(server.getWorldPath(LevelResource.ROOT)); - this.saveFolder = DimensionType.getStorageFolder(delegate.dimension(), worldFolder.toFile()).toPath() - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getServerDirectory().toPath() + .resolve(server.getWorldPath(LevelResource.ROOT)); - @Override - public Dimension getDimension() { - ServerLevel world = delegate.get(); - if (world != null) { - if (world.dimension().equals(Level.NETHER)) return Dimension.NETHER; - if (world.dimension().equals(Level.END)) return Dimension.END; - if (world.dimension().equals(Level.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + ResourceLocation id = delegate.dimension().location(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -93,8 +83,29 @@ public class ForgeWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ForgeWorld that = (ForgeWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java index fa6664ec..740b75b2 100644 --- a/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java +++ b/implementations/forge-1.17.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java @@ -24,13 +24,12 @@ */ package de.bluecolored.bluemap.forge; -import org.apache.logging.log4j.Logger; - import de.bluecolored.bluemap.core.logger.AbstractLogger; +import org.apache.logging.log4j.Logger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java index eadcd607..b19ca3af 100644 --- a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java +++ b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java @@ -26,13 +26,13 @@ package de.bluecolored.bluemap.forge; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.TextComponent; -import java.io.IOException; import java.util.Optional; public class ForgeCommandSource implements CommandSource { @@ -67,13 +67,8 @@ public class ForgeCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getLevel()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getLevel()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 112f6610..e899f182 100644 --- a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -63,7 +63,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Mod(Plugin.PLUGIN_ID) -public class ForgeMod implements ServerInterface { +public class ForgeMod implements Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -155,7 +155,7 @@ public class ForgeMod implements ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (ServerLevel serverWorld : serverInstance.getAllLevels()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -165,9 +165,7 @@ public class ForgeMod implements ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { ResourceLocation resourceLocation = ResourceLocation.tryParse((String) world); @@ -181,12 +179,12 @@ public class ForgeMod implements ServerInterface { } if (world instanceof ServerLevel) - return Optional.of(getWorld((ServerLevel) world)); + return Optional.of(getServerWorld((ServerLevel) world)); return Optional.empty(); } - public ServerWorld getWorld(ServerLevel world) { + public ServerWorld getServerWorld(ServerLevel world) { return worlds.get(world); } @@ -235,11 +233,6 @@ public class ForgeMod implements ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index a739b0da..07d9fdcd 100644 --- a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.core.BlockPos; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -37,7 +38,6 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.LightLayer; import net.minecraft.world.phys.Vec3; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -54,12 +54,11 @@ public class ForgePlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -84,7 +83,7 @@ public class ForgePlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -108,11 +107,6 @@ public class ForgePlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -133,16 +127,10 @@ public class ForgePlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayer player = server.getPlayerList().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.getOrDefault(player.gameMode.getGameModeForPlayer(), Gamemode.SURVIVAL); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -151,7 +139,6 @@ public class ForgePlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3 pos = player.getPosition(1f); this.position = new Vector3d(pos.x(), pos.y(), pos.z()); @@ -161,12 +148,7 @@ public class ForgePlayer implements Player { this.skyLight = player.getLevel().getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(new BlockPos(player.getX(), player.getY(), player.getZ())); this.blockLight = player.getLevel().getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getLightValue(new BlockPos(player.getX(), player.getY(), player.getZ())); - try { - var world = mod.getWorld(player.getLevel()); - this.world = mod.getPlugin().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getLevel()); } } diff --git a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java index ce92b260..dc2284be 100644 --- a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java +++ b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.forge; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelResource; import java.io.IOException; @@ -42,27 +41,18 @@ import java.util.concurrent.ExecutionException; public class ForgeWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public ForgeWorld(ServerLevel delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getServerDirectory().toPath().resolve(server.getWorldPath(LevelResource.ROOT)); - this.saveFolder = DimensionType.getStorageFolder(delegate.dimension(), worldFolder) - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getServerDirectory().toPath() + .resolve(server.getWorldPath(LevelResource.ROOT)); - @Override - public Dimension getDimension() { - ServerLevel world = delegate.get(); - if (world != null) { - if (world.dimension().equals(Level.NETHER)) return Dimension.NETHER; - if (world.dimension().equals(Level.END)) return Dimension.END; - if (world.dimension().equals(Level.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + ResourceLocation id = delegate.dimension().location(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -93,8 +83,29 @@ public class ForgeWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ForgeWorld that = (ForgeWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java index fa6664ec..a77265e0 100644 --- a/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java +++ b/implementations/forge-1.18.1/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.core.logger.AbstractLogger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java index 5bc17ce9..9487201e 100644 --- a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java +++ b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java @@ -28,11 +28,11 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.Component; -import java.io.IOException; import java.util.Optional; public class ForgeCommandSource implements CommandSource { @@ -67,13 +67,8 @@ public class ForgeCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getLevel()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getLevel()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 03e315f7..2a997211 100644 --- a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -29,8 +29,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -64,7 +64,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Mod(Plugin.PLUGIN_ID) -public class ForgeMod implements ServerInterface { +public class ForgeMod implements Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -160,7 +160,7 @@ public class ForgeMod implements ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (ServerLevel serverWorld : serverInstance.getAllLevels()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -170,9 +170,7 @@ public class ForgeMod implements ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { ResourceLocation resourceLocation = ResourceLocation.tryParse((String) world); @@ -186,12 +184,12 @@ public class ForgeMod implements ServerInterface { } if (world instanceof ServerLevel) - return Optional.of(getWorld((ServerLevel) world)); + return Optional.of(getServerWorld((ServerLevel) world)); return Optional.empty(); } - public ServerWorld getWorld(ServerLevel world) { + public ServerWorld getServerWorld(ServerLevel world) { return worlds.get(world); } @@ -240,11 +238,6 @@ public class ForgeMod implements ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index f24dba9e..64853c62 100644 --- a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.core.BlockPos; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -37,7 +38,6 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.LightLayer; import net.minecraft.world.phys.Vec3; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -54,12 +54,11 @@ public class ForgePlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -84,7 +83,7 @@ public class ForgePlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -108,11 +107,6 @@ public class ForgePlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -133,16 +127,10 @@ public class ForgePlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayer player = server.getPlayerList().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.getOrDefault(player.gameMode.getGameModeForPlayer(), Gamemode.SURVIVAL); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -151,7 +139,6 @@ public class ForgePlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3 pos = player.getPosition(1f); this.position = new Vector3d(pos.x(), pos.y(), pos.z()); @@ -161,12 +148,7 @@ public class ForgePlayer implements Player { this.skyLight = player.getLevel().getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ())); this.blockLight = player.getLevel().getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ())); - try { - var world = mod.getWorld(player.getLevel()); - this.world = mod.getPlugin().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.getLevel()); } } diff --git a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java index ce92b260..dc2284be 100644 --- a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java +++ b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.forge; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelResource; import java.io.IOException; @@ -42,27 +41,18 @@ import java.util.concurrent.ExecutionException; public class ForgeWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public ForgeWorld(ServerLevel delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getServerDirectory().toPath().resolve(server.getWorldPath(LevelResource.ROOT)); - this.saveFolder = DimensionType.getStorageFolder(delegate.dimension(), worldFolder) - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getServerDirectory().toPath() + .resolve(server.getWorldPath(LevelResource.ROOT)); - @Override - public Dimension getDimension() { - ServerLevel world = delegate.get(); - if (world != null) { - if (world.dimension().equals(Level.NETHER)) return Dimension.NETHER; - if (world.dimension().equals(Level.END)) return Dimension.END; - if (world.dimension().equals(Level.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + ResourceLocation id = delegate.dimension().location(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -93,8 +83,29 @@ public class ForgeWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ForgeWorld that = (ForgeWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java index fa6664ec..a77265e0 100644 --- a/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java +++ b/implementations/forge-1.19.4/src/main/java/de/bluecolored/bluemap/forge/Log4jLogger.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.core.logger.AbstractLogger; public class Log4jLogger extends AbstractLogger { - private Logger out; + private final Logger out; public Log4jLogger(Logger out) { this.out = out; diff --git a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java index 4a8f9233..64428e6f 100644 --- a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java +++ b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java @@ -28,11 +28,11 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.Component; -import java.io.IOException; import java.util.Optional; public class ForgeCommandSource implements CommandSource { @@ -67,13 +67,8 @@ public class ForgeCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getLevel()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getLevel()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index d9386d3e..50ccc1a1 100644 --- a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -30,7 +30,7 @@ import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -64,7 +64,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Mod(Plugin.PLUGIN_ID) -public class ForgeMod implements ServerInterface { +public class ForgeMod implements Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -160,7 +160,7 @@ public class ForgeMod implements ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (ServerLevel serverWorld : serverInstance.getAllLevels()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -170,9 +170,7 @@ public class ForgeMod implements ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { ResourceLocation resourceLocation = ResourceLocation.tryParse((String) world); @@ -186,12 +184,12 @@ public class ForgeMod implements ServerInterface { } if (world instanceof ServerLevel) - return Optional.of(getWorld((ServerLevel) world)); + return Optional.of(getServerWorld((ServerLevel) world)); return Optional.empty(); } - public ServerWorld getWorld(ServerLevel world) { + public ServerWorld getServerWorld(ServerLevel world) { return worlds.get(world); } @@ -240,11 +238,6 @@ public class ForgeMod implements ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index 45bb8cf9..e90501b3 100644 --- a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.core.BlockPos; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -37,7 +38,6 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.LightLayer; import net.minecraft.world.phys.Vec3; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -54,12 +54,11 @@ public class ForgePlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -84,7 +83,7 @@ public class ForgePlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -108,11 +107,6 @@ public class ForgePlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -133,16 +127,10 @@ public class ForgePlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayer player = server.getPlayerList().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.getOrDefault(player.gameMode.getGameModeForPlayer(), Gamemode.SURVIVAL); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -151,7 +139,6 @@ public class ForgePlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3 pos = player.getPosition(1f); this.position = new Vector3d(pos.x(), pos.y(), pos.z()); @@ -161,12 +148,7 @@ public class ForgePlayer implements Player { this.skyLight = player.level().getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ())); this.blockLight = player.level().getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ())); - try { - var world = mod.getWorld(player.level()).orElse(null); - this.world = world != null ? mod.getPlugin().getBlueMap().getWorldId(world.getSaveFolder()) : "unknown"; - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.serverLevel()); } } diff --git a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java index ce92b260..dc2284be 100644 --- a/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java +++ b/implementations/forge-1.20/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.forge; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelResource; import java.io.IOException; @@ -42,27 +41,18 @@ import java.util.concurrent.ExecutionException; public class ForgeWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public ForgeWorld(ServerLevel delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getServerDirectory().toPath().resolve(server.getWorldPath(LevelResource.ROOT)); - this.saveFolder = DimensionType.getStorageFolder(delegate.dimension(), worldFolder) - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getServerDirectory().toPath() + .resolve(server.getWorldPath(LevelResource.ROOT)); - @Override - public Dimension getDimension() { - ServerLevel world = delegate.get(); - if (world != null) { - if (world.dimension().equals(Level.NETHER)) return Dimension.NETHER; - if (world.dimension().equals(Level.END)) return Dimension.END; - if (world.dimension().equals(Level.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + ResourceLocation id = delegate.dimension().location(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -93,8 +83,29 @@ public class ForgeWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ForgeWorld that = (ForgeWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java index 4a8f9233..64428e6f 100644 --- a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java +++ b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeCommandSource.java @@ -28,11 +28,11 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import net.minecraft.commands.CommandSourceStack; import net.minecraft.network.chat.Component; -import java.io.IOException; import java.util.Optional; public class ForgeCommandSource implements CommandSource { @@ -67,13 +67,8 @@ public class ForgeCommandSource implements CommandSource { @Override public Optional getWorld() { - try { - var serverWorld = mod.getWorld(delegate.getLevel()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} - - return Optional.empty(); + ServerWorld serverWorld = mod.getServerWorld(delegate.getLevel()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } } diff --git a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java index 917e4348..94d4f607 100644 --- a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java +++ b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeMod.java @@ -29,8 +29,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.commands.Commands; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -63,7 +63,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Mod(Plugin.PLUGIN_ID) -public class ForgeMod implements ServerInterface { +public class ForgeMod implements Server { private final Plugin pluginInstance; private MinecraftServer serverInstance = null; @@ -159,7 +159,7 @@ public class ForgeMod implements ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (ServerLevel serverWorld : serverInstance.getAllLevels()) { loadedWorlds.add(worlds.get(serverWorld)); @@ -169,9 +169,7 @@ public class ForgeMod implements ServerInterface { @SuppressWarnings("unchecked") @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { ResourceLocation resourceLocation = ResourceLocation.tryParse((String) world); @@ -185,12 +183,12 @@ public class ForgeMod implements ServerInterface { } if (world instanceof ServerLevel) - return Optional.of(getWorld((ServerLevel) world)); + return Optional.of(getServerWorld((ServerLevel) world)); return Optional.empty(); } - public ServerWorld getWorld(ServerLevel world) { + public ServerWorld getServerWorld(ServerLevel world) { return worlds.get(world); } @@ -239,11 +237,6 @@ public class ForgeMod implements ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java index 45bb8cf9..e90501b3 100644 --- a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java +++ b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgePlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import net.minecraft.core.BlockPos; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -37,7 +38,6 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.LightLayer; import net.minecraft.world.phys.Vec3; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -54,12 +54,11 @@ public class ForgePlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private Gamemode gamemode; @@ -84,7 +83,7 @@ public class ForgePlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -108,11 +107,6 @@ public class ForgePlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -133,16 +127,10 @@ public class ForgePlayer implements Player { */ public void update() { MinecraftServer server = mod.getServer(); - if (server == null) { - this.online = false; - return; - } + if (server == null) return; ServerPlayer player = server.getPlayerList().getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.getOrDefault(player.gameMode.getGameModeForPlayer(), Gamemode.SURVIVAL); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -151,7 +139,6 @@ public class ForgePlayer implements Player { this.invisible = invis != null && invis.getDuration() > 0; this.name = Text.of(player.getName().getString()); - this.online = true; Vec3 pos = player.getPosition(1f); this.position = new Vector3d(pos.x(), pos.y(), pos.z()); @@ -161,12 +148,7 @@ public class ForgePlayer implements Player { this.skyLight = player.level().getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ())); this.blockLight = player.level().getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ())); - try { - var world = mod.getWorld(player.level()).orElse(null); - this.world = world != null ? mod.getPlugin().getBlueMap().getWorldId(world.getSaveFolder()) : "unknown"; - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = mod.getServerWorld(player.serverLevel()); } } diff --git a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java index ce92b260..dc2284be 100644 --- a/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java +++ b/implementations/neoforge-1.20.2/src/main/java/de/bluecolored/bluemap/forge/ForgeWorld.java @@ -24,12 +24,11 @@ */ package de.bluecolored.bluemap.forge; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.storage.LevelResource; import java.io.IOException; @@ -42,27 +41,18 @@ import java.util.concurrent.ExecutionException; public class ForgeWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public ForgeWorld(ServerLevel delegate) { this.delegate = new WeakReference<>(delegate); MinecraftServer server = delegate.getServer(); - Path worldFolder = delegate.getServer().getServerDirectory().toPath().resolve(server.getWorldPath(LevelResource.ROOT)); - this.saveFolder = DimensionType.getStorageFolder(delegate.dimension(), worldFolder) - .toAbsolutePath().normalize(); - } + this.worldFolder = delegate.getServer().getServerDirectory().toPath() + .resolve(server.getWorldPath(LevelResource.ROOT)); - @Override - public Dimension getDimension() { - ServerLevel world = delegate.get(); - if (world != null) { - if (world.dimension().equals(Level.NETHER)) return Dimension.NETHER; - if (world.dimension().equals(Level.END)) return Dimension.END; - if (world.dimension().equals(Level.OVERWORLD)) return Dimension.OVERWORLD; - } - - return ServerWorld.super.getDimension(); + ResourceLocation id = delegate.dimension().location(); + this.dimension = new Key(id.getNamespace(), id.getPath()); } @Override @@ -93,8 +83,29 @@ public class ForgeWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ForgeWorld that = (ForgeWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java index 6e6d6728..8a36a4dc 100644 --- a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java +++ b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.bukkit.Location; @@ -35,7 +36,6 @@ import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; -import java.io.IOException; import java.util.Optional; public class BukkitCommandSource implements CommandSource { @@ -74,11 +74,8 @@ public class BukkitCommandSource implements CommandSource { Location location = getLocation(); if (location != null) { - try { - var serverWorld = BukkitPlugin.getInstance().getWorld(location.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} + ServerWorld serverWorld = BukkitPlugin.getInstance().getServerWorld(location.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } return Optional.empty(); diff --git a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java index 4306b792..d3ef7560 100644 --- a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java +++ b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java @@ -82,7 +82,7 @@ public class BukkitCommands implements Listener { public void onTabComplete(TabCompleteEvent evt) { try { String input = evt.getBuffer(); - if (input.length() > 0 && input.charAt(0) == '/') { + if (!input.isEmpty() && input.charAt(0) == '/') { input = input.substring(1); } diff --git a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java index dbe7806e..26de00f7 100644 --- a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java +++ b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java @@ -25,16 +25,16 @@ package de.bluecolored.bluemap.bukkit; import com.flowpowered.math.vector.Vector3d; +import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; -import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.metadata.MetadataValue; import org.bukkit.potion.PotionEffectType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -51,12 +51,11 @@ public class BukkitPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private boolean vanished; @@ -78,7 +77,7 @@ public class BukkitPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -102,11 +101,6 @@ public class BukkitPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -132,10 +126,7 @@ public class BukkitPlayer implements Player { */ public void update() { org.bukkit.entity.Player player = Bukkit.getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -150,7 +141,6 @@ public class BukkitPlayer implements Player { this.vanished = vanished; this.name = Text.of(player.getName()); - this.online = player.isOnline(); Location location = player.getLocation(); this.position = new Vector3d(location.getX(), location.getY(), location.getZ()); @@ -160,12 +150,7 @@ public class BukkitPlayer implements Player { this.skyLight = player.getLocation().getBlock().getLightFromSky(); this.blockLight = player.getLocation().getBlock().getLightFromBlocks(); - try { - var world = BukkitPlugin.getInstance().getWorld(player.getWorld()); - this.world = BukkitPlugin.getInstance().getPlugin().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = BukkitPlugin.getInstance().getServerWorld(player.getWorld()); } } diff --git a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index 5c4f5a29..132ffa9f 100644 --- a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -29,7 +29,7 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -56,7 +56,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listener { +public class BukkitPlugin extends JavaPlugin implements Server, Listener { private static BukkitPlugin instance; @@ -180,7 +180,7 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (World world : Bukkit.getWorlds()) { loadedWorlds.add(worlds.get(world)); @@ -189,9 +189,7 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene } @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { var serverWorld = Bukkit.getWorld((String) world); @@ -209,12 +207,12 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene } if (world instanceof World) - return Optional.of(getWorld((World) world)); + return Optional.of(getServerWorld((World) world)); return Optional.empty(); } - public ServerWorld getWorld(World world) { + public ServerWorld getServerWorld(World world) { return worlds.get(world); } @@ -255,11 +253,6 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - private void initPlayer(org.bukkit.entity.Player bukkitPlayer) { BukkitPlayer player = new BukkitPlayer(bukkitPlayer.getUniqueId()); onlinePlayerMap.put(bukkitPlayer.getUniqueId(), player); diff --git a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java index 1d218ccd..57c9a290 100644 --- a/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java +++ b/implementations/paper/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java @@ -24,80 +24,76 @@ */ package de.bluecolored.bluemap.bukkit; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; import org.bukkit.World; import java.lang.ref.WeakReference; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Optional; public class BukkitWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public BukkitWorld(World delegate) { this.delegate = new WeakReference<>(delegate); - Dimension dimension = getDimension(); - Path saveFolder = delegate.getWorldFolder().toPath() - .resolve(dimension.getDimensionSubPath()) - .toAbsolutePath().normalize(); + Path worldFolder = delegate.getWorldFolder().toPath(); + + var id = delegate.key(); + this.dimension = new Key(id.namespace(), id.value()); // fix for hybrids - if (!Files.exists(saveFolder)) { - Path direct = delegate.getWorldFolder().toPath(); - if (Files.exists(direct) && direct.endsWith(dimension.getDimensionSubPath())) - saveFolder = direct; + Path dimensionFolder = MCAWorld.resolveDimensionFolder(worldFolder, dimension); + if (!Files.exists(dimensionFolder)) { + Path dimensionSubPath = worldFolder.relativize(dimensionFolder); + + if (Files.exists(worldFolder) && worldFolder.endsWith(dimensionSubPath)) + worldFolder = worldFolder.subpath(0, worldFolder.getNameCount() - dimensionSubPath.getNameCount()); } - this.saveFolder = saveFolder; - } - - @Override - public Dimension getDimension() { - World world = delegate.get(); - if (world != null) { - if (world.getEnvironment().equals(World.Environment.NETHER)) return Dimension.NETHER; - if (world.getEnvironment().equals(World.Environment.THE_END)) return Dimension.END; - } - return Dimension.OVERWORLD; - } - - @Override - public Optional getId() { - World world = delegate.get(); - if (world != null) { - return Optional.of(world.getUID().toString()); - } - return Optional.empty(); - } - - @Override - public Optional getName() { - World world = delegate.get(); - if (world != null) { - return Optional.of(world.getName()); - } - return Optional.empty(); + this.worldFolder = worldFolder; } + /* Not supported by folia @Override public boolean persistWorldChanges() { - /* Not supported by folia World world = delegate.get(); if (world != null) { world.save(); return true; } - */ return false; } + */ + + @Override + public Path getWorldFolder() { + return worldFolder; + } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BukkitWorld that = (BukkitWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java index e37f29a2..8559035f 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommandSource.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -36,7 +37,6 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; -import java.io.IOException; import java.util.Optional; public class BukkitCommandSource implements CommandSource { @@ -85,11 +85,8 @@ public class BukkitCommandSource implements CommandSource { Location location = getLocation(); if (location != null) { - try { - var serverWorld = BukkitPlugin.getInstance().getWorld(location.getWorld()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} + ServerWorld serverWorld = BukkitPlugin.getInstance().getServerWorld(location.getWorld()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } return Optional.empty(); diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java index 8f927eee..2ff4450a 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitCommands.java @@ -80,7 +80,7 @@ public class BukkitCommands implements Listener { public void onTabComplete(TabCompleteEvent evt) { try { String input = evt.getBuffer(); - if (input.length() > 0 && input.charAt(0) == '/') { + if (!input.isEmpty() && input.charAt(0) == '/') { input = input.substring(1); } diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java index dbe7806e..26de00f7 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlayer.java @@ -25,16 +25,16 @@ package de.bluecolored.bluemap.bukkit; import com.flowpowered.math.vector.Vector3d; +import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; -import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.metadata.MetadataValue; import org.bukkit.potion.PotionEffectType; -import java.io.IOException; import java.util.EnumMap; import java.util.Map; import java.util.UUID; @@ -51,12 +51,11 @@ public class BukkitPlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private boolean vanished; @@ -78,7 +77,7 @@ public class BukkitPlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -102,11 +101,6 @@ public class BukkitPlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -132,10 +126,7 @@ public class BukkitPlayer implements Player { */ public void update() { org.bukkit.entity.Player player = Bukkit.getPlayer(uuid); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.getGameMode()); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -150,7 +141,6 @@ public class BukkitPlayer implements Player { this.vanished = vanished; this.name = Text.of(player.getName()); - this.online = player.isOnline(); Location location = player.getLocation(); this.position = new Vector3d(location.getX(), location.getY(), location.getZ()); @@ -160,12 +150,7 @@ public class BukkitPlayer implements Player { this.skyLight = player.getLocation().getBlock().getLightFromSky(); this.blockLight = player.getLocation().getBlock().getLightFromBlocks(); - try { - var world = BukkitPlugin.getInstance().getWorld(player.getWorld()); - this.world = BukkitPlugin.getInstance().getPlugin().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = BukkitPlugin.getInstance().getServerWorld(player.getWorld()); } } diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java index ef2cd30b..829c2d86 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitPlugin.java @@ -29,7 +29,7 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.serverinterface.Player; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -55,7 +55,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listener { +public class BukkitPlugin extends JavaPlugin implements Server, Listener { private static BukkitPlugin instance; @@ -184,7 +184,7 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (World world : Bukkit.getWorlds()) { loadedWorlds.add(worlds.get(world)); @@ -193,9 +193,7 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene } @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { var serverWorld = Bukkit.getWorld((String) world); @@ -213,12 +211,12 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene } if (world instanceof World) - return Optional.of(getWorld((World) world)); + return Optional.of(getServerWorld((World) world)); return Optional.empty(); } - public ServerWorld getWorld(World world) { + public ServerWorld getServerWorld(World world) { return worlds.get(world); } @@ -261,11 +259,6 @@ public class BukkitPlugin extends JavaPlugin implements ServerInterface, Listene return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - /** * Only update some of the online players each tick to minimize performance impact on the server-thread. * Only call this method on the server-thread. diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java index 1a4fadf5..906cf3f8 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/BukkitWorld.java @@ -24,8 +24,10 @@ */ package de.bluecolored.bluemap.bukkit; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; +import de.bluecolored.bluemap.core.resources.datapack.DataPack; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.mca.MCAWorld; import org.bukkit.Bukkit; import org.bukkit.World; @@ -33,57 +35,35 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Optional; import java.util.concurrent.ExecutionException; public class BukkitWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public BukkitWorld(World delegate) { this.delegate = new WeakReference<>(delegate); - Dimension dimension = getDimension(); - Path saveFolder = delegate.getWorldFolder().toPath() - .resolve(dimension.getDimensionSubPath()) - .toAbsolutePath().normalize(); + this.dimension = resolveDimension(delegate); + Path worldFolder = delegate.getWorldFolder().toPath(); // fix for hybrids - if (!Files.exists(saveFolder)) { - Path direct = delegate.getWorldFolder().toPath(); - if (Files.exists(direct) && direct.endsWith(dimension.getDimensionSubPath())) - saveFolder = direct; + Path dimensionFolder = MCAWorld.resolveDimensionFolder(worldFolder, dimension); + if (!Files.exists(worldFolder)) { + Path dimensionSubPath = worldFolder.relativize(dimensionFolder); + + if (Files.exists(worldFolder) && worldFolder.endsWith(dimensionSubPath)) + worldFolder = worldFolder.subpath(0, worldFolder.getNameCount() - dimensionSubPath.getNameCount()); } - this.saveFolder = saveFolder; + this.worldFolder = worldFolder; } - @Override - public Dimension getDimension() { - World world = delegate.get(); - if (world != null) { - if (world.getEnvironment().equals(World.Environment.NETHER)) return Dimension.NETHER; - if (world.getEnvironment().equals(World.Environment.THE_END)) return Dimension.END; - } - return Dimension.OVERWORLD; - } - - @Override - public Optional getId() { - World world = delegate.get(); - if (world != null) { - return Optional.of(world.getUID().toString()); - } - return Optional.empty(); - } - - @Override - public Optional getName() { - World world = delegate.get(); - if (world != null) { - return Optional.of(world.getName()); - } - return Optional.empty(); + private Key resolveDimension(World world) { + if (world.getEnvironment().equals(World.Environment.NETHER)) return DataPack.DIMENSION_THE_NETHER; + if (world.getEnvironment().equals(World.Environment.THE_END)) return DataPack.DIMENSION_THE_END; + return DataPack.DIMENSION_OVERWORLD; } @Override @@ -108,8 +88,29 @@ public class BukkitWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BukkitWorld that = (BukkitWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } } diff --git a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java index b941bf29..3afeb6b3 100644 --- a/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java +++ b/implementations/spigot/src/main/java/de/bluecolored/bluemap/bukkit/JavaLogger.java @@ -24,14 +24,14 @@ */ package de.bluecolored.bluemap.bukkit; +import de.bluecolored.bluemap.core.logger.AbstractLogger; + import java.util.logging.Level; import java.util.logging.Logger; -import de.bluecolored.bluemap.core.logger.AbstractLogger; - public class JavaLogger extends AbstractLogger { - private Logger out; + private final Logger out; public JavaLogger(Logger out) { this.out = out; diff --git a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java index 7cdf99c1..e2ce04d7 100644 --- a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java +++ b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/EventForwarder.java @@ -24,9 +24,9 @@ */ package de.bluecolored.bluemap.sponge; -import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; import de.bluecolored.bluemap.common.plugin.text.Text; -import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; +import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Order; import org.spongepowered.api.event.message.PlayerChatEvent; @@ -34,7 +34,7 @@ import org.spongepowered.api.event.network.ServerSideConnectionEvent; public class EventForwarder { - private ServerEventListener listener; + private final ServerEventListener listener; public EventForwarder(ServerEventListener listener) { this.listener = listener; @@ -52,7 +52,7 @@ public class EventForwarder { @Listener(order = Order.POST) public void onPlayerChat(PlayerChatEvent evt) { - listener.onChatMessage(Text.of(PlainComponentSerializer.plain().serialize(evt.message()))); + listener.onChatMessage(Text.of(PlainTextComponentSerializer.plainText().serialize(evt.message()))); } } diff --git a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java index ccf78012..1c8e3948 100644 --- a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java +++ b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeCommandSource.java @@ -26,15 +26,15 @@ package de.bluecolored.bluemap.sponge; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.Plugin; -import de.bluecolored.bluemap.common.serverinterface.CommandSource; import de.bluecolored.bluemap.common.plugin.text.Text; +import de.bluecolored.bluemap.common.serverinterface.CommandSource; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.world.World; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.world.Locatable; -import java.io.IOException; import java.util.Optional; public class SpongeCommandSource implements CommandSource { @@ -71,11 +71,8 @@ public class SpongeCommandSource implements CommandSource { @Override public Optional getWorld() { if (audience instanceof Locatable locatable) { - try { - var serverWorld = SpongePlugin.getInstance().getWorld(locatable.serverLocation().world()); - String worldId = plugin.getBlueMap().getWorldId(serverWorld.getSaveFolder()); - return Optional.ofNullable(plugin.getWorlds().get(worldId)); - } catch (IOException ignore) {} + ServerWorld serverWorld = SpongePlugin.getInstance().getServerWorld(locatable.serverLocation().world()); + return Optional.ofNullable(plugin.getWorld(serverWorld)); } return Optional.empty(); diff --git a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java index 2624acc5..39ef1534 100644 --- a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java +++ b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlayer.java @@ -28,6 +28,7 @@ import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.common.plugin.text.Text; import de.bluecolored.bluemap.common.serverinterface.Gamemode; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.Keys; import org.spongepowered.api.effect.VanishState; @@ -37,7 +38,6 @@ import org.spongepowered.api.entity.living.player.gamemode.GameMode; import org.spongepowered.api.entity.living.player.gamemode.GameModes; import org.spongepowered.api.entity.living.player.server.ServerPlayer; -import java.io.IOException; import java.util.*; public class SpongePlayer implements Player { @@ -53,12 +53,11 @@ public class SpongePlayer implements Player { private final UUID uuid; private Text name; - private String world; + private ServerWorld world; private Vector3d position; private Vector3d rotation; private int skyLight; private int blockLight; - private boolean online; private boolean sneaking; private boolean invisible; private boolean vanished; @@ -80,7 +79,7 @@ public class SpongePlayer implements Player { } @Override - public String getWorld() { + public ServerWorld getWorld() { return this.world; } @@ -104,11 +103,6 @@ public class SpongePlayer implements Player { return blockLight; } - @Override - public boolean isOnline() { - return this.online; - } - @Override public boolean isSneaking() { return this.sneaking; @@ -134,10 +128,7 @@ public class SpongePlayer implements Player { */ public void update() { ServerPlayer player = Sponge.server().player(uuid).orElse(null); - if (player == null) { - this.online = false; - return; - } + if (player == null) return; this.gamemode = GAMEMODE_MAP.get(player.get(Keys.GAME_MODE).orElse(GameModes.NOT_SET.get())); if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL; @@ -154,7 +145,6 @@ public class SpongePlayer implements Player { this.vanished = player.get(Keys.VANISH_STATE).orElse(VanishState.unvanished()).invisible(); this.name = Text.of(player.name()); - this.online = player.isOnline(); this.position = SpongePlugin.fromSpongePoweredVector(player.position()); this.rotation = SpongePlugin.fromSpongePoweredVector(player.rotation()); this.sneaking = player.get(Keys.IS_SNEAKING).orElse(false); @@ -163,12 +153,7 @@ public class SpongePlayer implements Player { this.skyLight = 15; //player.world().light(LightTypes.SKY, player.blockPosition()); this.blockLight = 0; //player.world().light(LightTypes.BLOCK, player.blockPosition()); - try { - var world = SpongePlugin.getInstance().getWorld(player.world()); - this.world = SpongePlugin.getInstance().getPlugin().getBlueMap().getWorldId(world.getSaveFolder()); - } catch (IOException | NullPointerException e) { // NullPointerException -> the plugin isn't fully loaded - this.world = "unknown"; - } + this.world = SpongePlugin.getInstance().getServerWorld(player.world()); } } diff --git a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java index 457b4763..ef46c02b 100644 --- a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java +++ b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongePlugin.java @@ -32,8 +32,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.inject.Inject; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.serverinterface.Player; +import de.bluecolored.bluemap.common.serverinterface.Server; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; -import de.bluecolored.bluemap.common.serverinterface.ServerInterface; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.MinecraftVersion; @@ -42,7 +42,6 @@ import de.bluecolored.bluemap.sponge.SpongeCommands.SpongeCommandProxy; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.spongepowered.api.Platform; import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.Command; import org.spongepowered.api.config.ConfigDir; @@ -66,7 +65,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; @org.spongepowered.plugin.builtin.jvm.Plugin(Plugin.PLUGIN_ID) -public class SpongePlugin implements ServerInterface { +public class SpongePlugin implements Server { private static SpongePlugin instance; private final PluginContainer pluginContainer; @@ -131,7 +130,7 @@ public class SpongePlugin implements ServerInterface { } @Listener - public void onServerStart(StartedEngineEvent evt) { + public void onServerStart(StartedEngineEvent evt) { asyncExecutor = evt.game().asyncScheduler().executor(pluginContainer); syncExecutor = evt.engine().scheduler().executor(pluginContainer); @@ -156,7 +155,7 @@ public class SpongePlugin implements ServerInterface { } @Listener - public void onServerStop(StoppingEngineEvent evt) { + public void onServerStop(StoppingEngineEvent evt) { Logger.global.logInfo("Stopping..."); evt.engine().scheduler().tasks(pluginContainer).forEach(ScheduledTask::cancel); pluginInstance.unload(); @@ -221,7 +220,7 @@ public class SpongePlugin implements ServerInterface { } @Override - public Collection getLoadedWorlds() { + public Collection getLoadedServerWorlds() { Collection loadedWorlds = new ArrayList<>(3); for (var world : Sponge.server().worldManager().worlds()) { loadedWorlds.add(worlds.get(world)); @@ -230,9 +229,7 @@ public class SpongePlugin implements ServerInterface { } @Override - public Optional getWorld(Object world) { - if (world instanceof Path) - return getWorld((Path) world); + public Optional getServerWorld(Object world) { if (world instanceof String) { ResourceKey resourceKey = ResourceKey.resolve((String) world); @@ -246,12 +243,12 @@ public class SpongePlugin implements ServerInterface { } if (world instanceof org.spongepowered.api.world.server.ServerWorld) - return Optional.of(getWorld((org.spongepowered.api.world.server.ServerWorld) world)); + return Optional.of(getServerWorld((org.spongepowered.api.world.server.ServerWorld) world)); return Optional.empty(); } - public ServerWorld getWorld(org.spongepowered.api.world.server.ServerWorld world) { + public ServerWorld getServerWorld(org.spongepowered.api.world.server.ServerWorld world) { return worlds.get(world); } @@ -270,11 +267,6 @@ public class SpongePlugin implements ServerInterface { return onlinePlayerMap.values(); } - @Override - public Optional getPlayer(UUID uuid) { - return Optional.ofNullable(onlinePlayerMap.get(uuid)); - } - @Override public de.bluecolored.bluemap.core.util.Tristate isMetricsEnabled() { if (pluginContainer != null) { diff --git a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeWorld.java b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeWorld.java index 85c6e260..be14cf5e 100644 --- a/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeWorld.java +++ b/implementations/sponge/src/main/java/de/bluecolored/bluemap/sponge/SpongeWorld.java @@ -24,15 +24,14 @@ */ package de.bluecolored.bluemap.sponge; -import de.bluecolored.bluemap.common.serverinterface.Dimension; import de.bluecolored.bluemap.common.serverinterface.ServerWorld; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import de.bluecolored.bluemap.core.resources.datapack.DataPack; +import de.bluecolored.bluemap.core.util.Key; import org.spongepowered.api.world.WorldTypes; import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.file.Path; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; @@ -40,41 +39,15 @@ import java.util.concurrent.ExecutionException; public class SpongeWorld implements ServerWorld { private final WeakReference delegate; - private final Path saveFolder; + private final Path worldFolder; + private final Key dimension; public SpongeWorld(org.spongepowered.api.world.server.ServerWorld delegate) { this.delegate = new WeakReference<>(delegate); - this.saveFolder = delegate.directory() - .toAbsolutePath().normalize(); - } - - @Override - public Dimension getDimension() { - var world = delegate.get(); - if (world != null) { - if (world.worldType().equals(WorldTypes.THE_NETHER.get())) return Dimension.NETHER; - if (world.worldType().equals(WorldTypes.THE_END.get())) return Dimension.END; - } - return Dimension.OVERWORLD; - } - - @Override - public Optional getId() { - var world = delegate.get(); - if (world != null) { - return Optional.of(world.uniqueId().toString()); - } - return Optional.empty(); - } - - @Override - public Optional getName() { - var world = delegate.get(); - if (world != null) { - return world.properties().displayName() - .map(PlainTextComponentSerializer.plainText()::serialize); - } - return Optional.empty(); + this.worldFolder = delegate.directory(); + this.dimension = WorldTypes.registry().findValueKey(delegate.worldType()) + .map(k -> new Key(k.namespace(), k.value())) + .orElse(DataPack.DIMENSION_OVERWORLD); } @Override @@ -102,8 +75,29 @@ public class SpongeWorld implements ServerWorld { } @Override - public Path getSaveFolder() { - return this.saveFolder; + public Path getWorldFolder() { + return worldFolder; + } + + @Override + public Key getDimension() { + return dimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SpongeWorld that = (SpongeWorld) o; + Object world = delegate.get(); + return world != null && world.equals(that.delegate.get()); + } + + @Override + public int hashCode() { + Object world = delegate.get(); + return world != null ? world.hashCode() : 0; } }