Settings rework and encapsulation (#1330)

* Settings rework and encapsulation

* Settings overhaul 3

Move everything in the new system to a new API package

Require rules using the new system to specify descriptions in their translation files

Implement temporary system on top of current translations to allow that to work properly

* Fix getRules fallback

* Final touches to settings refactor

* Mention that set shouldn't throw and remove comment

* Change translation keys to unify with translation PR

* Deprecate printAllRulesToLog to allow using any PrintStream

* Remove restriction of CarpetRules being Comparable

And fix overreaction in a deprecation message

* Bring back user input to rule observers

* Make Validator deprecation warning not spam on use

* Remove unneeded abstract methods in CarpetRule already inherited from CarpetRule

They literally do nothing, don't even force them to be implemented, and may be confusing as to why they're there or as if they are needed to be implemented

* Fix legacy SettingsManager field not being assigned correctly

* Finish fixing conflicts from future merge

* Actually fix remaining conflicts from future merge (missing imports)

* Import the newer rule in SettingsManager

* Refactor Validators

* Don't spam deprecation warnings on custom managers

* Get out the var

* Fix missing warning in old rule observers

* Update src/main/java/carpet/api/settings/Rule.java

* Remove `@ApiStatus.Experimental` annotations

* Fix incorrect `@deprecated` javadoc tags

By removing them given the class is deprecated anyway and else there's too much noise

* Add missing static import

* Remove TypedField and use a regular field

I liked that class tbh, but it's true that it wasn't really needed

* Move `field` assignment out of for removal

* Fix not following contract of `type()` returning boxed value

Previously handled by TypedField

* Fix few incorrect translation keys

* Remove resaving of comments when saving config files

Comments ignoring is still a thing when reading, so `locked` configs for example will keep comments

* Condense rule load messages in one

* Only print loaded settings message when at least one setting was changed

* Fix legacy validator detector

* Tweak and add more context to response to invalid values

* Fix strict validator not actually erroring on invalid input
This commit is contained in:
altrisi authored and GitHub committed 2022-06-27 13:12:15 +02:00
1 parent f5a92ebb8e
commit 0a972d6c56
44 files changed
+2687 -1616

No files matched your search

@@ -0,0 +1,57 @@
package carpet.utils;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.TickTask;
import net.minecraft.server.level.ServerPlayer;
/**
* A few helpful methods to work with settings and commands.
*
* This is not any kind of API, but it's unlikely to change
*
*/
public final class CommandHelper {
private CommandHelper() {}
/**
* Notifies all players that the commands changed by resending the command tree.
*/
public static void notifyPlayersCommandsChanged(MinecraftServer server)
{
if (server == null || server.getPlayerList() == null)
{
return;
}
server.tell(new TickTask(server.getTickCount(), () ->
{
try {
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
server.getCommands().sendCommands(player);
}
}
catch (NullPointerException ignored) {}
}));
}
/**
* Whether the given source has enough permission level to run a command that requires the given commandLevel
*/
public static boolean canUseCommand(CommandSourceStack source, Object commandLevel)
{
if (commandLevel instanceof Boolean) return (Boolean) commandLevel;
String commandLevelString = commandLevel.toString();
switch (commandLevelString)
{
case "true": return true;
case "false": return false;
case "ops": return source.hasPermission(2); // typical for other cheaty commands
case "0":
case "1":
case "2":
case "3":
case "4":
return source.hasPermission(Integer.parseInt(commandLevelString));
}
return false;
}
}
@@ -0,0 +1,31 @@
package carpet.utils;
/**
* This is not public API!
*/
public final class TranslationKeys {
public static final String BASE_RULE_NAMESPACE = "%s.rule.";
public static final String BASE_RULE_PATTERN = BASE_RULE_NAMESPACE + "%s."; // [settingsManager].rule.[name]
public static final String RULE_NAME_PATTERN = BASE_RULE_PATTERN + "name";
public static final String RULE_DESC_PATTERN = BASE_RULE_PATTERN + "desc";
public static final String RULE_EXTRA_PREFIX_PATTERN = BASE_RULE_PATTERN + "extra.";
public static final String CATEGORY_PATTERN = "%s.category.%s"; //[settingsManager].category.[name]
// Settings command
private static final String SETTINGS_BASE = "carpet.settings.command.";
public static final String BROWSE_CATEGORIES = SETTINGS_BASE + "browse_categories";
public static final String VERSION = SETTINGS_BASE + "version";
public static final String LIST_ALL_CATEGORY = SETTINGS_BASE + "list_all_category";
public static final String CURRENT_SETTINGS_HEADER = SETTINGS_BASE + "current_settings_header";
public static final String SWITCH_TO = SETTINGS_BASE + "switch_to";
public static final String UNKNOWN_RULE = SETTINGS_BASE + "unknown_rule";
public static final String CURRENT_FROM_FILE_HEADER = SETTINGS_BASE + "current_from_file_header";
public static final String MOD_SETTINGS_MATCHING = SETTINGS_BASE + "mod_settings_matching";
public static final String ALL_MOD_SETTINGS = SETTINGS_BASE + "all_mod_settings";
public static final String TAGS = SETTINGS_BASE + "tags";
public static final String CHANGE_PERMANENTLY = SETTINGS_BASE + "change_permanently";
public static final String CHANGE_PERMANENTLY_HOVER = SETTINGS_BASE + "change_permanently_tooltip";
public static final String DEFAULT_SET = SETTINGS_BASE + "default_set";
public static final String DEFAULT_REMOVED = SETTINGS_BASE + "default_removed";
public static final String CURRENT_VALUE = SETTINGS_BASE + "current_value";
}
+66 -30
View File
@@ -10,33 +10,38 @@ import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import net.minecraft.commands.CommandSourceStack;
public class Translations
{
private static Map<String, String> translationMap;
private static Map<String, String> translationMap = Collections.emptyMap();
public static String tr(String key)
{
return translationMap == null ? key : translationMap.getOrDefault(key, key);
return translationMap.getOrDefault(key, key);
}
public static String trOrNull(String key)
{
return translationMap.get(key);
}
public static String tr(String key, String str)
{
return translationMap == null ? str : translationMap.getOrDefault(key, str);
return translationMap.getOrDefault(key, str);
}
public static boolean hasTranslations()
{
return translationMap != null;
return !translationMap.isEmpty();
}
public static boolean hasTranslation(String key)
{
return translationMap != null && translationMap.containsKey(key);
return translationMap.containsKey(key);
}
public static Map<String, String> getTranslationFromResourcePath(String path)
@@ -45,44 +50,59 @@ public class Translations
try
{
dataJSON = IOUtils.toString(
Objects.requireNonNull(Translations.class.getClassLoader().getResourceAsStream(String.format("assets/carpet/lang/%s.json", CarpetSettings.language))),
Objects.requireNonNull(Translations.class.getClassLoader().getResourceAsStream(path)),
StandardCharsets.UTF_8);
} catch (NullPointerException | IOException e) {
return null;
return Map.of();
}
Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
Gson gson = new GsonBuilder().setLenient().create();
return gson.fromJson(dataJSON, new TypeToken<Map<String, String>>() {}.getType());
}
public static void updateLanguage(CommandSourceStack source)
public static void updateLanguage()
{
if (CarpetSettings.language.equalsIgnoreCase("none"))
{
translationMap = null;
return;
}
Map<String, String> translations = new HashMap<>();
Map<String, String> trans = getTranslationFromResourcePath(String.format("assets/carpet/lang/%s.json", CarpetSettings.language));
if (trans != null) trans.forEach(translations::put);
translations.putAll(getTranslationFromResourcePath(String.format("assets/carpet/lang/%s.json", CarpetSettings.language)));
for (CarpetExtension ext : CarpetServer.extensions)
{
Map<String, String> extMappings = ext.canHasTranslations(CarpetSettings.language);
if (extMappings != null)
{
extMappings.forEach((key, value) ->
{
if (!translations.containsKey(key)) translations.put(key, value);
});
if (extMappings == null) continue; // would be nice to get rid of this, but too many extensions return null where they don't know they do
boolean warned = false;
for (var entry : extMappings.entrySet()) {
var key = entry.getKey();
// Migrate the old format
if (!key.startsWith("carpet.")) {
if (key.startsWith("rule.")) {
// default to carpet's settings manager. Custom managers are really uncommon and the known ones don't provide translations anyway
key = TranslationKeys.BASE_RULE_NAMESPACE.formatted("carpet") + key.substring(5);
} else if (key.startsWith("category.")) {
key = TranslationKeys.CATEGORY_PATTERN.formatted("carpet", key.substring(9));
}
if (!warned && key != entry.getKey()) {
CarpetSettings.LOG.warn("""
Found outdated translation keys in extension '%s'!
These won't be supported in a later Carpet version!
Carpet will now try to map them to the correct keys in a best-effort basis""".formatted(ext.getClass().getName()));
warned = true;
}
}
translations.putIfAbsent(key, entry.getValue());
}
}
translations.entrySet().removeIf(e -> e.getKey().startsWith("//"));
if (translations.isEmpty())
{
translationMap = null;
return;
}
translations.keySet().removeIf(e -> {
if (e.startsWith("//")) {
CarpetSettings.LOG.warn("""
Found translation key starting with // while preparing translations!
Doing this is deprecated and may cause issues in later versions! Consider settings GSON to "lenient" mode and
using regular comments instead!
Translation key is '%s'""".formatted(e));
return true;
} else
return false;
});
// Remove after deprecated settings api is removed
addFallbacksTo(translations);
translationMap = translations;
}
@@ -91,4 +111,20 @@ public class Translations
// will put some validations for availble languages at some point
return true;
}
// fallbacks for old rules that don't define rule descriptions or stuff in language files yet
// to be removed when old settings system is removed and translation refactor is finished
private static final Map<String, String> FALLBACKS = new HashMap<>();
/**
* @deprecated if you compile against this method I'll steal your kneecaps
*/
@Deprecated(forRemoval = true)
public static void registerFallbackTranslation(String key, String description) {
FALLBACKS.put(key, description);
}
private static void addFallbacksTo(Map<String, String> translationMap) {
FALLBACKS.forEach(translationMap::putIfAbsent);
}
}