feat: introduce migrating config nodes to new locations (#2642)

- Initial case of moving schematic limits from experimental to limits
 - Closes #2533
This commit is contained in:
Jordan 2024-05-01 17:31:06 +09:00 committed by GitHub
parent 1b2e2fe12a
commit debfabff08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 123 additions and 56 deletions

View File

@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.configuration;
import com.fastasyncworldedit.core.configuration.file.YamlConfiguration;
import com.fastasyncworldedit.core.util.StringMan;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import org.apache.logging.log4j.Logger;
@ -14,8 +15,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
@ -27,6 +30,9 @@ public class Config {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<String, Object> removedKeyVals = new HashMap<>();
private List<String> existingMigrateNodes = null;
public Config() {
save(new PrintWriter(new ByteArrayOutputStream(0)), getClass(), this, 0);
}
@ -43,7 +49,8 @@ public class Config {
try {
return (T) field.get(instance);
} catch (IllegalAccessException e) {
e.printStackTrace();
LOGGER.error("Failed to get config option: {}", key, e);
return null;
}
}
}
@ -67,6 +74,10 @@ public class Config {
if (field.getAnnotation(Final.class) != null) {
return;
}
Migrate migrate = field.getAnnotation(Migrate.class);
if (existingMigrateNodes != null && migrate != null) {
existingMigrateNodes.add(migrate.value());
}
if (field.getType() == String.class && !(value instanceof String)) {
value = value + "";
}
@ -74,17 +85,22 @@ public class Config {
field.set(instance, value);
return;
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Failed to set config option: {}", key);
}
}
}
LOGGER.error("Failed to set config option: {}: {} | {} | {}.yml", key, value, instance, root.getSimpleName());
removedKeyVals.put(key, value);
LOGGER.error(
"Failed to set config option: {}: {} | {} | {}.yml. This is likely because it was removed.",
key,
value,
instance,
root.getSimpleName()
);
}
public boolean load(File file) {
if (!file.exists()) {
return false;
}
existingMigrateNodes = new ArrayList<>();
YamlConfiguration yml = YamlConfiguration.loadConfiguration(file);
for (String key : yml.getKeys(true)) {
Object value = yml.get(key);
@ -93,6 +109,10 @@ public class Config {
}
set(key, value, getClass());
}
for (String node : existingMigrateNodes) {
removedKeyVals.remove(node);
}
existingMigrateNodes = null;
return true;
}
@ -113,7 +133,7 @@ public class Config {
save(writer, getClass(), instance, 0);
writer.close();
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Failed to save config file: {}", file, e);
}
}
@ -166,6 +186,19 @@ public class Config {
}
/**
* Indicates that a field should be instantiated / created.
*
* @since TODO
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Migrate {
String value();
}
@Ignore // This is not part of the config
public static class ConfigBlock<T> {
@ -222,7 +255,6 @@ public class Config {
try {
String CTRF = System.lineSeparator();
String spacing = StringMan.repeat(" ", indent);
HashMap<Class<?>, Object> instances = new HashMap<>();
for (Field field : clazz.getFields()) {
if (field.getAnnotation(Ignore.class) != null) {
continue;
@ -239,31 +271,14 @@ public class Config {
}
if (current == ConfigBlock.class) {
current = (Class<?>) ((ParameterizedType) (field.getGenericType())).getActualTypeArguments()[0];
comment = current.getAnnotation(Comment.class);
if (comment != null) {
for (String commentLine : comment.value()) {
writer.write(spacing + "# " + commentLine + CTRF);
}
}
BlockName blockNames = current.getAnnotation(BlockName.class);
if (blockNames != null) {
writer.write(spacing + toNodeName(current.getSimpleName()) + ":" + CTRF);
ConfigBlock configBlock = (ConfigBlock) field.get(instance);
if (configBlock == null || configBlock.getInstances().isEmpty()) {
configBlock = new ConfigBlock();
field.set(instance, configBlock);
for (String blockName : blockNames.value()) {
configBlock.put(blockName, current.getDeclaredConstructor().newInstance());
}
}
// Save each instance
for (Map.Entry<String, Object> entry : ((Map<String, Object>) configBlock.getRaw()).entrySet()) {
String key = entry.getKey();
writer.write(spacing + " " + toNodeName(key) + ":" + CTRF);
save(writer, current, entry.getValue(), indent + 4);
}
}
handleConfigBlockSave(writer, instance, indent, field, spacing, CTRF, current);
continue;
} else if (!removedKeyVals.isEmpty()) {
Migrate migrate = field.getAnnotation(Migrate.class);
Object value;
if (migrate != null && (value = removedKeyVals.remove(migrate.value())) != null) {
field.set(instance, value);
}
}
Create create = field.getAnnotation(Create.class);
if (create != null) {
@ -281,7 +296,6 @@ public class Config {
writer.write(spacing + toNodeName(current.getSimpleName()) + ":" + CTRF);
if (value == null) {
field.set(instance, value = current.getDeclaredConstructor().newInstance());
instances.put(current, value);
}
save(writer, current, value, indent + 2);
} else {
@ -292,7 +306,42 @@ public class Config {
}
}
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Failed to save config file", e);
}
}
private <T> void handleConfigBlockSave(
PrintWriter writer,
Object instance,
int indent,
Field field,
String spacing,
String CTRF,
Class<T> current
) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
Comment comment = current.getAnnotation(Comment.class);
if (comment != null) {
for (String commentLine : comment.value()) {
writer.write(spacing + "# " + commentLine + CTRF);
}
}
BlockName blockNames = current.getAnnotation(BlockName.class);
if (blockNames != null) {
writer.write(spacing + toNodeName(current.getSimpleName()) + ":" + CTRF);
ConfigBlock<T> configBlock = (ConfigBlock<T>) field.get(instance);
if (configBlock == null || configBlock.getInstances().isEmpty()) {
configBlock = new ConfigBlock<>();
field.set(instance, configBlock);
for (String blockName : blockNames.value()) {
configBlock.put(blockName, current.getDeclaredConstructor().newInstance());
}
}
// Save each instance
for (Map.Entry<String, T> entry : configBlock.getRaw().entrySet()) {
String key = entry.getKey();
writer.write(spacing + " " + toNodeName(key) + ":" + CTRF);
save(writer, current, entry.getValue(), indent + 4);
}
}
}
@ -311,7 +360,7 @@ public class Config {
return field;
} catch (Throwable ignored) {
LOGGER.warn(
"Invalid config field: {} for {}",
"Invalid config field: {} for {}. It is possible this is because it has been removed.",
StringMan.join(split, "."),
toNodeName(instance.getClass().getSimpleName())
);
@ -379,7 +428,7 @@ public class Config {
return null;
}
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Failed retrieving instance for config node: {}", StringUtil.joinString(split, "."), e);
}
return null;
}

View File

@ -145,6 +145,14 @@ public class Settings extends Config {
limit.MAX_HISTORY,
newLimit.MAX_HISTORY_MB != -1 ? newLimit.MAX_HISTORY_MB : Integer.MAX_VALUE
);
limit.SCHEM_FILE_NUM_LIMIT = Math.max(
limit.SCHEM_FILE_NUM_LIMIT,
newLimit.SCHEM_FILE_NUM_LIMIT != -1 ? newLimit.SCHEM_FILE_NUM_LIMIT : Integer.MAX_VALUE
);
limit.SCHEM_FILE_SIZE_LIMIT = Math.max(
limit.SCHEM_FILE_SIZE_LIMIT,
newLimit.SCHEM_FILE_SIZE_LIMIT != -1 ? newLimit.SCHEM_FILE_SIZE_LIMIT : Integer.MAX_VALUE
);
limit.MAX_EXPRESSION_MS = Math.max(
limit.MAX_EXPRESSION_MS,
newLimit.MAX_EXPRESSION_MS != -1 ? newLimit.MAX_EXPRESSION_MS : Integer.MAX_VALUE
@ -353,6 +361,18 @@ public class Settings extends Config {
" - History on disk or memory will be deleted",
})
public int MAX_HISTORY_MB = -1;
@Comment({
"Sets a maximum limit (in kb) for the size of a player's schematics directory (per-player mode only)",
"Set to -1 to disable"
})
@Migrate("experimental.per-player-file-size-limit")
public int SCHEM_FILE_SIZE_LIMIT = -1;
@Comment({
"Sets a maximum limit for the amount of schematics in a player's schematics directory (per-player mode only)",
"Set to -1 to disable"
})
@Migrate("experimental.per-player-file-num-limit")
public int SCHEM_FILE_NUM_LIMIT = -1;
@Comment("Maximum time in milliseconds //calc can execute")
public int MAX_EXPRESSION_MS = 50;
@Comment({
@ -615,18 +635,6 @@ public class Settings extends Config {
})
public boolean ALLOW_TICK_FLUIDS = false;
@Comment({
"Sets a maximum limit (in kb) for the size of a player's schematics directory (per-player mode only)",
"Set to -1 to disable"
})
public int PER_PLAYER_FILE_SIZE_LIMIT = -1;
@Comment({
"Sets a maximum limit for the amount of schematics in a player's schematics directory (per-player mode only)",
"Set to -1 to disable"
})
public int PER_PLAYER_FILE_NUM_LIMIT = -1;
}
@Comment({"Web/HTTP connection related settings"})

View File

@ -15,6 +15,8 @@ public class FaweLimit {
public int MAX_BLOCKSTATES = 0;
public int MAX_ENTITIES = 0;
public int MAX_HISTORY = 0;
public int SCHEM_FILE_SIZE_LIMIT = 0;
public int SCHEM_FILE_NUM_LIMIT = 0;
public int MAX_EXPRESSION_MS = 0;
public int INVENTORY_MODE = Integer.MAX_VALUE;
public int SPEED_REDUCTION = Integer.MAX_VALUE;
@ -111,6 +113,8 @@ public class FaweLimit {
MAX.MAX_BLOCKSTATES = Integer.MAX_VALUE;
MAX.MAX_ENTITIES = Integer.MAX_VALUE;
MAX.MAX_HISTORY = Integer.MAX_VALUE;
MAX.SCHEM_FILE_NUM_LIMIT = Integer.MAX_VALUE;
MAX.SCHEM_FILE_SIZE_LIMIT = Integer.MAX_VALUE;
MAX.MAX_EXPRESSION_MS = 50;
MAX.FAST_PLACEMENT = true;
MAX.CONFIRM_LARGE = true;
@ -237,6 +241,8 @@ public class FaweLimit {
&& MAX_BLOCKSTATES == Integer.MAX_VALUE
&& MAX_ENTITIES == Integer.MAX_VALUE
&& MAX_HISTORY == Integer.MAX_VALUE
&& SCHEM_FILE_SIZE_LIMIT == Integer.MAX_VALUE
&& SCHEM_FILE_NUM_LIMIT == Integer.MAX_VALUE
&& INVENTORY_MODE == 0
&& SPEED_REDUCTION == 0
&& FAST_PLACEMENT
@ -256,6 +262,8 @@ public class FaweLimit {
MAX_FAILS = limit.MAX_FAILS;
MAX_ITERATIONS = limit.MAX_ITERATIONS;
MAX_HISTORY = limit.MAX_HISTORY;
SCHEM_FILE_NUM_LIMIT = limit.SCHEM_FILE_NUM_LIMIT;
SCHEM_FILE_SIZE_LIMIT = limit.SCHEM_FILE_SIZE_LIMIT;
INVENTORY_MODE = limit.INVENTORY_MODE;
SPEED_REDUCTION = limit.SPEED_REDUCTION;
FAST_PLACEMENT = limit.FAST_PLACEMENT;
@ -279,6 +287,8 @@ public class FaweLimit {
limit.MAX_FAILS = MAX_FAILS;
limit.MAX_ITERATIONS = MAX_ITERATIONS;
limit.MAX_HISTORY = MAX_HISTORY;
limit.SCHEM_FILE_SIZE_LIMIT = SCHEM_FILE_SIZE_LIMIT;
limit.SCHEM_FILE_NUM_LIMIT = SCHEM_FILE_NUM_LIMIT;
limit.FAST_PLACEMENT = FAST_PLACEMENT;
limit.CONFIRM_LARGE = CONFIRM_LARGE;
limit.RESTRICT_HISTORY_TO_REGIONS = RESTRICT_HISTORY_TO_REGIONS;

View File

@ -700,10 +700,10 @@ public class SchematicCommands {
String headerBytesElem = String.format("%.1fkb", totalBytes / 1000.0);
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT > -1) {
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && actor.getLimit().SCHEM_FILE_SIZE_LIMIT > -1) {
headerBytesElem += String.format(
" / %dkb",
Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT
actor.getLimit().SCHEM_FILE_SIZE_LIMIT
);
}
@ -837,7 +837,7 @@ public class SchematicCommands {
//FAWE start
boolean checkFilesize = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS
&& Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT > -1;
&& actor.getLimit().SCHEM_FILE_SIZE_LIMIT > -1;
double directorysizeKb = 0;
String curFilepath = file.getAbsolutePath();
@ -867,7 +867,7 @@ public class SchematicCommands {
}
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT > -1) {
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && actor.getLimit().SCHEM_FILE_NUM_LIMIT > -1) {
if (numFiles == -1) {
numFiles = 0;
@ -880,7 +880,7 @@ public class SchematicCommands {
}
}
}
int limit = Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT;
int limit = actor.getLimit().SCHEM_FILE_NUM_LIMIT;
if (numFiles >= limit) {
TextComponent noSlotsErr = TextComponent.of( //TODO - to be moved into captions/translatablecomponents
@ -931,7 +931,7 @@ public class SchematicCommands {
if (checkFilesize) {
double curKb = filesizeKb + directorysizeKb;
int allocatedKb = Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT;
int allocatedKb = actor.getLimit().SCHEM_FILE_SIZE_LIMIT;
if (overwrite) {
curKb -= oldKbOverwritten;
@ -966,11 +966,11 @@ public class SchematicCommands {
actor.print(kbRemainingNotif);
}
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT > -1) {
if (Settings.settings().PATHS.PER_PLAYER_SCHEMATICS && actor.getLimit().SCHEM_FILE_NUM_LIMIT > -1) {
TextComponent slotsRemainingNotif = TextComponent.of(
//TODO - to be moved into captions/translatablecomponents
"You have " + (Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_NUM_LIMIT - numFiles)
"You have " + (actor.getLimit().SCHEM_FILE_NUM_LIMIT - numFiles)
+ " schematic file slots left.",
TextColor.GRAY
);