diff --git a/build.gradle.kts b/build.gradle.kts index 3bed62c..b3fd50a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { `maven-publish` } -version = "1.0.0" +version = "1.1.0" group = "io.github.ashisbored" repositories { @@ -27,6 +27,10 @@ dependencies { // placeholder-api modImplementation(libs.placeholder.api) include(libs.placeholder.api) + + // more-codecs + modImplementation(libs.more.codecs) + include(libs.more.codecs) } tasks.processResources { diff --git a/libs.versions.toml b/libs.versions.toml index 7e8c932..4fa0253 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -6,6 +6,7 @@ fabric-loader = "0.11.6" fabric-api = "0.37.1+1.17" placeholder-api = "1.0.1+1.17" +more-codecs = "0.2.0" [libraries] minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } @@ -15,3 +16,4 @@ fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-l fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" } placeholder-api = { module = "eu.pb4:placeholder-api", version.ref = "placeholder-api" } +more-codecs = { module = "xyz.nucleoid:more-codecs", version.ref = "more-codecs" } diff --git a/src/main/java/io/github/ashisbored/playerpronouns/Config.java b/src/main/java/io/github/ashisbored/playerpronouns/Config.java index 82185b1..b037352 100644 --- a/src/main/java/io/github/ashisbored/playerpronouns/Config.java +++ b/src/main/java/io/github/ashisbored/playerpronouns/Config.java @@ -5,8 +5,10 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.github.ashisbored.playerpronouns.data.Pronoun; import net.fabricmc.loader.api.FabricLoader; import java.io.IOException; @@ -19,15 +21,15 @@ import java.util.Optional; public class Config { private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.BOOL.fieldOf("allow_custom").forGetter(config -> config.allowCustom), - Codec.STRING.listOf().fieldOf("single").forGetter(config -> config.single), - Codec.STRING.listOf().fieldOf("pairs").forGetter(config -> config.pairs) + Pronoun.CODEC.listOf().fieldOf("single").forGetter(config -> config.single), + Pronoun.CODEC.listOf().fieldOf("pairs").forGetter(config -> config.pairs) ).apply(instance, Config::new)); private final boolean allowCustom; - private final List single; - private final List pairs; + private final List single; + private final List pairs; - private Config(boolean allowCustom, List single, List pairs) { + private Config(boolean allowCustom, List single, List pairs) { this.allowCustom = allowCustom; this.single = single; this.pairs = pairs; @@ -41,11 +43,11 @@ public class Config { return allowCustom; } - public List getSingle() { + public List getSingle() { return single; } - public List getPairs() { + public List getPairs() { return pairs; } @@ -69,7 +71,10 @@ public class Config { String s = Files.readString(path); JsonParser parser = new JsonParser(); JsonElement ele = parser.parse(s); - return CODEC.decode(JsonOps.INSTANCE, ele).map(Pair::getFirst).result().orElseGet(Config::new); + DataResult result = CODEC.decode(JsonOps.INSTANCE, ele).map(Pair::getFirst); + Optional> err = result.error(); + err.ifPresent(e -> PlayerPronouns.LOGGER.warn("Failed to load config: {}", e.message())); + return result.result().orElseGet(Config::new); } catch (IOException e) { PlayerPronouns.LOGGER.warn("Failed to load config!", e); return new Config(); diff --git a/src/main/java/io/github/ashisbored/playerpronouns/PlayerPronouns.java b/src/main/java/io/github/ashisbored/playerpronouns/PlayerPronouns.java index 84d04dd..6bbbed3 100644 --- a/src/main/java/io/github/ashisbored/playerpronouns/PlayerPronouns.java +++ b/src/main/java/io/github/ashisbored/playerpronouns/PlayerPronouns.java @@ -3,8 +3,9 @@ package io.github.ashisbored.playerpronouns; import eu.pb4.placeholders.PlaceholderAPI; import eu.pb4.placeholders.PlaceholderResult; import io.github.ashisbored.playerpronouns.command.PronounsCommand; -import io.github.ashisbored.playerpronouns.data.BinaryPronounDatabase; +import io.github.ashisbored.playerpronouns.data.PronounDatabase; import io.github.ashisbored.playerpronouns.data.PronounList; +import io.github.ashisbored.playerpronouns.data.Pronouns; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; @@ -24,7 +25,7 @@ public class PlayerPronouns implements ModInitializer { public static final Logger LOGGER = LogManager.getLogger(); public static final String MOD_ID = "playerpronouns"; - private static BinaryPronounDatabase pronounDatabase; + private static PronounDatabase pronounDatabase; public static Config config; @Override @@ -40,7 +41,7 @@ public class PlayerPronouns implements ModInitializer { if (!Files.exists(playerData)) { Files.createDirectories(playerData); } - pronounDatabase = BinaryPronounDatabase.load(playerData.resolve("pronouns.dat")); + pronounDatabase = PronounDatabase.load(playerData.resolve("pronouns.dat")); } catch (IOException e) { LOGGER.error("Failed to create/load pronoun database!", e); } @@ -69,8 +70,26 @@ public class PlayerPronouns implements ModInitializer { if (pronounDatabase == null) { return PlaceholderResult.value("Unknown"); } - String pronouns = pronounDatabase.get(player.getUuid()); - return PlaceholderResult.value(Objects.requireNonNullElse(pronouns, "Unknown")); + Pronouns pronouns = pronounDatabase.get(player.getUuid()); + if (pronouns == null) { + return PlaceholderResult.value("Unknown"); + } + return PlaceholderResult.value(pronouns.formatted()); + }); + + PlaceholderAPI.register(new Identifier(MOD_ID, "raw_pronouns"), ctx -> { + if (!ctx.hasPlayer()) { + return PlaceholderResult.invalid("missing player"); + } + ServerPlayerEntity player = ctx.getPlayer(); + if (pronounDatabase == null) { + return PlaceholderResult.value("Unknown"); + } + Pronouns pronouns = pronounDatabase.get(player.getUuid()); + if (pronouns == null) { + return PlaceholderResult.value("Unknown"); + } + return PlaceholderResult.value(pronouns.raw()); }); } @@ -82,7 +101,7 @@ public class PlayerPronouns implements ModInitializer { pronounDatabase.save(playerData.resolve("pronouns.dat")); } - public static boolean setPronouns(ServerPlayerEntity player, String pronouns) { + public static boolean setPronouns(ServerPlayerEntity player, Pronouns pronouns) { if (pronounDatabase == null) return false; pronounDatabase.put(player.getUuid(), pronouns); diff --git a/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsArgument.java b/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsArgument.java index 02bc0c3..c02849d 100644 --- a/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsArgument.java +++ b/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsArgument.java @@ -15,7 +15,7 @@ public class PronounsArgument { .suggests((ctx, builder) -> { String remaining = builder.getRemainingLowerCase(); - for (String pronouns : PronounList.get().getCalculatedPronounStrings()) { + for (String pronouns : PronounList.get().getCalculatedPronounStrings().keySet()) { if (pronouns.toLowerCase(Locale.ROOT).startsWith(remaining)) { builder.suggest(pronouns); } diff --git a/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsCommand.java b/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsCommand.java index 7cd7472..ecb8828 100644 --- a/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsCommand.java +++ b/src/main/java/io/github/ashisbored/playerpronouns/command/PronounsCommand.java @@ -4,11 +4,15 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import io.github.ashisbored.playerpronouns.PlayerPronouns; import io.github.ashisbored.playerpronouns.data.PronounList; +import io.github.ashisbored.playerpronouns.data.Pronouns; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import java.util.Map; + import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static io.github.ashisbored.playerpronouns.command.PronounsArgument.pronouns; import static net.minecraft.server.command.CommandManager.literal; @@ -19,17 +23,26 @@ public class PronounsCommand { .then(pronouns("pronouns") .executes(ctx -> { ServerPlayerEntity player = ctx.getSource().getPlayer(); - String pronouns = getString(ctx, "pronouns"); + String pronounsString = getString(ctx, "pronouns"); - if (!PlayerPronouns.config.allowCustom() && !PronounList.get().getCalculatedPronounStrings().contains(pronouns)) { + Map pronounTexts = PronounList.get().getCalculatedPronounStrings(); + if (!PlayerPronouns.config.allowCustom() && !pronounTexts.containsKey(pronounsString)) { ctx.getSource().sendError(new LiteralText("Custom pronouns have been disabled by the server administrator.")); return 0; } + Pronouns pronouns; + if (pronounTexts.containsKey(pronounsString)) { + pronouns = new Pronouns(pronounsString, pronounTexts.get(pronounsString)); + } else { + pronouns = new Pronouns(pronounsString, new LiteralText(pronounsString)); + } + if (!PlayerPronouns.setPronouns(player, pronouns)) { ctx.getSource().sendError(new LiteralText("Failed to update pronouns, sorry")); } else { - ctx.getSource().sendFeedback(new LiteralText("Updated your pronouns to " + pronouns + "!") + ctx.getSource().sendFeedback(new LiteralText("Updated your pronouns to ") + .append(pronouns.formatted()) .formatted(Formatting.GREEN), false); } diff --git a/src/main/java/io/github/ashisbored/playerpronouns/data/BinaryPronounDatabase.java b/src/main/java/io/github/ashisbored/playerpronouns/data/BinaryPronounDatabase.java index 79e680a..eb56548 100644 --- a/src/main/java/io/github/ashisbored/playerpronouns/data/BinaryPronounDatabase.java +++ b/src/main/java/io/github/ashisbored/playerpronouns/data/BinaryPronounDatabase.java @@ -2,11 +2,14 @@ package io.github.ashisbored.playerpronouns.data; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Map; import java.util.UUID; public class BinaryPronounDatabase { @@ -48,6 +51,19 @@ public class BinaryPronounDatabase { } } + public static PalettePronounDatabase convert(Path path) throws IOException { + Object2ObjectMap pronouns = new Object2ObjectOpenHashMap<>(); + Map pronounStrings = PronounList.get().getCalculatedPronounStrings(); + for (var entry : BinaryPronounDatabase.load(path).data.entrySet()) { + Text formatted = new LiteralText(entry.getValue()); + if (pronounStrings.containsKey(entry.getValue())) { + formatted = pronounStrings.get(entry.getValue()); + } + pronouns.put(entry.getKey(), new Pronouns(entry.getValue(), formatted)); + } + return new PalettePronounDatabase(pronouns); + } + public static BinaryPronounDatabase load(Path path) throws IOException { if (!Files.exists(path)) { return new BinaryPronounDatabase(); diff --git a/src/main/java/io/github/ashisbored/playerpronouns/data/PalettePronounDatabase.java b/src/main/java/io/github/ashisbored/playerpronouns/data/PalettePronounDatabase.java new file mode 100644 index 0000000..f5c0bc5 --- /dev/null +++ b/src/main/java/io/github/ashisbored/playerpronouns/data/PalettePronounDatabase.java @@ -0,0 +1,139 @@ +package io.github.ashisbored.playerpronouns.data; + +import com.google.gson.JsonParser; +import com.mojang.serialization.JsonOps; +import io.github.ashisbored.playerpronouns.PlayerPronouns; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.util.Pair; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * An improved version of {@link io.github.ashisbored.playerpronouns.data.BinaryPronounDatabase} that uses a palette + * for efficiency when storing lots of players. It also supports versioning of the file to allow for changes to be + * made to the format in the future. + */ +public class PalettePronounDatabase implements PronounDatabase { + public static final int VERSION_NUMBER = 1; + private static final JsonParser JSON_PARSER = new JsonParser(); + private final Object2ObjectMap data; + + protected PalettePronounDatabase(Object2ObjectMap data) { + this.data = data; + } + + private PalettePronounDatabase() { + this(new Object2ObjectOpenHashMap<>()); + } + + @Override + public void put(UUID player, Pronouns pronouns) { + this.data.put(player, pronouns); + } + + @Override + public @Nullable Pronouns get(UUID player) { + return this.data.get(player); + } + + @Override + public synchronized void save(Path path) throws IOException { + try (OutputStream os = Files.newOutputStream(path); + DataOutputStream out = new DataOutputStream(os)) { + + out.writeShort(0x4568); + out.writeInt(VERSION_NUMBER); + + Pair, Object2IntMap> pair = this.convertToPalette(); + List palette = pair.getLeft(); + Object2IntMap values = pair.getRight(); + + out.writeInt(palette.size()); + for (Pronouns pronouns : palette) { + String p = Pronouns.CODEC.encodeStart(JsonOps.INSTANCE, pronouns).result().orElseThrow().toString(); + out.writeUTF(p); + } + + out.writeInt(values.size()); + for (var entry : values.object2IntEntrySet()) { + out.writeLong(entry.getKey().getMostSignificantBits()); + out.writeLong(entry.getKey().getLeastSignificantBits()); + out.writeInt(entry.getIntValue()); + } + } + } + + private Pair, Object2IntMap> convertToPalette() { + List palette = new ArrayList<>(); + Object2IntMap values = new Object2IntOpenHashMap<>(); + for (var entry : this.data.entrySet()) { + if (!palette.contains(entry.getValue())) { + palette.add(entry.getValue()); + } + values.put(entry.getKey(), palette.indexOf(entry.getValue())); + } + return new Pair<>(palette, values); + } + + public static PalettePronounDatabase load(Path path) throws IOException { + if (!Files.exists(path)) { + return new PalettePronounDatabase(); + } + + try (InputStream is = Files.newInputStream(path); + DataInputStream in = new DataInputStream(is)) { + + short magic = in.readShort(); + if (magic != 0x4568) { + throw new IOException("Invalid DB magic: " + magic); + } + + int version = in.readInt(); + if (version > VERSION_NUMBER) { + throw new IOException("DB version " + version + " is greater than the latest supported: " + version); + } + + List palette = new ArrayList<>(); + int paletteSize = in.readInt(); + for (int i = 0; i < paletteSize; i++) { + String s = in.readUTF(); + Optional optionalPronouns = Pronouns.CODEC.decode(JsonOps.INSTANCE, JSON_PARSER.parse(s)).resultOrPartial(e -> { + throw new RuntimeException(new IOException("Invalid pronouns in database: " + s)); + }).map(com.mojang.datafixers.util.Pair::getFirst); + if (optionalPronouns.isEmpty()) { + throw new IOException("Invalid pronouns in database: " + s); + } + palette.add(optionalPronouns.get()); + } + + Object2ObjectMap data = new Object2ObjectOpenHashMap<>(); + + // V1 Parsing + if (version == 1) { + int playerCount = in.readInt(); + for (int i = 0; i < playerCount; i++) { + long mostSigBits = in.readLong(); + long leastSigBits = in.readLong(); + UUID uuid = new UUID(mostSigBits, leastSigBits); + int pronounIndex = in.readInt(); + Pronouns old = data.put(uuid, palette.get(pronounIndex)); + if (old != null) { + PlayerPronouns.LOGGER.warn("Duplicate UUID in database: " + uuid); + } + } + } + + return new PalettePronounDatabase(data); + } + } +} diff --git a/src/main/java/io/github/ashisbored/playerpronouns/data/Pronoun.java b/src/main/java/io/github/ashisbored/playerpronouns/data/Pronoun.java new file mode 100644 index 0000000..cbad4ac --- /dev/null +++ b/src/main/java/io/github/ashisbored/playerpronouns/data/Pronoun.java @@ -0,0 +1,107 @@ +package io.github.ashisbored.playerpronouns.data; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.github.ashisbored.playerpronouns.PlayerPronouns; +import net.minecraft.text.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public record Pronoun( + String pronoun, + Style style +) { + public static final Codec CODEC = new PronounCodec(); + + private static class PronounCodec implements Codec { + private static final Codec OBJECT_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("pronoun").forGetter(Pronoun::pronoun), + Codec.STRING.listOf().xmap(Pronoun::styleFrom, Pronoun::fromStyle).fieldOf("style").forGetter(Pronoun::style) + ).apply(instance, Pronoun::new)); + + private PronounCodec() { } + + @Override + public DataResult> decode(DynamicOps ops, T input) { + Optional asString = ops.getStringValue(input).result(); + return asString.map(s -> DataResult.success(new Pair<>(new Pronoun(s, Style.EMPTY), ops.empty()))) + .orElseGet(() -> OBJECT_CODEC.decode(ops, input)); + } + + @Override + public DataResult encode(Pronoun input, DynamicOps ops, T prefix) { + if (input.style.isEmpty()) { + return ops.mergeToPrimitive(prefix, ops.createString(input.pronoun)); + } else { + return OBJECT_CODEC.encode(input, ops, prefix); + } + } + } + + private static Style styleFrom(List formatting) { + Style style = Style.EMPTY; + + for (String format : formatting) { + switch (format) { + case "bold" -> style = style.withBold(true); + case "italic" -> style = style.withItalic(true); + case "strikethrough" -> style = style.withStrikethrough(true); + case "underline" -> style = style.withUnderline(true); + case "obfuscated" -> style = style.obfuscated(true); + default -> { + TextColor col = TextColor.parse(format); + if (col != null) { + style = style.withColor(col); + } else { + PlayerPronouns.LOGGER.warn("Invalid formatting: {}", format); + } + } + } + } + + return style; + } + + private static List fromStyle(Style style) { + List ret = new ArrayList<>(); + TextColor colour = style.getColor(); + if (colour != null) { + ret.add(colour.toString()); + } + if (style.isBold()) { + ret.add("bold"); + } + if (style.isItalic()) { + ret.add("italic"); + } + if (style.isStrikethrough()) { + ret.add("strikethrough"); + } + if (style.isUnderlined()) { + ret.add("underline"); + } + if (style.isObfuscated()) { + ret.add("obfuscated"); + } + return ret; + } + + public MutableText toText() { + return new LiteralText(this.pronoun).setStyle(this.style); + } + + @Override + public String toString() { + return this.pronoun; + } + + @Override + public int hashCode() { + return this.pronoun.hashCode(); + } +} diff --git a/src/main/java/io/github/ashisbored/playerpronouns/data/PronounDatabase.java b/src/main/java/io/github/ashisbored/playerpronouns/data/PronounDatabase.java new file mode 100644 index 0000000..13b7fd3 --- /dev/null +++ b/src/main/java/io/github/ashisbored/playerpronouns/data/PronounDatabase.java @@ -0,0 +1,43 @@ +package io.github.ashisbored.playerpronouns.data; + +import io.github.ashisbored.playerpronouns.PlayerPronouns; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; + +public interface PronounDatabase { + void put(UUID player, Pronouns pronouns); + @Nullable Pronouns get(UUID player); + void save(Path path) throws IOException; + + static PronounDatabase load(Path path) throws IOException { + if (!Files.exists(path)) { + // Will create a new empty database. + return PalettePronounDatabase.load(path); + } + + boolean legacy = false; + try (InputStream is = Files.newInputStream(path); + DataInputStream in = new DataInputStream(is)) { + short magic = in.readShort(); + if (magic == 0x4567) { + legacy = true; + } + } + + if (legacy) { + PlayerPronouns.LOGGER.info("Old (1.0.0) format pronoun database found, converting..."); + Path backupPath = path.getParent().resolve(path.getFileName().toString() + ".bak"); + Files.copy(path, backupPath); + PlayerPronouns.LOGGER.info("Old database backed up to {}", backupPath); + return BinaryPronounDatabase.convert(path); + } + + return PalettePronounDatabase.load(path); + } +} diff --git a/src/main/java/io/github/ashisbored/playerpronouns/data/PronounList.java b/src/main/java/io/github/ashisbored/playerpronouns/data/PronounList.java index e1b3dca..82d0679 100644 --- a/src/main/java/io/github/ashisbored/playerpronouns/data/PronounList.java +++ b/src/main/java/io/github/ashisbored/playerpronouns/data/PronounList.java @@ -5,6 +5,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.github.ashisbored.playerpronouns.Config; import io.github.ashisbored.playerpronouns.PlayerPronouns; +import net.minecraft.text.LiteralText; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; +import net.minecraft.text.Text; import net.minecraft.util.Pair; import java.io.IOException; @@ -15,13 +19,13 @@ import java.util.*; public class PronounList { private static PronounList INSTANCE; - private final List defaultSingle; - private final List defaultPairs; - private final List customSingle; - private final List customPairs; - private final List calculatedPronounStrings; + private final List defaultSingle; + private final List defaultPairs; + private final List customSingle; + private final List customPairs; + private final Map calculatedPronounStrings; - public PronounList(List defaultSingle, List defaultPairs, List customSingle, List customPairs) { + public PronounList(List defaultSingle, List defaultPairs, List customSingle, List customPairs) { this.defaultSingle = defaultSingle; this.defaultPairs = defaultPairs; this.customSingle = customSingle; @@ -29,24 +33,33 @@ public class PronounList { this.calculatedPronounStrings = this.computePossibleCombinations(); } - public List getCalculatedPronounStrings() { + public Map getCalculatedPronounStrings() { return this.calculatedPronounStrings; } - private List computePossibleCombinations() { - List ret = new ArrayList<>(); - ret.addAll(this.defaultSingle); - ret.addAll(this.customSingle); - List combinedPairs = new ArrayList<>(); + private Map computePossibleCombinations() { + Map ret = new HashMap<>(); + for (Pronoun pronoun : this.defaultSingle) { + ret.put(pronoun.pronoun(), pronoun.toText()); + } + for (Pronoun pronoun : this.customSingle) { + ret.put(pronoun.pronoun(), pronoun.toText()); + } + List combinedPairs = new ArrayList<>(); combinedPairs.addAll(this.defaultPairs); combinedPairs.addAll(this.customPairs); for (int i = 0; i < combinedPairs.size(); i++) { for (int j = 0; j < combinedPairs.size(); j++) { if (i == j) continue; - ret.add(combinedPairs.get(i) + "/" + combinedPairs.get(j)); + Pronoun a = combinedPairs.get(i); + Pronoun b = combinedPairs.get(j); + MutableText combined = new LiteralText(""); + combined.append(a.toText()); + combined.append(new LiteralText("/")); + combined.append(b.toText()); + ret.put(a.pronoun() + "/" + b.pronoun(), combined); } } - ret.sort(Comparator.naturalOrder()); return ret; } @@ -55,7 +68,7 @@ public class PronounList { throw new IllegalStateException("PronounList has already been loaded!"); } - Pair, List> defaults = loadDefaults(); + Pair, List> defaults = loadDefaults(); INSTANCE = new PronounList( defaults.getLeft(), defaults.getRight(), @@ -71,16 +84,16 @@ public class PronounList { return INSTANCE; } - private static Pair, List> loadDefaults() { + private static Pair, List> loadDefaults() { try (InputStream is = Objects.requireNonNull(PronounList.class.getResourceAsStream("/default_pronouns.json")); InputStreamReader reader = new InputStreamReader(is)) { JsonObject ele = new JsonParser().parse(reader).getAsJsonObject(); JsonArray jsonSingle = ele.getAsJsonArray("single"); JsonArray jsonPairs = ele.getAsJsonArray("pairs"); - List single = new ArrayList<>(); - List pairs = new ArrayList<>(); - jsonSingle.forEach(e -> single.add(e.getAsString())); - jsonPairs.forEach(e -> pairs.add(e.getAsString())); + List single = new ArrayList<>(); + List pairs = new ArrayList<>(); + jsonSingle.forEach(e -> single.add(new Pronoun(e.getAsString(), Style.EMPTY))); + jsonPairs.forEach(e -> pairs.add(new Pronoun(e.getAsString(), Style.EMPTY))); return new Pair<>(single, pairs); } catch (IOException e) { PlayerPronouns.LOGGER.error("Failed to load default pronouns!", e); diff --git a/src/main/java/io/github/ashisbored/playerpronouns/data/Pronouns.java b/src/main/java/io/github/ashisbored/playerpronouns/data/Pronouns.java new file mode 100644 index 0000000..f7d16dc --- /dev/null +++ b/src/main/java/io/github/ashisbored/playerpronouns/data/Pronouns.java @@ -0,0 +1,16 @@ +package io.github.ashisbored.playerpronouns.data; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.text.Text; +import xyz.nucleoid.codecs.MoreCodecs; + +public record Pronouns( + String raw, + Text formatted +) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("raw").forGetter(Pronouns::raw), + MoreCodecs.TEXT.fieldOf("formatted").forGetter(Pronouns::formatted) + ).apply(instance, Pronouns::new)); +}