use of org.lanternpowered.server.world.LanternWorldPropertiesIO.LevelData in project LanternServer by LanternPowered.
the class LanternWorldManager method copyWorld.
/**
* Creates a world copy asynchronously using the new name given and returns
* the new world properties if the copy was possible.
*
* <p>If the world is already loaded then the following will occur:</p>
*
* <ul>
* <li>World is saved.</li>
* <li>World saving is disabled.</li>
* <li>World is copied. </li>
* <li>World saving is enabled.</li>
* </ul>
*
* @param worldProperties The world properties to copy
* @param copyName The name for copied world
* @return An {@link Optional} containing the properties of the new world
* instance, if the copy was successful
*/
public CompletableFuture<Optional<WorldProperties>> copyWorld(WorldProperties worldProperties, String copyName) {
checkNotNull(worldProperties, "worldProperties");
checkNotNull(copyName, "copyName");
return Functional.asyncFailableFuture(() -> {
// Get the new dimension id
final int dimensionId = getNextFreeDimensionId();
// TODO: Save the world if loaded
// TODO: Block world saving
// Get the lookup entry
final WorldLookupEntry entry = this.worldByProperties.get(worldProperties);
// The folder of the new world
final Path targetFolder = this.getWorldFolder(copyName, dimensionId);
// The folder of the original world
final Path folder = entry.folder;
if (Files.exists(targetFolder) && Files.list(targetFolder).count() > 0) {
this.logger.error("The copy world folder already exists and it isn't empty!");
return Optional.empty();
}
// Save the changes once more to make sure that they will be saved
saveWorldProperties(worldProperties);
// Copy the world folder
final String folderPath = folder.toFile().getAbsolutePath();
try {
Files.walkFileTree(folder, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
final Path dstPath = targetFolder.resolve(path.toFile().getAbsolutePath().substring(folderPath.length()));
Files.copy(path, dstPath, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
this.logger.error("Failed to copy the world folder of {}: {} to {}", copyName, folder, targetFolder, e);
return Optional.empty();
}
final WorldConfigResult result = this.getOrCreateWorldConfig(copyName);
// Copy the settings
result.config.copyFrom(entry.properties.getConfig());
result.config.save();
final LevelData levelData;
final LanternWorldProperties properties;
try {
levelData = LanternWorldPropertiesIO.read(folder, copyName, null);
properties = LanternWorldPropertiesIO.convert(levelData, result.config, false);
} catch (IOException e) {
this.logger.error("Unable to open the copied world properties of {}", copyName, e);
return Optional.empty();
}
final LevelData newData = new LevelData(levelData.worldName, levelData.uniqueId, levelData.worldData, levelData.spongeWorldData, dimensionId, null);
// Store the new world
this.addUpdatedWorldProperties(properties, targetFolder, dimensionId);
// Save the changes once more to make sure that they will be saved
LanternWorldPropertiesIO.write(targetFolder, newData);
return Optional.of(properties);
}, this.executor);
}
use of org.lanternpowered.server.world.LanternWorldPropertiesIO.LevelData in project LanternServer by LanternPowered.
the class LanternWorldManager method saveWorldProperties.
/**
* Persists the given {@link WorldProperties} to the world storage for it,
* updating any modified values.
*
* @param worldProperties the world properties to save
* @return true if the save was successful
*/
public boolean saveWorldProperties(WorldProperties worldProperties) {
checkNotNull(worldProperties, "worldProperties");
final WorldLookupEntry entry = this.worldByProperties.get(worldProperties);
checkNotNull(entry, "entry");
final LanternWorldProperties worldProperties0 = (LanternWorldProperties) worldProperties;
final BitSet dimensionMap = entry.dimensionId == 0 ? (BitSet) this.dimensionMap.clone() : null;
try {
final LevelData levelData = LanternWorldPropertiesIO.convert(worldProperties0, entry.dimensionId, dimensionMap);
LanternWorldPropertiesIO.write(entry.folder, levelData);
worldProperties0.getConfig().save();
} catch (IOException e) {
this.logger.error("Unable to save the world properties of {}: {}", worldProperties.getWorldName(), e.getMessage(), e);
return false;
}
return true;
}
use of org.lanternpowered.server.world.LanternWorldPropertiesIO.LevelData in project LanternServer by LanternPowered.
the class LanternWorldManager method init.
/**
* Initializes the root world and the dimension id map.
*/
@SuppressWarnings("SuspiciousMethodCalls")
public void init() throws IOException {
final Path rootWorldDir = this.rootWorldDirectory.get();
// The properties of the root world
LanternWorldProperties rootWorldProperties = null;
LevelData levelData;
if (Files.exists(rootWorldDir)) {
try {
levelData = LanternWorldPropertiesIO.read(rootWorldDir, null, null);
// Create a config
try {
final WorldConfigResult result = getOrCreateWorldConfig(levelData.worldName);
rootWorldProperties = LanternWorldPropertiesIO.convert(levelData, result.config, result.newCreated);
if (result.newCreated) {
result.config.save();
}
} catch (IOException e) {
this.logger.error("Unable to read/write the root world config, please fix this issue before loading the world.", e);
throw e;
}
// Already store the data
addUpdatedWorldProperties(rootWorldProperties, this.rootWorldDirectory.get(), 0);
} catch (FileNotFoundException e) {
// We can ignore this exception, because this means
// that we have to generate the world
} catch (IOException e) {
this.logger.error("Unable to load root world, please fix this issue before starting the server.", e);
throw e;
}
}
// Always use a new dimension map, we will scan for the worlds
// through folders and the dimension ids will be generated or
// refreshed if needed
this.dimensionMap = new BitSet();
LanternWorldProperties rootWorldProperties0 = rootWorldProperties;
// Generate the root (default) world if missing
if (rootWorldProperties0 == null) {
final String name = "Overworld";
rootWorldProperties0 = createWorld(WorldArchetype.builder().from(WorldArchetypes.OVERWORLD).generator(GeneratorTypes.OVERWORLD).build(name, name), "", 0);
}
// Get all the dimensions (worlds) that should be loaded
final List<WorldLookupEntry> loadQueue = new ArrayList<>(1);
// Add the root dimension
loadQueue.add(this.worldByDimensionId.get(0));
final Map<Integer, Tuple<Path, LevelData>> idToLevelData = new HashMap<>();
final List<Tuple<Path, LevelData>> levelDataWithoutId = new ArrayList<>();
if (rootWorldProperties != null) {
for (Path path : Files.list(rootWorldDir).filter(Files::isDirectory).collect(Collectors.toList())) {
if (Files.list(path).count() == 0 || this.ignoredDirectoryNames.contains(path.getFileName().toString().toLowerCase())) {
continue;
}
try {
try {
levelData = LanternWorldPropertiesIO.read(path, null, null);
} catch (FileNotFoundException e) {
this.logger.info("Found a invalid world directory {} inside the root world directory, the level.dat file is missing", path.getFileName().toString());
continue;
}
final Integer dimensionId;
if (path.getFileName().toString().equalsIgnoreCase("DIM1")) {
dimensionId = 1;
} else if (path.getFileName().toString().equalsIgnoreCase("DIM-1")) {
dimensionId = -1;
} else if (levelData.dimensionId != null && levelData.dimensionId >= MIN_CUSTOM_DIMENSION_ID) {
dimensionId = levelData.dimensionId;
} else {
dimensionId = null;
}
final Tuple<Path, LevelData> tuple = Tuple.of(path, levelData);
if (dimensionId == null || idToLevelData.containsValue(dimensionId)) {
levelDataWithoutId.add(tuple);
} else {
idToLevelData.put(dimensionId, tuple);
}
} catch (Exception e) {
this.logger.info("Unable to load the world in the directory {}", path.getFileName().toString());
}
}
}
// Generate a dimension id for all the worlds that need it
for (Tuple<Path, LevelData> tuple : levelDataWithoutId) {
idToLevelData.put(getNextFreeDimensionId(), tuple);
}
levelDataWithoutId.clear();
// Load the world properties and config files for all the worlds
for (Map.Entry<Integer, Tuple<Path, LevelData>> entry : idToLevelData.entrySet()) {
levelData = entry.getValue().getSecond();
final LanternWorldProperties worldProperties;
try {
final WorldConfigResult result = getOrCreateWorldConfig(levelData.worldName);
worldProperties = LanternWorldPropertiesIO.convert(levelData, result.config, result.newCreated);
if (result.newCreated) {
result.config.save();
}
} catch (IOException e) {
this.logger.error("Unable to read/write the world config, please fix this issue before loading the world.", e);
throw e;
}
// Store the world properties
final WorldLookupEntry lookupEntry = addWorldProperties(worldProperties, entry.getValue().getFirst(), entry.getKey());
// Check if it should be loaded on startup
if (worldProperties.loadOnStartup()) {
loadQueue.add(lookupEntry);
}
}
if (!this.worldByDimensionId.containsKey(-1)) {
final String name = "Nether";
if (createWorld(WorldArchetype.builder().from(WorldArchetypes.THE_NETHER).generator(GeneratorTypes.NETHER).build(name, name), "DIM-1", -1).loadOnStartup()) {
loadQueue.add(this.worldByDimensionId.get(-1));
}
}
// The end
if (!this.worldByDimensionId.containsKey(1)) {
final String name = "TheEnd";
if (createWorld(WorldArchetype.builder().from(WorldArchetypes.THE_END).generator(GeneratorTypes.THE_END).build(name, name), "DIM1", 1).loadOnStartup()) {
loadQueue.add(this.worldByDimensionId.get(1));
}
}
// The root world must be enabled
rootWorldProperties0.setEnabled(true);
// Load all the worlds
loadQueue.forEach(this::loadWorld);
}
Aggregations