forked from mirror/BlueMap
Rewrite the CLI to use a config file
This commit is contained in:
parent
fa32a90138
commit
fee5d7ec46
@ -26,12 +26,13 @@
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
@ -47,223 +48,184 @@
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import de.bluecolored.bluemap.core.config.Configuration;
|
||||
import de.bluecolored.bluemap.core.config.Configuration.MapConfig;
|
||||
import de.bluecolored.bluemap.core.config.ConfigurationFile;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.render.StaticRenderSettings;
|
||||
import de.bluecolored.bluemap.core.metrics.Metrics;
|
||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.web.BlueMapWebRequestHandler;
|
||||
import de.bluecolored.bluemap.core.web.WebFilesManager;
|
||||
import de.bluecolored.bluemap.core.web.BlueMapWebServer;
|
||||
import de.bluecolored.bluemap.core.web.WebSettings;
|
||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class BlueMapCLI {
|
||||
|
||||
private ConfigurationFile configFile;
|
||||
private Configuration config;
|
||||
private ResourcePack resourcePack;
|
||||
private boolean forceRender;
|
||||
|
||||
private File webroot = new File("web");
|
||||
private File dataPath = new File(webroot, "data");
|
||||
|
||||
private File extraResourceFile = null;
|
||||
private int threadCount;
|
||||
|
||||
private String mapId = null;
|
||||
private String mapName = null;
|
||||
|
||||
private int highresTileSize = 32;
|
||||
private int lowresTileSize = 50;
|
||||
private int samplesPerHighresTile = 4;
|
||||
|
||||
private float highresViewDistance = 6f;
|
||||
private float lowresViewDistance = 5f;
|
||||
|
||||
private boolean excludeFacesWithoutSunlight = true;
|
||||
private float ambientOcclusion = 0.25f;
|
||||
private float lighting = 0.8f;
|
||||
private int sliceY = Integer.MAX_VALUE;
|
||||
private int maxY = Integer.MAX_VALUE;
|
||||
private int minY = 0;
|
||||
|
||||
private int port = 8100;
|
||||
private int maxConnections = 100;
|
||||
private InetAddress bindAdress = null;
|
||||
|
||||
public BlueMapCLI() {
|
||||
threadCount = Runtime.getRuntime().availableProcessors();
|
||||
public BlueMapCLI(ConfigurationFile configFile, boolean forceRender) {
|
||||
this.configFile = configFile;
|
||||
this.config = configFile.getConfig();
|
||||
this.forceRender = forceRender;
|
||||
this.resourcePack = null;
|
||||
}
|
||||
|
||||
public void renderMap(File mapPath, boolean updateOnly) throws IOException, NoSuchResourceException {
|
||||
dataPath.mkdirs();
|
||||
|
||||
if (!mapPath.exists() || !mapPath.isDirectory()) {
|
||||
throw new IOException("Save folder '" + mapPath + "' does not exist or is not a directory!");
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Reading world...");
|
||||
World world = MCAWorld.load(mapPath.toPath(), UUID.randomUUID());
|
||||
public void renderMaps() throws IOException, NoSuchResourceException {
|
||||
Preconditions.checkNotNull(resourcePack);
|
||||
|
||||
if (mapName == null) {
|
||||
mapName = world.getName();
|
||||
}
|
||||
config.getWebDataPath().toFile().mkdirs();
|
||||
|
||||
if (mapId == null) {
|
||||
mapId = mapPath.getName().toLowerCase();
|
||||
}
|
||||
Map<String, MapType> maps = new HashMap<>();
|
||||
|
||||
Logger.global.logInfo("Starting Render:"
|
||||
+ "\n map: " + mapPath.getAbsolutePath()
|
||||
+ "\n map-id: " + mapId
|
||||
+ "\n map-name: " + mapName
|
||||
+ "\n thread-count: " + threadCount
|
||||
+ "\n data-path: " + dataPath.getAbsolutePath()
|
||||
+ "\n render-all: " + !excludeFacesWithoutSunlight
|
||||
+ "\n ambient-occlusion: " + ambientOcclusion
|
||||
+ "\n lighting: " + lighting
|
||||
+ "\n sliceY: " + (sliceY < Integer.MAX_VALUE ? sliceY : "-")
|
||||
+ "\n maxY: " + (maxY < Integer.MAX_VALUE ? maxY : "-")
|
||||
+ "\n minY: " + (minY > 0 ? minY : "-")
|
||||
+ "\n hr-tilesize: " + highresTileSize
|
||||
+ "\n lr-tilesize: " + lowresTileSize
|
||||
+ "\n lr-resolution: " + samplesPerHighresTile
|
||||
+ "\n hr-viewdistance: " + highresViewDistance
|
||||
+ "\n lr-viewdistance: " + lowresViewDistance
|
||||
);
|
||||
|
||||
Logger.global.logInfo("Loading Resources...");
|
||||
ResourcePack resourcePack = loadResources();
|
||||
|
||||
Logger.global.logInfo("Initializing renderer...");
|
||||
HiresModelManager hiresModelManager = new HiresModelManager(
|
||||
dataPath.toPath().resolve("hires").resolve(mapId),
|
||||
resourcePack,
|
||||
new Vector2i(highresTileSize, highresTileSize),
|
||||
ForkJoinPool.commonPool()
|
||||
);
|
||||
|
||||
LowresModelManager lowresModelManager = new LowresModelManager(
|
||||
dataPath.toPath().resolve("lowres").resolve(mapId),
|
||||
new Vector2i(lowresTileSize, lowresTileSize),
|
||||
new Vector2i(samplesPerHighresTile, samplesPerHighresTile)
|
||||
);
|
||||
|
||||
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager, new StaticRenderSettings(
|
||||
ambientOcclusion,
|
||||
excludeFacesWithoutSunlight,
|
||||
lighting,
|
||||
maxY,
|
||||
minY,
|
||||
sliceY
|
||||
));
|
||||
|
||||
File webSettingsFile = new File(dataPath, "settings.json");
|
||||
Logger.global.logInfo("Writing '" + webSettingsFile.getAbsolutePath() + "'...");
|
||||
WebSettings webSettings = new WebSettings(webSettingsFile);
|
||||
webSettings.setName(mapName, mapId);
|
||||
webSettings.setFrom(tileRenderer, mapId);
|
||||
webSettings.setHiresViewDistance(highresViewDistance, mapId);
|
||||
webSettings.setLowresViewDistance(lowresViewDistance, mapId);
|
||||
webSettings.save();
|
||||
|
||||
|
||||
Logger.global.logInfo("Collecting tiles to render...");
|
||||
|
||||
Collection<Vector2i> chunks;
|
||||
if (updateOnly) {
|
||||
long lastRender = webSettings.getLong(mapId, "last-render");
|
||||
chunks = world.getChunkList(lastRender);
|
||||
} else {
|
||||
chunks = world.getChunkList();
|
||||
}
|
||||
|
||||
Set<Vector2i> tiles = new HashSet<>();
|
||||
for (Vector2i chunk : chunks) {
|
||||
Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16);
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(0, 0, 15)));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 0)));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 15)));
|
||||
}
|
||||
Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)");
|
||||
|
||||
if (tiles.isEmpty()) {
|
||||
Logger.global.logInfo("Render finished!");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Starting Render...");
|
||||
long starttime = System.currentTimeMillis();
|
||||
RenderManager renderManager = new RenderManager(world, tileRenderer, tiles, threadCount);
|
||||
renderManager.start(() -> {
|
||||
Logger.global.logInfo("Waiting for threads to quit...");
|
||||
if (!ForkJoinPool.commonPool().awaitQuiescence(30, TimeUnit.SECONDS)) {
|
||||
Logger.global.logWarning("Some save-threads are taking very long to exit (>30s), they will be ignored.");
|
||||
for (MapConfig mapConfig : config.getMapConfigs()) {
|
||||
File mapPath = new File(mapConfig.getWorldPath());
|
||||
if (!mapPath.exists() || !mapPath.isDirectory()) {
|
||||
throw new IOException("Save folder '" + mapPath + "' does not exist or is not a directory!");
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Preparing renderer for map '" + mapConfig.getId() + "' ...");
|
||||
World world = MCAWorld.load(mapPath.toPath(), UUID.randomUUID());
|
||||
|
||||
HiresModelManager hiresModelManager = new HiresModelManager(
|
||||
config.getWebDataPath().resolve("hires").resolve(mapConfig.getId()),
|
||||
resourcePack,
|
||||
new Vector2i(mapConfig.getHiresTileSize(), mapConfig.getHiresTileSize()),
|
||||
ForkJoinPool.commonPool()
|
||||
);
|
||||
|
||||
LowresModelManager lowresModelManager = new LowresModelManager(
|
||||
config.getWebDataPath().resolve("lowres").resolve(mapConfig.getId()),
|
||||
new Vector2i(mapConfig.getLowresPointsPerLowresTile(), mapConfig.getLowresPointsPerLowresTile()),
|
||||
new Vector2i(mapConfig.getLowresPointsPerHiresTile(), mapConfig.getLowresPointsPerHiresTile())
|
||||
);
|
||||
|
||||
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager, mapConfig);
|
||||
|
||||
MapType mapType = new MapType(mapConfig.getId(), mapConfig.getName(), world, tileRenderer);
|
||||
maps.put(mapConfig.getId(), mapType);
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Writing settings.json ...");
|
||||
WebSettings webSettings = new WebSettings(config.getWebDataPath().resolve("settings.json").toFile());
|
||||
for (MapType map : maps.values()) {
|
||||
webSettings.setName(map.getName(), map.getId());
|
||||
webSettings.setFrom(map.getTileRenderer(), map.getId());
|
||||
}
|
||||
for (MapConfig map : config.getMapConfigs()) {
|
||||
webSettings.setHiresViewDistance(map.getHiresViewDistance(), map.getId());
|
||||
webSettings.setLowresViewDistance(map.getLowresViewDistance(), map.getId());
|
||||
}
|
||||
webSettings.save();
|
||||
|
||||
for (MapType map : maps.values()) {
|
||||
Logger.global.logInfo("Rendering map '" + map.getId() + "' ...");
|
||||
Logger.global.logInfo("Collecting tiles to render...");
|
||||
|
||||
Collection<Vector2i> chunks;
|
||||
if (!forceRender) {
|
||||
long lastRender = webSettings.getLong(map.getId(), "last-render");
|
||||
chunks = map.getWorld().getChunkList(lastRender);
|
||||
} else {
|
||||
chunks = map.getWorld().getChunkList();
|
||||
}
|
||||
|
||||
HiresModelManager hiresModelManager = map.getTileRenderer().getHiresModelManager();
|
||||
Set<Vector2i> tiles = new HashSet<>();
|
||||
for (Vector2i chunk : chunks) {
|
||||
Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16);
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(0, 0, 15)));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 0)));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 15)));
|
||||
}
|
||||
Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)");
|
||||
if (!forceRender && chunks.size() == 0) {
|
||||
Logger.global.logInfo("(This is normal if nothing has changed in the world since the last render. Use -f on the command-line to force a render of all chunks)");
|
||||
}
|
||||
|
||||
if (tiles.isEmpty()) {
|
||||
Logger.global.logInfo("Render finished!");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Starting Render...");
|
||||
long starttime = System.currentTimeMillis();
|
||||
|
||||
RenderTask task = new RenderTask(map, tiles, config.getRenderThreadCount());
|
||||
task.render();
|
||||
|
||||
try {
|
||||
webSettings.set(starttime, mapId, "last-render");
|
||||
webSettings.set(starttime, map.getId(), "last-render");
|
||||
webSettings.save();
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to update web-settings!", e);
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Render finished!");
|
||||
});
|
||||
}
|
||||
|
||||
public void updateWebFiles() throws IOException {
|
||||
webroot.mkdirs();
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Creating webfiles in " + webroot.getAbsolutePath());
|
||||
WebFilesManager webFilesManager = new WebFilesManager(webroot.toPath());
|
||||
webFilesManager.updateFiles();
|
||||
Logger.global.logInfo("Waiting for all threads to quit...");
|
||||
if (!ForkJoinPool.commonPool().awaitQuiescence(30, TimeUnit.SECONDS)) {
|
||||
Logger.global.logWarning("Some save-threads are taking very long to exit (>30s), they will be ignored.");
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Render finished!");
|
||||
}
|
||||
|
||||
public void startWebserver() throws UnknownHostException {
|
||||
if (bindAdress == null) bindAdress = InetAddress.getLocalHost();
|
||||
|
||||
Logger.global.logInfo("Starting webserver:"
|
||||
+ "\n address: " + this.bindAdress.toString() + ""
|
||||
+ "\n port: " + this.port
|
||||
+ "\n max connections: " + this.maxConnections
|
||||
+ "\n webroot: " + this.webroot.getAbsolutePath()
|
||||
);
|
||||
|
||||
WebServer webserver = new WebServer(
|
||||
this.port,
|
||||
this.maxConnections,
|
||||
this.bindAdress,
|
||||
new BlueMapWebRequestHandler(this.webroot.toPath())
|
||||
);
|
||||
public void startWebserver() throws IOException {
|
||||
Logger.global.logInfo("Starting webserver...");
|
||||
|
||||
BlueMapWebServer webserver = new BlueMapWebServer(config);
|
||||
webserver.updateWebfiles();
|
||||
webserver.start();
|
||||
}
|
||||
|
||||
private ResourcePack loadResources() throws IOException, NoSuchResourceException {
|
||||
File defaultResourceFile;
|
||||
try {
|
||||
defaultResourceFile = File.createTempFile("res", ".zip");
|
||||
defaultResourceFile.delete();
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to create temporary resource file!", e);
|
||||
}
|
||||
try {
|
||||
ResourcePack.downloadDefaultResource(defaultResourceFile);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to create default resources!", e);
|
||||
private boolean loadResources() throws IOException, NoSuchResourceException {
|
||||
File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile();
|
||||
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
||||
|
||||
if (!defaultResourceFile.exists()) {
|
||||
if (!handleMissingResources(defaultResourceFile)) return false;
|
||||
}
|
||||
|
||||
List<File> resourcePacks = new ArrayList<>();
|
||||
resourcePacks.add(defaultResourceFile);
|
||||
if (this.extraResourceFile != null) resourcePacks.add(extraResourceFile);
|
||||
//find more resource packs
|
||||
File resourcePackFolder = configFile.getFile().toPath().resolveSibling("resourcepacks").toFile();
|
||||
resourcePackFolder.mkdirs();
|
||||
File[] resourcePacks = resourcePackFolder.listFiles();
|
||||
Arrays.sort(resourcePacks);
|
||||
|
||||
ResourcePack resourcePack = new ResourcePack(resourcePacks, new File(dataPath, "textures.json"));
|
||||
List<File> resources = new ArrayList<>(resourcePacks.length + 1);
|
||||
resources.add(defaultResourceFile);
|
||||
for (File file : resourcePacks) resources.add(file);
|
||||
|
||||
defaultResourceFile.delete();
|
||||
resourcePack = new ResourcePack(resources, textureExportFile);
|
||||
|
||||
return resourcePack;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleMissingResources(File resourceFile) {
|
||||
if (config.isDownloadAccepted()) {
|
||||
try {
|
||||
Logger.global.logInfo("Downloading " + ResourcePack.MINECRAFT_CLIENT_URL + " to " + resourceFile + " ...");
|
||||
ResourcePack.downloadDefaultResource(resourceFile);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to download resources!", e);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Logger.global.logWarning("BlueMap is missing important resources!");
|
||||
Logger.global.logWarning("You need to accept the download of the required files in order of BlueMap to work!");
|
||||
Logger.global.logWarning("Please check: " + configFile.getFile() + " and try again!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, NoSuchResourceException {
|
||||
@ -271,253 +233,73 @@ public static void main(String[] args) throws IOException, NoSuchResourceExcepti
|
||||
|
||||
try {
|
||||
CommandLine cmd = parser.parse(BlueMapCLI.createOptions(), args, false);
|
||||
|
||||
|
||||
//help
|
||||
if (cmd.hasOption("h")) {
|
||||
BlueMapCLI.printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean executed = false;
|
||||
|
||||
BlueMapCLI bluemapcli = new BlueMapCLI();
|
||||
|
||||
if (cmd.hasOption("o")) bluemapcli.dataPath = new File(cmd.getOptionValue("o"));
|
||||
if (cmd.hasOption("r")) bluemapcli.extraResourceFile = new File(cmd.getOptionValue("r"));
|
||||
if (cmd.hasOption("t")) bluemapcli.threadCount = Integer.parseInt(cmd.getOptionValue("t"));
|
||||
|
||||
if (cmd.hasOption("d")) bluemapcli.webroot = new File(cmd.getOptionValue("d"));
|
||||
if (cmd.hasOption("i")) bluemapcli.bindAdress = InetAddress.getByName(cmd.getOptionValue("i"));
|
||||
bluemapcli.port = Integer.parseInt(cmd.getOptionValue("p", Integer.toString(bluemapcli.port)));
|
||||
bluemapcli.maxConnections = Integer.parseInt(cmd.getOptionValue("connections", Integer.toString(bluemapcli.maxConnections)));
|
||||
|
||||
bluemapcli.mapName = cmd.getOptionValue("n", bluemapcli.mapName);
|
||||
bluemapcli.mapId = cmd.getOptionValue("id", bluemapcli.mapId);
|
||||
|
||||
bluemapcli.ambientOcclusion = Float.parseFloat(cmd.getOptionValue("ao", Float.toString(bluemapcli.ambientOcclusion)));
|
||||
bluemapcli.lighting = Float.parseFloat(cmd.getOptionValue("lighting", Float.toString(bluemapcli.lighting)));
|
||||
bluemapcli.sliceY = Integer.parseInt(cmd.getOptionValue("y-slice", Integer.toString(bluemapcli.sliceY)));
|
||||
bluemapcli.maxY = Integer.parseInt(cmd.getOptionValue("y-max", Integer.toString(bluemapcli.maxY)));
|
||||
bluemapcli.minY = Integer.parseInt(cmd.getOptionValue("y-min", Integer.toString(bluemapcli.minY)));
|
||||
|
||||
bluemapcli.highresTileSize = Integer.parseInt(cmd.getOptionValue("hr-tilesize", Integer.toString(bluemapcli.highresTileSize)));
|
||||
bluemapcli.highresViewDistance = Float.parseFloat(cmd.getOptionValue("hr-viewdist", Float.toString(bluemapcli.highresViewDistance)));
|
||||
bluemapcli.lowresTileSize = Integer.parseInt(cmd.getOptionValue("lr-tilesize", Integer.toString(bluemapcli.lowresTileSize)));
|
||||
bluemapcli.samplesPerHighresTile = Integer.parseInt(cmd.getOptionValue("lr-resolution", Integer.toString(bluemapcli.samplesPerHighresTile)));
|
||||
bluemapcli.lowresViewDistance = Float.parseFloat(cmd.getOptionValue("lr-viewdist", Float.toString(bluemapcli.lowresViewDistance)));
|
||||
|
||||
//load config
|
||||
File configFile = new File("bluemap.conf").getAbsoluteFile();
|
||||
if (cmd.hasOption("c")) {
|
||||
bluemapcli.updateWebFiles();
|
||||
executed = true;
|
||||
configFile = new File(cmd.getOptionValue("c"));
|
||||
configFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
if (cmd.hasOption("s")) {
|
||||
bluemapcli.startWebserver();
|
||||
executed = true;
|
||||
boolean configCreated = !configFile.exists();
|
||||
|
||||
ConfigurationFile config = ConfigurationFile.loadOrCreate(configFile, BlueMapCLI.class.getResource("/bluemap-cli.conf"));
|
||||
|
||||
if (configCreated) {
|
||||
Logger.global.logInfo("No config file found! Created an example config here: " + configFile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd.hasOption("w")) {
|
||||
bluemapcli.renderMap(new File(cmd.getOptionValue("w")), !cmd.hasOption("f"));
|
||||
executed = true;
|
||||
BlueMapCLI bluemap = new BlueMapCLI(config, cmd.hasOption("f"));
|
||||
|
||||
if (config.getConfig().isWebserverEnabled()) {
|
||||
//start webserver
|
||||
bluemap.startWebserver();
|
||||
}
|
||||
|
||||
if (executed) return;
|
||||
if (!config.getConfig().getMapConfigs().isEmpty()) {
|
||||
//load resources
|
||||
if (bluemap.loadResources()) {
|
||||
|
||||
//metrics
|
||||
if (config.getConfig().isMetricsEnabled()) Metrics.sendReportAsync("CLI");
|
||||
|
||||
//render maps
|
||||
bluemap.renderMaps();
|
||||
|
||||
//since we don't need it any more, free some memory
|
||||
bluemap.resourcePack = null;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ParseException e) {
|
||||
Logger.global.logError("Failed to parse provided arguments!", e);
|
||||
} catch (NumberFormatException e) {
|
||||
Logger.global.logError("One argument expected a number but got the wrong format!", e);
|
||||
BlueMapCLI.printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
BlueMapCLI.printHelp();
|
||||
}
|
||||
|
||||
private static Options createOptions() {
|
||||
Options options = new Options();
|
||||
|
||||
options.addOption("h", "help", false, "Displays this message");
|
||||
|
||||
options.addOption(
|
||||
Option.builder("o")
|
||||
.longOpt("out")
|
||||
.hasArg()
|
||||
.argName("directory-path")
|
||||
.desc("Defines the render-output directory. Default is '<webroot>/data' (See option -d)")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("d")
|
||||
.longOpt("dir")
|
||||
.hasArg()
|
||||
.argName("directory-path")
|
||||
.desc("Defines the webroot directory. Default is './web'")
|
||||
.build()
|
||||
);
|
||||
|
||||
options.addOption("s", "webserver", false, "Starts the integrated webserver");
|
||||
options.addOption(
|
||||
Option.builder("c")
|
||||
.longOpt("create-web")
|
||||
.desc("The webfiles will be (re)created, existing web-files in the webroot will be replaced!")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("i")
|
||||
.longOpt("ip")
|
||||
.hasArg()
|
||||
.argName("ip-adress")
|
||||
.desc("Specifies the IP adress the webserver will use")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("p")
|
||||
.longOpt("port")
|
||||
.hasArg()
|
||||
.argName("port")
|
||||
.desc("Specifies the port the webserver will use. Default is 8100")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("connections")
|
||||
.hasArg()
|
||||
.argName("count")
|
||||
.desc("Sets the maximum count of simultaneous client-connections that the webserver will allow. Default is 100")
|
||||
.build()
|
||||
);
|
||||
|
||||
options.addOption(
|
||||
Option.builder("w")
|
||||
.longOpt("world")
|
||||
.hasArg()
|
||||
.argName("directory-path")
|
||||
.desc("Defines the world-save folder that will be rendered")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("f")
|
||||
.longOpt("force-render")
|
||||
.desc("Rerenders all tiles even if there are no changes since the last render")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("r")
|
||||
.longOpt("resource")
|
||||
.hasArg()
|
||||
.argName("file")
|
||||
.desc("Defines the resourcepack that will be used to render the map")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("t")
|
||||
.longOpt("threads")
|
||||
.hasArg()
|
||||
.argName("thread-count")
|
||||
.desc("Defines the number of threads that will be used to render the map. Default is the number of system cores")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("I")
|
||||
.longOpt("id")
|
||||
.hasArg()
|
||||
.argName("id")
|
||||
.desc("The id of the world. Default is the name of the world-folder")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("n")
|
||||
.longOpt("name")
|
||||
.hasArg()
|
||||
.argName("name")
|
||||
.desc("The name of the world. Default is the world-name defined in the level.dat")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("render-all")
|
||||
.desc("Also renders blocks that are normally omitted due to a sunlight value of 0. Enabling this can cause a big performance impact in the web-viewer, but it might fix some cases where blocks are missing.")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("ao")
|
||||
.longOpt("ambient-occlusion")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The strength of ambient-occlusion baked into the model (a value between 0 and 1). Default is 0.25")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("l")
|
||||
.longOpt("lighting")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The max strength of shadows baked into the model (a value between 0 and 1 where 0 is fully bright (no lighting) and 1 is max lighting-contrast). Default is 0.8")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("ys")
|
||||
.longOpt("y-slice")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Using this, BlueMap pretends that every Block above the defined value is AIR. Default is disabled")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("yM")
|
||||
.longOpt("y-max")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Blocks above this height will not be rendered. Default is no limit")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("ym")
|
||||
.longOpt("y-min")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Blocks below this height will not be rendered. Default is no limit")
|
||||
.build()
|
||||
);
|
||||
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("hr-tilesize")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Defines the size of one map-tile in blocks. If you change this value, the lowres values might need adjustment as well! Default is 32")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("hr-viewdist")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The View-Distance for hires tiles on the web-map (the value is the radius in tiles). Default is 6")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("lr-tilesize")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Defines the size of one lowres-map-tile in grid-points. Default is 50")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("lr-resolution")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map. Calculation: 32 / 4 = 8! You have to use values that result in an integer if you use the above calculation! Default is 4")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("lr-viewdist")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The View-Distance for lowres tiles on the web-map (the value is the radius in tiles). Default is 5")
|
||||
.build()
|
||||
);
|
||||
Option.builder("c")
|
||||
.longOpt("config")
|
||||
.hasArg()
|
||||
.argName("config-file")
|
||||
.desc("Sets path of the configuration file to use")
|
||||
.build()
|
||||
);
|
||||
|
||||
options.addOption("f", "force-render", false, "Forces rendering everything, instead of only rendering chunks that have been modified since the last render");
|
||||
|
||||
return options;
|
||||
}
|
||||
@ -534,7 +316,7 @@ private static void printHelp() {
|
||||
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
filename = "./" + new File(".").toPath().relativize(file.toPath()).toString();
|
||||
filename = "." + File.separator + new File("").getAbsoluteFile().toPath().relativize(file.toPath()).toString();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
filename = file.getAbsolutePath();
|
||||
}
|
||||
@ -543,13 +325,7 @@ private static void printHelp() {
|
||||
|
||||
String command = "java -jar " + filename;
|
||||
|
||||
formatter.printHelp(command + " [options]", "\nOptions:", createOptions(), "\n"
|
||||
+ "Examples:\n\n"
|
||||
+ command + " -w ./world/\n"
|
||||
+ " -> Renders the whole world to ./web/data/\n\n"
|
||||
+ command + " -csi localhost\n"
|
||||
+ " -> Creates all neccesary web-files in ./web/ and starts the webserver. (Open http://localhost:8100/ in your browser)"
|
||||
);
|
||||
formatter.printHelp(command + " [options]", "\nOptions:", createOptions(), "");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||
import de.bluecolored.bluemap.core.render.WorldTile;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class MapType {
|
||||
|
||||
private final String id;
|
||||
private String name;
|
||||
private World world;
|
||||
private TileRenderer tileRenderer;
|
||||
|
||||
public MapType(String id, String name, World world, TileRenderer tileRenderer) {
|
||||
Preconditions.checkNotNull(id);
|
||||
Preconditions.checkNotNull(name);
|
||||
Preconditions.checkNotNull(world);
|
||||
Preconditions.checkNotNull(tileRenderer);
|
||||
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.world = world;
|
||||
this.tileRenderer = tileRenderer;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public TileRenderer getTileRenderer() {
|
||||
return tileRenderer;
|
||||
}
|
||||
|
||||
public void renderTile(Vector2i tile) throws IOException, ChunkNotGeneratedException {
|
||||
getTileRenderer().render(new WorldTile(getWorld(), tile));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj != null && obj instanceof MapType) {
|
||||
MapType that = (MapType) obj;
|
||||
|
||||
return this.id.equals(that.id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -42,7 +42,7 @@
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class RenderManager extends Thread {
|
||||
public class RenderTask {
|
||||
|
||||
private World world;
|
||||
private TileRenderer tileRenderer;
|
||||
@ -54,11 +54,9 @@ public class RenderManager extends Thread {
|
||||
|
||||
private Thread[] threads;
|
||||
|
||||
private Runnable onFinished;
|
||||
|
||||
public RenderManager(World world, TileRenderer tileRenderer, Collection<Vector2i> tilesToRender, int threadCount) {
|
||||
this.world = world;
|
||||
this.tileRenderer = tileRenderer;
|
||||
public RenderTask(MapType map, Collection<Vector2i> tilesToRender, int threadCount) {
|
||||
this.world = map.getWorld();
|
||||
this.tileRenderer = map.getTileRenderer();
|
||||
|
||||
//Sort the chunks to opimize the chunk-cache usage of MCAWorld and generate the world in a nicer order, so you can see the first results early in the web-map during render
|
||||
Vector2d sortGridSize = new Vector2d(20, 20).div(tileRenderer.getHiresModelManager().getTileSize().toDouble().div(16)).ceil().max(1, 1); //Find a good grid size to match the MCAWorlds chunk-cache size of 500
|
||||
@ -96,14 +94,7 @@ public RenderManager(World world, TileRenderer tileRenderer, Collection<Vector2i
|
||||
|
||||
}
|
||||
|
||||
public synchronized void start(Runnable onFinished) {
|
||||
this.onFinished = onFinished;
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
public void render() {
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
@ -154,8 +145,6 @@ public void run() {
|
||||
}
|
||||
|
||||
tileRenderer.save();
|
||||
|
||||
onFinished.run();
|
||||
}
|
||||
|
||||
private void renderThread() {
|
168
BlueMapCLI/src/main/resources/bluemap-cli.conf
Normal file
168
BlueMapCLI/src/main/resources/bluemap-cli.conf
Normal file
@ -0,0 +1,168 @@
|
||||
## ##
|
||||
## BlueMap ##
|
||||
## ##
|
||||
## by Blue (Lukas Rieger) ##
|
||||
## http://bluecolored.de/ ##
|
||||
## ##
|
||||
|
||||
# !! Don't change this !!
|
||||
# This is used to detect version-changes in the configuration
|
||||
# and update configuration correctly.
|
||||
version: "%version%"
|
||||
|
||||
# By changing the setting (accept-download) below to TRUE you are indicating that you have accepted mojang's EULA (https://account.mojang.com/documents/minecraft_eula),
|
||||
# you confirm that you own a license to Minecraft (Java Edition)
|
||||
# and you agree that BlueMap will download and use this file for you: %minecraft-client-url%
|
||||
# (Alternatively you can download the file yourself and store it here: <data>/minecraft-client-%minecraft-client-version%.jar)
|
||||
# This file contains resources that belong to mojang and you must not redistribute it or do anything else that is not compilant with mojang's EULA.
|
||||
# BlueMap uses resources in this file to generate the 3D-Models used for the map and texture them. (BlueMap will not work without those resources.)
|
||||
# %datetime-iso%
|
||||
accept-download: false
|
||||
|
||||
# If this is true, BlueMap might send really basic metrics reports containg only the implementation-type and the version that is being used to https://metrics.bluecolored.de/bluemap/
|
||||
# This allows me to track the basic usage of BlueMap and helps me stay motivated to further develop this tool! Please leave it on :)
|
||||
# An example report looks like this: {"implementation":"CLI","version":"%version%"}
|
||||
metrics: true
|
||||
|
||||
# The folder where bluemap saves data-files it needs during runtime or to save e.g. the render-progress to resume it later.
|
||||
data: "data"
|
||||
|
||||
web {
|
||||
# With this setting you can enable the integrated web-server.
|
||||
enabled: false
|
||||
|
||||
# The webroot of the website that displays the map.
|
||||
webroot: "web"
|
||||
|
||||
# The IP-Adress that the webserver binds to.
|
||||
# If this setting is commented out, bluemap tries to find the default ip-adress of your system.
|
||||
# If you only want to access it locally use "localhost".
|
||||
#ip: "localhost"
|
||||
#ip: "127.0.0.1"
|
||||
|
||||
# The port that the webserver listenes to.
|
||||
# Default is 8100
|
||||
port: 8100
|
||||
|
||||
# Max number of simultaneous connections that the webserver allows
|
||||
# Default is 100
|
||||
maxConnectionCount: 100
|
||||
|
||||
# Unncomment this to override the path where bluemap stores the data-files.
|
||||
# Default is "<webroot>/data"
|
||||
#web-data: "path/to/data/folder"
|
||||
}
|
||||
|
||||
# This changes the amount of threads that BlueMap will use to render the maps.
|
||||
# A higher value can improve render-speed but could impact performance on the host machine.
|
||||
# This should be always below or equal to the number of available processor-cores.
|
||||
# If this value is commented out BlueMap tries to find the optimal thread count to max out render-performance
|
||||
#renderThreadCount: 2
|
||||
|
||||
# This is an array with multiple configured maps.
|
||||
# You can define multiple maps, for different worlds with different render-settings here
|
||||
maps: [
|
||||
|
||||
{
|
||||
# The id of this map
|
||||
# Should only contain word-charactes: [a-zA-Z0-9_]
|
||||
id: "world"
|
||||
|
||||
# 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: "World"
|
||||
|
||||
# The path to the save-folder of the world to render
|
||||
world: "world"
|
||||
|
||||
# If this is false, BlueMap tries to omit all blocks that are not visible from above-ground.
|
||||
# More specific: Block-Faces that have a sunlight/skylight value of 0 are removed.
|
||||
# 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.
|
||||
# Default is false
|
||||
renderCaves: false
|
||||
|
||||
# AmbientOcclusion adds soft shadows into corners, which gives the map a much better look.
|
||||
# This has only a small impact on render-time and has no impact on the web-performance of the map.
|
||||
# The value defines the strength of the shading, a value of 0 disables ambientOcclusion.
|
||||
# Default is 0.25
|
||||
ambientOcclusion: 0.25
|
||||
|
||||
# Lighting uses the light-data in minecraft to shade each block-face.
|
||||
# If this is enabled, caves and inside buildings without torches will be darker.
|
||||
# The value defines the strength of the shading and a value of 0 disables lighting (every block will be fully lit).
|
||||
# Default is 0.8
|
||||
lighting: 0.8
|
||||
|
||||
# Using this, BlueMap pretends that every Block above the defined value is AIR.
|
||||
# Default is disabled
|
||||
#sliceY: 90
|
||||
|
||||
# With the below values you can just not render blocks at certain heights.
|
||||
# This can be used to ignore the nethers ceiling.
|
||||
# Default is no min or max y value
|
||||
#minY: 50
|
||||
#maxY: 126
|
||||
|
||||
# HIRES is the high-resolution render of the map. Where you see every block.
|
||||
hires {
|
||||
# Defines the size of one map-tile in blocks.
|
||||
# If you change this value, the lowres values might need adjustment as well!
|
||||
# Default is 32
|
||||
tileSize: 32
|
||||
|
||||
# The View-Distance for hires tiles on the web-map (the value is the radius in tiles)
|
||||
# Default is 3.5
|
||||
viewDistance: 3.5
|
||||
}
|
||||
|
||||
# LOWRES is the low-resolution render of the map. THats the model that you see if you zoom far out to get an overview.
|
||||
lowres {
|
||||
# Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map.
|
||||
# Calculation: 32 / 4 = 8
|
||||
# You can only use values that result in an integer if you use the above calculation!
|
||||
# Default is 4
|
||||
pointsPerHiresTile: 4
|
||||
|
||||
# Defines the size of one lowres-map-tile in points.
|
||||
# Default is 50
|
||||
pointsPerLowresTile: 50
|
||||
|
||||
# The View-Distance for lowres tiles on the web-map (the value is the radius in tiles)
|
||||
# Default is 4
|
||||
viewDistance: 4
|
||||
}
|
||||
}
|
||||
|
||||
# Here another example for the End-Map
|
||||
# Things we dont want to change from default we can just omit
|
||||
{
|
||||
id: "end"
|
||||
name: "End"
|
||||
world: "world/DIM1"
|
||||
|
||||
# In the end is no light, so we need to enable this or we don't see anything.
|
||||
renderCaves: true
|
||||
|
||||
# Same here, we don't want a dark map. But not completely disabled, so we see the effect of e.g torches.
|
||||
lighting: 0.4
|
||||
}
|
||||
|
||||
# Here another example for the Nether-Map
|
||||
{
|
||||
id: "nether"
|
||||
name: "Nether"
|
||||
world: "world/DIM-1"
|
||||
|
||||
renderCaves: true
|
||||
lighting: 0.6
|
||||
|
||||
# We slice the whole world at y:90 so evrery block above 90 will be air.
|
||||
# This way we dont render the nethers ceiling.
|
||||
sliceY: 90
|
||||
|
||||
# Instead of slicing we also could do this, that would look like an x-ray view through the ceiling.
|
||||
#maxY: 126
|
||||
}
|
||||
|
||||
]
|
@ -43,6 +43,7 @@ public class Configuration implements WebServerConfig {
|
||||
private String version;
|
||||
|
||||
private boolean downloadAccepted = false;
|
||||
private boolean metricsEnabled = false;
|
||||
|
||||
private boolean webserverEnabled = true;
|
||||
private int webserverPort = 8100;
|
||||
@ -61,6 +62,7 @@ public class Configuration implements WebServerConfig {
|
||||
public Configuration(ConfigurationNode node) throws IOException {
|
||||
version = node.getNode("version").getString("-");
|
||||
downloadAccepted = node.getNode("accept-download").getBoolean(false);
|
||||
metricsEnabled = node.getNode("metrics").getBoolean(false);
|
||||
|
||||
dataPath = toFolder(node.getNode("data").getString("data"));
|
||||
|
||||
@ -157,6 +159,10 @@ public boolean isDownloadAccepted() {
|
||||
return downloadAccepted;
|
||||
}
|
||||
|
||||
public boolean isMetricsEnabled() {
|
||||
return metricsEnabled;
|
||||
}
|
||||
|
||||
public int getRenderThreadCount() {
|
||||
return renderThreadCount;
|
||||
}
|
||||
|
@ -52,16 +52,23 @@ public class ConfigurationFile {
|
||||
CONFIG_PLACEHOLDERS.add(new Placeholder("minecraft-client-version", ResourcePack.MINECRAFT_CLIENT_VERSION));
|
||||
}
|
||||
|
||||
private File configFile;
|
||||
private Configuration config;
|
||||
|
||||
private ConfigurationFile(File configFile) throws IOException {
|
||||
this.configFile = configFile;
|
||||
|
||||
ConfigurationLoader<CommentedConfigurationNode> configLoader = HoconConfigurationLoader.builder()
|
||||
.setFile(configFile)
|
||||
.build();
|
||||
|
||||
CommentedConfigurationNode rootNode = configLoader.load();
|
||||
|
||||
config = new Configuration(rootNode);
|
||||
this.config = new Configuration(rootNode);
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return configFile;
|
||||
}
|
||||
|
||||
public Configuration getConfig() {
|
||||
|
Loading…
Reference in New Issue
Block a user