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.configuration.file.YamlConfiguration;
import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.util.StringMan;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.LogManagerCompat;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -14,8 +15,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -27,6 +30,9 @@ public class Config {
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private final Map<String, Object> removedKeyVals = new HashMap<>();
private List<String> existingMigrateNodes = null;
public Config() { public Config() {
save(new PrintWriter(new ByteArrayOutputStream(0)), getClass(), this, 0); save(new PrintWriter(new ByteArrayOutputStream(0)), getClass(), this, 0);
} }
@ -43,7 +49,8 @@ public class Config {
try { try {
return (T) field.get(instance); return (T) field.get(instance);
} catch (IllegalAccessException e) { } 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) { if (field.getAnnotation(Final.class) != null) {
return; return;
} }
Migrate migrate = field.getAnnotation(Migrate.class);
if (existingMigrateNodes != null && migrate != null) {
existingMigrateNodes.add(migrate.value());
}
if (field.getType() == String.class && !(value instanceof String)) { if (field.getType() == String.class && !(value instanceof String)) {
value = value + ""; value = value + "";
} }
@ -74,17 +85,22 @@ public class Config {
field.set(instance, value); field.set(instance, value);
return; return;
} catch (Throwable e) { } 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) { public boolean load(File file) {
if (!file.exists()) { existingMigrateNodes = new ArrayList<>();
return false;
}
YamlConfiguration yml = YamlConfiguration.loadConfiguration(file); YamlConfiguration yml = YamlConfiguration.loadConfiguration(file);
for (String key : yml.getKeys(true)) { for (String key : yml.getKeys(true)) {
Object value = yml.get(key); Object value = yml.get(key);
@ -93,6 +109,10 @@ public class Config {
} }
set(key, value, getClass()); set(key, value, getClass());
} }
for (String node : existingMigrateNodes) {
removedKeyVals.remove(node);
}
existingMigrateNodes = null;
return true; return true;
} }
@ -113,7 +133,7 @@ public class Config {
save(writer, getClass(), instance, 0); save(writer, getClass(), instance, 0);
writer.close(); writer.close();
} catch (Throwable e) { } 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 @Ignore // This is not part of the config
public static class ConfigBlock<T> { public static class ConfigBlock<T> {
@ -222,7 +255,6 @@ public class Config {
try { try {
String CTRF = System.lineSeparator(); String CTRF = System.lineSeparator();
String spacing = StringMan.repeat(" ", indent); String spacing = StringMan.repeat(" ", indent);
HashMap<Class<?>, Object> instances = new HashMap<>();
for (Field field : clazz.getFields()) { for (Field field : clazz.getFields()) {
if (field.getAnnotation(Ignore.class) != null) { if (field.getAnnotation(Ignore.class) != null) {
continue; continue;
@ -239,31 +271,14 @@ public class Config {
} }
if (current == ConfigBlock.class) { if (current == ConfigBlock.class) {
current = (Class<?>) ((ParameterizedType) (field.getGenericType())).getActualTypeArguments()[0]; current = (Class<?>) ((ParameterizedType) (field.getGenericType())).getActualTypeArguments()[0];
comment = current.getAnnotation(Comment.class); handleConfigBlockSave(writer, instance, indent, field, spacing, CTRF, current);
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);
}
}
continue; 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); Create create = field.getAnnotation(Create.class);
if (create != null) { if (create != null) {
@ -281,7 +296,6 @@ public class Config {
writer.write(spacing + toNodeName(current.getSimpleName()) + ":" + CTRF); writer.write(spacing + toNodeName(current.getSimpleName()) + ":" + CTRF);
if (value == null) { if (value == null) {
field.set(instance, value = current.getDeclaredConstructor().newInstance()); field.set(instance, value = current.getDeclaredConstructor().newInstance());
instances.put(current, value);
} }
save(writer, current, value, indent + 2); save(writer, current, value, indent + 2);
} else { } else {
@ -292,7 +306,42 @@ public class Config {
} }
} }
} catch (Throwable e) { } 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; return field;
} catch (Throwable ignored) { } catch (Throwable ignored) {
LOGGER.warn( LOGGER.warn(
"Invalid config field: {} for {}", "Invalid config field: {} for {}. It is possible this is because it has been removed.",
StringMan.join(split, "."), StringMan.join(split, "."),
toNodeName(instance.getClass().getSimpleName()) toNodeName(instance.getClass().getSimpleName())
); );
@ -379,7 +428,7 @@ public class Config {
return null; return null;
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); LOGGER.error("Failed retrieving instance for config node: {}", StringUtil.joinString(split, "."), e);
} }
return null; return null;
} }

View File

@ -145,6 +145,14 @@ public class Settings extends Config {
limit.MAX_HISTORY, limit.MAX_HISTORY,
newLimit.MAX_HISTORY_MB != -1 ? newLimit.MAX_HISTORY_MB : Integer.MAX_VALUE 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 = Math.max(
limit.MAX_EXPRESSION_MS, limit.MAX_EXPRESSION_MS,
newLimit.MAX_EXPRESSION_MS != -1 ? newLimit.MAX_EXPRESSION_MS : Integer.MAX_VALUE 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", " - History on disk or memory will be deleted",
}) })
public int MAX_HISTORY_MB = -1; 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") @Comment("Maximum time in milliseconds //calc can execute")
public int MAX_EXPRESSION_MS = 50; public int MAX_EXPRESSION_MS = 50;
@Comment({ @Comment({
@ -615,18 +635,6 @@ public class Settings extends Config {
}) })
public boolean ALLOW_TICK_FLUIDS = false; 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"}) @Comment({"Web/HTTP connection related settings"})

View File

@ -15,6 +15,8 @@ public class FaweLimit {
public int MAX_BLOCKSTATES = 0; public int MAX_BLOCKSTATES = 0;
public int MAX_ENTITIES = 0; public int MAX_ENTITIES = 0;
public int MAX_HISTORY = 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 MAX_EXPRESSION_MS = 0;
public int INVENTORY_MODE = Integer.MAX_VALUE; public int INVENTORY_MODE = Integer.MAX_VALUE;
public int SPEED_REDUCTION = 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_BLOCKSTATES = Integer.MAX_VALUE;
MAX.MAX_ENTITIES = Integer.MAX_VALUE; MAX.MAX_ENTITIES = Integer.MAX_VALUE;
MAX.MAX_HISTORY = 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.MAX_EXPRESSION_MS = 50;
MAX.FAST_PLACEMENT = true; MAX.FAST_PLACEMENT = true;
MAX.CONFIRM_LARGE = true; MAX.CONFIRM_LARGE = true;
@ -237,6 +241,8 @@ public class FaweLimit {
&& MAX_BLOCKSTATES == Integer.MAX_VALUE && MAX_BLOCKSTATES == Integer.MAX_VALUE
&& MAX_ENTITIES == Integer.MAX_VALUE && MAX_ENTITIES == Integer.MAX_VALUE
&& MAX_HISTORY == 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 && INVENTORY_MODE == 0
&& SPEED_REDUCTION == 0 && SPEED_REDUCTION == 0
&& FAST_PLACEMENT && FAST_PLACEMENT
@ -256,6 +262,8 @@ public class FaweLimit {
MAX_FAILS = limit.MAX_FAILS; MAX_FAILS = limit.MAX_FAILS;
MAX_ITERATIONS = limit.MAX_ITERATIONS; MAX_ITERATIONS = limit.MAX_ITERATIONS;
MAX_HISTORY = limit.MAX_HISTORY; 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; INVENTORY_MODE = limit.INVENTORY_MODE;
SPEED_REDUCTION = limit.SPEED_REDUCTION; SPEED_REDUCTION = limit.SPEED_REDUCTION;
FAST_PLACEMENT = limit.FAST_PLACEMENT; FAST_PLACEMENT = limit.FAST_PLACEMENT;
@ -279,6 +287,8 @@ public class FaweLimit {
limit.MAX_FAILS = MAX_FAILS; limit.MAX_FAILS = MAX_FAILS;
limit.MAX_ITERATIONS = MAX_ITERATIONS; limit.MAX_ITERATIONS = MAX_ITERATIONS;
limit.MAX_HISTORY = MAX_HISTORY; 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.FAST_PLACEMENT = FAST_PLACEMENT;
limit.CONFIRM_LARGE = CONFIRM_LARGE; limit.CONFIRM_LARGE = CONFIRM_LARGE;
limit.RESTRICT_HISTORY_TO_REGIONS = RESTRICT_HISTORY_TO_REGIONS; 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); 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( headerBytesElem += String.format(
" / %dkb", " / %dkb",
Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT actor.getLimit().SCHEM_FILE_SIZE_LIMIT
); );
} }
@ -837,7 +837,7 @@ public class SchematicCommands {
//FAWE start //FAWE start
boolean checkFilesize = Settings.settings().PATHS.PER_PLAYER_SCHEMATICS 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; double directorysizeKb = 0;
String curFilepath = file.getAbsolutePath(); 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) { if (numFiles == -1) {
numFiles = 0; 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) { if (numFiles >= limit) {
TextComponent noSlotsErr = TextComponent.of( //TODO - to be moved into captions/translatablecomponents TextComponent noSlotsErr = TextComponent.of( //TODO - to be moved into captions/translatablecomponents
@ -931,7 +931,7 @@ public class SchematicCommands {
if (checkFilesize) { if (checkFilesize) {
double curKb = filesizeKb + directorysizeKb; double curKb = filesizeKb + directorysizeKb;
int allocatedKb = Settings.settings().EXPERIMENTAL.PER_PLAYER_FILE_SIZE_LIMIT; int allocatedKb = actor.getLimit().SCHEM_FILE_SIZE_LIMIT;
if (overwrite) { if (overwrite) {
curKb -= oldKbOverwritten; curKb -= oldKbOverwritten;
@ -966,11 +966,11 @@ public class SchematicCommands {
actor.print(kbRemainingNotif); 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( TextComponent slotsRemainingNotif = TextComponent.of(
//TODO - to be moved into captions/translatablecomponents //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.", + " schematic file slots left.",
TextColor.GRAY TextColor.GRAY
); );