Multiverse-Core/src/main/java/org/mvplugins/multiverse/core/world/config/WorldsConfigManager.java

217 lines
8.2 KiB
Java

package org.mvplugins.multiverse.core.world.config;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.Tuple2;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler;
/**
* Manages the worlds.yml file.
*/
@Service
public final class WorldsConfigManager {
// TODO: Rename to worlds.yml
private static final String CONFIG_FILENAME = "worlds2.yml";
private final Map<String, WorldConfig> worldConfigMap;
private final File worldConfigFile;
private final MultiverseCore multiverseCore;
private YamlConfiguration worldsConfig;
@Inject
WorldsConfigManager(@NotNull MultiverseCore multiverseCore) {
worldConfigMap = new HashMap<>();
worldConfigFile = multiverseCore.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
this.multiverseCore = multiverseCore;
}
/**
* Loads the worlds.yml file and creates a WorldConfig for each world in the file if it doesn't already exist.
*
* @return A tuple containing a list of the new WorldConfigs added and a list of the worlds removed from the config.
*/
public Try<Tuple2<List<WorldConfig>, List<String>>> load() {
return Try.of(() -> {
loadWorldYmlFile();
return parseNewAndRemovedWorlds();
});
}
/**
* Loads the worlds.yml file.
*
* @throws IOException If an error occurs while loading the file.
* @throws InvalidConfigurationException If the file is not a valid YAML file.
*/
private void loadWorldYmlFile() throws IOException, InvalidConfigurationException {
boolean exists = worldConfigFile.exists();
if (!exists && !worldConfigFile.createNewFile()) {
throw new IllegalStateException("Could not create worlds.yml config file");
}
if (exists) {
migrateRemoveOldConfigSerializable();
}
worldsConfig = new YamlConfiguration();
worldsConfig.load(worldConfigFile);
}
private void migrateRemoveOldConfigSerializable() {
Try.of(() -> Files.readString(worldConfigFile.toPath()))
.mapTry(configData -> {
if (!configData.contains("==: MVWorld")) {
throw new ConfigMigratedException();
}
return configData.replace(" ==: MVWorld\n", "")
.replace(" ==: MVSpawnSettings\n", "")
.replace(" ==: MVSpawnSubSettings\n", "")
.replace(" ==: MVEntryFee\n", "");
})
.andThenTry(configData -> Files.writeString(worldConfigFile.toPath(), configData))
.andThenTry(() -> {
YamlConfiguration config = YamlConfiguration.loadConfiguration(worldConfigFile);
List<ConfigurationSection> worlds = config.getConfigurationSection("worlds")
.getKeys(false)
.stream()
.map(worldName -> config.getConfigurationSection("worlds." + worldName))
.toList();
config.set("worlds", null);
for (ConfigurationSection world : worlds) {
config.createSection(world.getName(), world.getValues(true));
}
config.save(worldConfigFile);
})
.onFailure(e -> {
if (e instanceof ConfigMigratedException) {
Logging.fine("Config already migrated");
return;
}
Logging.warning("Failed to migrate old worlds.yml file: %s", e.getMessage());
e.printStackTrace();
});
}
/**
* Parses the worlds.yml file and creates a WorldConfig for each world in the file if it doesn't already exist.
*
* @return A tuple containing a list of the new WorldConfigs added and a list of the worlds removed from the config.
*/
private Tuple2<List<WorldConfig>, List<String>> parseNewAndRemovedWorlds() {
Set<String> allWorldsInConfig = worldsConfig.getKeys(false);
List<WorldConfig> newWorldsAdded = new ArrayList<>();
for (String worldName : allWorldsInConfig) {
getWorldConfig(worldName)
.peek(config -> config.load(getWorldConfigSection(worldName)))
.onEmpty(() -> {
WorldConfig newWorldConfig = new WorldConfig(
worldName,
getWorldConfigSection(worldName),
multiverseCore);
worldConfigMap.put(worldName, newWorldConfig);
newWorldsAdded.add(newWorldConfig);
});
}
List<String> worldsRemoved = worldConfigMap.keySet().stream()
.filter(worldName -> !allWorldsInConfig.contains(worldName))
.toList();
for (String s : worldsRemoved) {
worldConfigMap.remove(s);
}
return new Tuple2<>(newWorldsAdded, worldsRemoved);
}
/**
* Whether the worlds.yml file has been loaded.
*
* @return Whether the worlds.yml file has been loaded.
*/
public boolean isLoaded() {
return worldsConfig != null;
}
/**
* Saves the worlds.yml file.
*
* @return Whether the save was successful or the error that occurred.
*/
public Try<Void> save() {
return Try.run(() -> worldsConfig.save(worldConfigFile));
}
/**
* Gets the {@link WorldConfig} instance of all worlds in the worlds.yml file.
*
* @param worldName The name of the world to check.
* @return The {@link WorldConfig} instance of the world, or empty option if it doesn't exist.
*/
public @NotNull Option<WorldConfig> getWorldConfig(@NotNull String worldName) {
return Option.of(worldConfigMap.get(worldName));
}
/**
* Add a new world to the worlds.yml file. If a world with the given name already exists, an exception is thrown.
*
* @param worldName The name of the world to add.
* @return The newly created {@link WorldConfig} instance.
*/
public @NotNull WorldConfig addWorldConfig(@NotNull String worldName) {
if (worldConfigMap.containsKey(worldName)) {
throw new IllegalArgumentException("WorldConfig for world " + worldName + " already exists.");
}
WorldConfig worldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName), multiverseCore);
worldConfigMap.put(worldName, worldConfig);
return worldConfig;
}
/**
* Deletes the world config for the given world.
*
* @param worldName The name of the world to delete.
*/
public void deleteWorldConfig(@NotNull String worldName) {
worldConfigMap.remove(worldName);
worldsConfig.set(worldName, null);
}
/**
* Gets the {@link ConfigurationSection} for the given world in the worlds.yml file. If the section doesn't exist,
* it is created.
*
* @param worldName The name of the world.
* @return The {@link ConfigurationSection} for the given world.
*/
private ConfigurationSection getWorldConfigSection(String worldName) {
return worldsConfig.isConfigurationSection(worldName)
? worldsConfig.getConfigurationSection(worldName) : worldsConfig.createSection(worldName);
}
private static final class ConfigMigratedException extends RuntimeException {
private ConfigMigratedException() {
super("Config migrated");
}
}
}