From 1e584842461cca9b53e6c5fbfe85e06f62e1c414 Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Thu, 19 Oct 2023 19:23:04 +0200 Subject: [PATCH] - README.md changes - Optimized pack storing, should use less resources now - general optimizations --- README.md | 8 +++- .../extension/pickpack/{Util => }/Form.java | 39 ++++++++------- .../geyser/extension/pickpack/PickPack.java | 39 +++++++++------ .../extension/pickpack/Util/ConfigLoader.java | 1 - .../extension/pickpack/Util/FileSaveUtil.java | 45 +++++++++--------- .../pickpack/Util/LanguageManager.java | 21 +++++---- .../pickpack/Util/PlayerStorage.java | 41 ++++++++++------ .../pickpack/Util/ResourcePackLoader.java | 47 +++++++++---------- src/main/resources/en_US.properties | 10 ++-- 9 files changed, 140 insertions(+), 111 deletions(-) rename src/main/java/net/onebeastchris/geyser/extension/pickpack/{Util => }/Form.java (81%) diff --git a/README.md b/README.md index 3bfb5da..fff5df9 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,15 @@ Download available in the releases tab. ### Usage: - Configure the ip/port in the config.yml. The file is located under /Geyser/extensions/PickPack/config.yml. These are needed so the automatic transfer packet works. -- Place packs that players can opt out of in the "optOut" folder. (located in Geyser/extensions/PickPack) -- Place packs that players can opt in to in the "optIn" folder. (located in Geyser/extensions/PickPack) +- Place packs that are applied by default go in the "defaultPacks" folder. (located in Geyser/extensions/PickPack/defaultPacks) +- Place packs that players can optionally turn on go in the "optionalPacks" folder. (located in Geyser/extensions/optionalPacks) - Reload the extension using `/pickpack reload`, or restart the server. ### Commands: - `/pickpack menu` or `/pickpack list` - Opens the pack selection menu. - `/pickpack reset` or `/pickpack default` - Resets the player's pack selection to the default packs. - `/pickpack reload` - Reloads the config.yml and the packs. + +### Languages: +This extension has multi-language support. However, the default language is English - to add more languages, copy the `en_US.properties` file from the /extensions/PickPack/translations folder, and translate the strings. +Then, save the file as e.g. `de_DE.properties` for German. You can select a default locale in the config.yml. Otherwise, all locales will be loaded & used automatically if a player's language matches. diff --git a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/Form.java b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Form.java similarity index 81% rename from src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/Form.java rename to src/main/java/net/onebeastchris/geyser/extension/pickpack/Form.java index 8b291f3..c909047 100644 --- a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/Form.java +++ b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Form.java @@ -1,11 +1,11 @@ -package net.onebeastchris.geyser.extension.pickpack.Util; +package net.onebeastchris.geyser.extension.pickpack; -import net.onebeastchris.geyser.extension.pickpack.PickPack; +import net.onebeastchris.geyser.extension.pickpack.Util.LanguageManager; import org.geysermc.cumulus.component.ToggleComponent; import org.geysermc.cumulus.form.CustomForm; import org.geysermc.cumulus.form.ModalForm; import org.geysermc.geyser.api.connection.GeyserConnection; -import org.geysermc.geyser.api.pack.ResourcePack; +import org.geysermc.geyser.api.pack.ResourcePackManifest; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; @@ -41,7 +41,7 @@ public void send(String... args) { return; } case "clear" -> { - CompletableFuture future = PickPack.storage.setPacks(xuid, new ArrayList<>(loader.OPT_OUT.values())); + CompletableFuture future = PickPack.storage.setPacks(xuid, new ArrayList<>(loader.OPTIONAL.keySet())); future.thenRun(() -> handle(config.useTransferPacket())); return; } @@ -53,7 +53,7 @@ public void send(String... args) { .button1(LanguageManager.getLocaleString(lang, "main.menu.filter")) .button2(LanguageManager.getLocaleString(lang, "main.menu.select")); - form.validResultHandler((modalform, response) -> { + form.validResultHandler((modalForm, response) -> { switch (response.clickedButtonId()) { case 0 -> filterForm(); case 1 -> packsForm(config.useTransferPacket(), config.showPackDescription(), Filter.ALL); @@ -66,8 +66,8 @@ public void filterForm() { CustomForm.Builder form = CustomForm.builder() .title(LanguageManager.getLocaleString(lang, "filter.form.title")); - if (PickPack.storage.getPacks(connection.xuid()).isEmpty()) { - form.dropdown(LanguageManager.getLocaleString(lang, "filter.button.name"), LanguageManager.getLocaleString(lang, "filter.all.packs"), LanguageManager.getLocaleString(lang, "filter.all.packs")); + if (PickPack.storage.getPackIds(connection.xuid()).isEmpty()) { + form.dropdown(LanguageManager.getLocaleString(lang, "filter.button.name"), LanguageManager.getLocaleString(lang, "filter.all.packs")); } else { form.dropdown(LanguageManager.getLocaleString(lang, "filter.button.name"), LanguageManager.getLocaleString(lang, "filter.all.packs"), @@ -81,6 +81,7 @@ public void filterForm() { form.validResultHandler((customform, response) -> { int filterResult = response.asDropdown(0); boolean description = response.asToggle(1); + // 2 is the label boolean transfer = response.asToggle(3); switch (filterResult) { @@ -98,16 +99,18 @@ public void packsForm(boolean transferPacket, boolean description, Filter filter CustomForm.Builder form = CustomForm.builder() .title(LanguageManager.getLocaleString(lang, "pack.form.title")); - form.label(LanguageManager.getLocaleString(lang, "pack.form.label").replace("%filter%", getFilterType(filter))); + form.label(String.format(LanguageManager.getLocaleString(lang, "pack.form.label"), getFilterType(filter))); - for (Map.Entry entry : loader.PACKS_INFO.entrySet()) { - String name = entry.getValue()[0]; + for (Map.Entry entry : loader.PACKS_INFO.entrySet()) { + String name = entry.getValue().header().name(); boolean currentlyApplied = PickPack.storage.hasSpecificPack(xuid, entry.getKey()); - boolean isVisible = filter.equals(Filter.ALL) || (filter.equals(Filter.APPLIED) && currentlyApplied) || (filter.equals(Filter.NOT_APPLIED) && !currentlyApplied); + boolean isVisible = filter.equals(Filter.ALL) || + (filter.equals(Filter.APPLIED) && currentlyApplied) || + (filter.equals(Filter.NOT_APPLIED) && !currentlyApplied); if (isVisible) { form.toggle(name, currentlyApplied); - if (description) form.label(ChatColor.ITALIC + entry.getValue()[1] + ChatColor.RESET); - tempMap.put(entry.getValue()[0], entry.getKey()); //makes it easier to get the uuid from the name later on + if (description) form.label(ChatColor.ITALIC + entry.getValue().header().description() + ChatColor.RESET); + tempMap.put(entry.getValue().header().name(), entry.getKey()); //makes it easier to get the uuid from the name later on } } @@ -116,19 +119,19 @@ public void packsForm(boolean transferPacket, boolean description, Filter filter }); form.validResultHandler((customform, response) -> { - List playerPacks = new ArrayList<>(); + List playerPacks = new ArrayList<>(); customform.content().forEach((component) -> { if (component instanceof ToggleComponent) { if (Boolean.TRUE.equals(response.next())) { String uuid = tempMap.get(component.text()); - playerPacks.add(loader.getPack(uuid)); + playerPacks.add(uuid); } } }); if (filter.equals(Filter.NOT_APPLIED)) { //keep the old packs if we are filtering for not applied packs - playerPacks.addAll(PickPack.storage.getPacks(xuid)); + playerPacks.addAll(PickPack.storage.getPackIds(xuid)); } CompletableFuture future = PickPack.storage.setPacks(xuid, playerPacks); @@ -141,8 +144,8 @@ public void packsForm(boolean transferPacket, boolean description, Filter filter private String getPacks(String xuid) { StringBuilder packs = new StringBuilder(); - for (ResourcePack pack : PickPack.storage.getPacks(xuid)) { - String name = pack.manifest().header().name(); + for (String packId : PickPack.storage.getPackIds(xuid)) { + String name = loader.PACKS_INFO.get(packId).header().name(); packs.append(" - ").append(name).append("\n"); } if (packs.length() == 0) packs.append(LanguageManager.getLocaleString(lang, "no_packs.warning")); diff --git a/src/main/java/net/onebeastchris/geyser/extension/pickpack/PickPack.java b/src/main/java/net/onebeastchris/geyser/extension/pickpack/PickPack.java index 257ec71..dbf7527 100644 --- a/src/main/java/net/onebeastchris/geyser/extension/pickpack/PickPack.java +++ b/src/main/java/net/onebeastchris/geyser/extension/pickpack/PickPack.java @@ -13,20 +13,19 @@ import org.geysermc.geyser.api.pack.ResourcePack; import org.geysermc.geyser.command.GeyserCommandSource; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.List; public class PickPack implements Extension { public static ResourcePackLoader loader; public static PlayerStorage storage; public static Path storagePath; - public static ConfigLoader.Config config; public static ExtensionLogger logger; - private Path optInPath; - private Path optOutPath; - @Subscribe public void onPreInitialize(GeyserPreInitializeEvent event) { // need to load config early so commands can get their translations @@ -46,8 +45,9 @@ public void onPreInitialize(GeyserPreInitializeEvent event) { } try { - LanguageManager.init(this.dataFolder().resolve("lang")); + LanguageManager.init(this.dataFolder().resolve("translations")); } catch (Exception e) { + e.printStackTrace(); this.disable(); throw new RuntimeException("Failed to load language files!", e); } @@ -55,17 +55,31 @@ public void onPreInitialize(GeyserPreInitializeEvent event) { @Subscribe public void onPostInitialize(GeyserPostInitializeEvent event) { - optInPath = this.dataFolder().resolve("optIn"); - optOutPath = this.dataFolder().resolve("optOut"); + Path optionalPacksPath = this.dataFolder().resolve("OptionalPacks"); + Path defaultPacksPath = this.dataFolder().resolve("DefaultPacks"); storagePath = this.dataFolder().resolve("cache"); + try { + // Replace opt-in and opt-out directories with new ones + if (this.dataFolder().resolve("optIn").toFile().exists()) { + Files.move(this.dataFolder().resolve("optIn"), optionalPacksPath, StandardCopyOption.REPLACE_EXISTING); + } + if (this.dataFolder().resolve("optOut").toFile().exists()) { + Files.move(this.dataFolder().resolve("optOut"), defaultPacksPath, StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + logger.error("Unable to migrate optIn and optOut directories!" + e.getMessage()); + e.printStackTrace(); + this.disable(); + } + logger = this.logger(); - FileSaveUtil.makeDir(optInPath, "opt-in-packs"); - FileSaveUtil.makeDir(optOutPath, "opt-out-packs"); + FileSaveUtil.makeDir(optionalPacksPath, "optional packs directory"); + FileSaveUtil.makeDir(defaultPacksPath, "default packs directory"); FileSaveUtil.makeDir(storagePath, "storage"); - loader = new ResourcePackLoader(optOutPath, optInPath); + loader = new ResourcePackLoader(optionalPacksPath, defaultPacksPath); storage = new PlayerStorage(); logger.info("PickPack extension loaded!"); @@ -83,9 +97,6 @@ public void onPlayerResourcePackLoadEvent(SessionLoadResourcePacksEvent event) { @Subscribe public void CommandEvent(GeyserDefineCommandsEvent commandsEvent) { - if (!this.isEnabled()) { - return; - } commandsEvent.register(Command.builder(this) .name("menu") .aliases(List.of("list")) @@ -125,7 +136,7 @@ public void CommandEvent(GeyserDefineCommandsEvent commandsEvent) { .suggestedOpOnly(true) .permission(config.reloadPermission()) .executor((source, command, args) -> { - loader.reload(optOutPath, optInPath); + loader.reload(); try { config = ConfigLoader.load(this, this.getClass(), ConfigLoader.Config.class); assert config != null; diff --git a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ConfigLoader.java b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ConfigLoader.java index 96aee0d..a2f19f4 100644 --- a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ConfigLoader.java +++ b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ConfigLoader.java @@ -67,7 +67,6 @@ public record Config( @JsonProperty("use-transfer-packet") boolean useTransferPacket, @JsonProperty("menu-permission") String menuPermission, @JsonProperty("default-permission") String defaultPermission, - @JsonProperty("default-locale") String defaultLocale, @JsonProperty("reload-permission") String reloadPermission, @JsonProperty("translations") Translations translations diff --git a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/FileSaveUtil.java b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/FileSaveUtil.java index 85f4d4e..245355a 100644 --- a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/FileSaveUtil.java +++ b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/FileSaveUtil.java @@ -7,39 +7,42 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import static net.onebeastchris.geyser.extension.pickpack.PickPack.loader; import static net.onebeastchris.geyser.extension.pickpack.PickPack.logger; public class FileSaveUtil { - @SuppressWarnings("ResultOfMethodCallIgnored") - public static void save(List list, String xuid) { + + public static void save(List list, String xuid) { Path filepath = PickPack.storagePath.resolve(xuid + ".txt"); - if (filepath.toFile().exists()) { - filepath.toFile().delete(); - } - List packUUIDs = new ArrayList<>(); - for (ResourcePack pack : list) { - packUUIDs.add(pack.manifest().header().uuid().toString()); - } - saveToFile(packUUIDs, filepath); + saveToFile(list, filepath); } - public static List load(Path filepath) { - List packUUIDs = readFromFile(filepath); - List list = new ArrayList<>(); - for (String pack : packUUIDs) { - ResourcePack resourcePack = loader.getPack(pack); - if (resourcePack != null) { - list.add(resourcePack); - } else { - logger.debug("Could not find pack with UUID " + pack + " in either folder! We are not loading it."); - } + public static List load(Path filepath) { + List packs = readFromFile(filepath); + AtomicBoolean changed = new AtomicBoolean(false); + packs.forEach(packId -> { + ResourcePack pack = loader.getPack(packId); + if (pack == null) { + logger.debug("Could not find pack with UUID " + packId + " in cache, removing from file"); + packs.remove(packId); + changed.set(true); } - return list; + }); + if (changed.get()) { + saveToFile(packs, filepath); + } + return packs; } public static void saveToFile(List packs, Path filepath) { + if (filepath.toFile().exists()) { + if (!filepath.toFile().delete()) { + logger.error("Failed to delete " + filepath.getFileName()); + return; + } + } try (BufferedWriter writer = new BufferedWriter(new FileWriter(filepath.toFile()))) { for (String pack : packs) { writer.write(pack); diff --git a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/LanguageManager.java b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/LanguageManager.java index 8308be1..ad241bd 100644 --- a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/LanguageManager.java +++ b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/LanguageManager.java @@ -11,9 +11,8 @@ import java.util.*; public class LanguageManager { - - private static final String EN_US_PROPERTIES = "en_us.properties"; - public static String DEFAULT_LOCALE; + public static String DEFAULT_LOCALE = "en_us"; + public static final String EN_US_PROPERTIES = "en_US.properties"; public static Map LOCALE_PROPERTIES = new HashMap<>(); @SuppressWarnings("resource") @@ -33,15 +32,17 @@ public static void init(Path languageFolder) throws IOException { throw new RuntimeException("Failed to list language files!", e); } - DEFAULT_LOCALE = PickPack.config.defaultLocale() == null ? EN_US_PROPERTIES : PickPack.config.defaultLocale() + ".properties"; + if (PickPack.config.defaultLocale() != null) { + DEFAULT_LOCALE = PickPack.config.defaultLocale().replace(".properties", ""); + } + + //Check: Does default locale exist? Fallback to en_us if it does not. + if (languageFiles.stream().noneMatch(path -> path.getFileName().toString().equalsIgnoreCase(DEFAULT_LOCALE + ".properties"))) { - //Check: Does english exist? - String currentDefaultLocale = DEFAULT_LOCALE; - if (languageFiles.stream().noneMatch(path -> path.getFileName().toString().equalsIgnoreCase(currentDefaultLocale))) { // Check: Is default locale not english? - if (!DEFAULT_LOCALE.equalsIgnoreCase(EN_US_PROPERTIES)) { + if (!DEFAULT_LOCALE.equalsIgnoreCase("en_us")) { PickPack.logger.warning("Default configured locale " + DEFAULT_LOCALE + " not found, falling back to en_us.properties"); - DEFAULT_LOCALE = EN_US_PROPERTIES; + DEFAULT_LOCALE = "en_us"; } try (InputStream input = PickPack.class.getClassLoader().getResourceAsStream(EN_US_PROPERTIES)) { @@ -69,6 +70,8 @@ public static void init(Path languageFolder) throws IOException { localeProp.load(reader); } catch (Exception e) { throw new AssertionError("Failed to load locale " + fileName); + } finally { + localeStream.close(); } // Insert the locale into the mappings, all lowercase diff --git a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/PlayerStorage.java b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/PlayerStorage.java index 394d6c8..4f368e3 100644 --- a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/PlayerStorage.java +++ b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/PlayerStorage.java @@ -17,7 +17,10 @@ import static net.onebeastchris.geyser.extension.pickpack.PickPack.logger; public class PlayerStorage { - public Map> cache; + /** + * This is a map of XUIDs to a list of resource packs. + */ + public Map> cache; public PlayerStorage() { cache = new HashMap<>(); @@ -27,13 +30,8 @@ public PlayerStorage() { } } - public CompletableFuture setPacks(String xuid, List packs) { + public CompletableFuture setPacks(String xuid, List packs) { cache.put(xuid, packs); - StringBuilder packsString = new StringBuilder(); - for (ResourcePack pack : packs) { - packsString.append(pack.manifest().header().name()).append(" "); - } - logger.debug("Saving packs for " + xuid + ": " + packsString); Executors.newSingleThreadExecutor().execute(() -> FileSaveUtil.save(packs, xuid) ); @@ -41,25 +39,38 @@ public CompletableFuture setPacks(String xuid, List packs) { } public @NonNull List getPacks(String xuid) { + if (cache.containsKey(xuid)) { + List packs = new ArrayList<>(); + cache.get(xuid).forEach(pack -> { + ResourcePack resourcePack = loader.getPack(pack); + if (resourcePack != null) { + packs.add(resourcePack); + } + }); + return packs; + } else { + return new ArrayList<>(loader.DEFAULT.values()); + } + } + + public @NonNull List getPackIds(String xuid) { if (cache.containsKey(xuid)) { return cache.get(xuid); } else { - return new ArrayList<>(loader.OPT_OUT.values()); + return new ArrayList<>(loader.DEFAULT.keySet()); } } public boolean hasSpecificPack(String xuid, String uuid) { if (cache.containsKey(xuid)) { - boolean hasPack = false; - for (ResourcePack pack : cache.get(xuid)) { - if (pack.manifest().header().uuid().toString().equals(uuid)) { - hasPack = true; - break; + for (String pack : cache.get(xuid)) { + if (pack.equals(uuid)) { + return true; } } - return hasPack; + return false; } else { - return loader.OPT_OUT.containsKey(uuid); + return loader.DEFAULT.containsKey(uuid); } } } diff --git a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ResourcePackLoader.java b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ResourcePackLoader.java index f3cb953..7bc9c5a 100644 --- a/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ResourcePackLoader.java +++ b/src/main/java/net/onebeastchris/geyser/extension/pickpack/Util/ResourcePackLoader.java @@ -2,6 +2,7 @@ import org.geysermc.geyser.api.pack.PackCodec; import org.geysermc.geyser.api.pack.ResourcePack; +import org.geysermc.geyser.api.pack.ResourcePackManifest; import java.io.File; import java.nio.file.Path; @@ -12,52 +13,50 @@ import static net.onebeastchris.geyser.extension.pickpack.PickPack.logger; public class ResourcePackLoader { - public Map OPT_IN = new HashMap<>(); - public Map OPT_OUT = new HashMap<>(); - public Map PACKS_INFO = new HashMap<>(); + public Map DEFAULT = new HashMap<>(); + public Map OPTIONAL = new HashMap<>(); + public Map PACKS_INFO = new HashMap<>(); - public ResourcePackLoader(Path optOutPath, Path optInPath) { - loadPacks(optOutPath, optInPath); + private final Path optionalPacksPath; + private final Path defaultPacksPath; + public ResourcePackLoader(Path optionalPacksPath, Path defaultPacksPath) { + this.optionalPacksPath = optionalPacksPath; + this.defaultPacksPath = defaultPacksPath; + loadPacks(); } - public void reload(Path optOutPath, Path optInPath) { - OPT_OUT.clear(); - OPT_IN.clear(); + public void reload() { + OPTIONAL.clear(); + DEFAULT.clear(); PACKS_INFO.clear(); - loadPacks(optOutPath, optInPath); + loadPacks(); } - public void loadPacks(Path optout, Path optin) { + public void loadPacks() { try { - OPT_OUT = loadFromFolder(optout); - OPT_IN = loadFromFolder(optin); + OPTIONAL = loadFromFolder(optionalPacksPath); + DEFAULT = loadFromFolder(defaultPacksPath); - logger.info("Loaded " + OPT_OUT.size() + " opt-out packs!"); - logger.info("Loaded " + OPT_IN.size() + " opt-in packs!"); + logger.info("Loaded " + DEFAULT.size() + " default packs!"); + logger.info("Loaded " + OPTIONAL.size() + " optional packs!"); } catch (Exception e) { logger.error("Failed to load packs!", e); } } - public ResourcePack getPack(String packUUID) { - if (OPT_IN.containsKey(packUUID)) { - return OPT_IN.get(packUUID); - } else return OPT_OUT.get(packUUID); + public ResourcePack getPack(String packId) { + return DEFAULT.getOrDefault(packId, OPTIONAL.get(packId)); } public HashMap loadFromFolder(Path path) { HashMap packs = new HashMap<>(); + for (File file : Objects.requireNonNull(path.toFile().listFiles())) { try { ResourcePack pack = ResourcePack.create(PackCodec.path(file.toPath())); String uuid = pack.manifest().header().uuid().toString(); packs.put(uuid, pack); - - PACKS_INFO.put(uuid, new String[] { - pack.manifest().header().name(), - pack.manifest().header().description(), - pack.manifest().header().version().toString() - }); + PACKS_INFO.put(uuid, pack.manifest()); } catch (Exception e) { logger.error("Failed to load pack " + file.getName(), e); e.printStackTrace(); diff --git a/src/main/resources/en_US.properties b/src/main/resources/en_US.properties index 6d2da7b..d0115a5 100644 --- a/src/main/resources/en_US.properties +++ b/src/main/resources/en_US.properties @@ -1,10 +1,5 @@ -menu.command.description=Choose your own resource packs! -reset.command.description=Reset your packs to the default packs -reload.command.description=Reloads the config - main.menu.title=Your current packs: main.menu.filter=Filter packs -main.menu.back=Back main.menu.select=Select packs filter.form.title=Which packs would you like to use? @@ -17,8 +12,9 @@ filter.transfer.warning=If the transfer packet does not work for you, you can di filter.transfer.toggle=Use transfer packet pack.form.title=Choose your packs: -pack.form.label=§l§2 showing§r§l§6 %filter% §r +pack.form.label=Showing %s no_packs.warning=You have no packs applied! disconnect.message=§6 Join back to apply the changes! -reload.message=§6 Config reloaded! \ No newline at end of file +reload.success=§6 Config reloaded! +reload.error=§c Config reload failed! \ No newline at end of file