Skip to content

Commit

Permalink
Support custom formatting for pronouns, along with a new database for…
Browse files Browse the repository at this point in the history
…mat using a palette
  • Loading branch information
ashhhleyyy committed Aug 7, 2021
1 parent 9f9977a commit 915fd82
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 39 deletions.
6 changes: 5 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
`maven-publish`
}

version = "1.0.0"
version = "1.1.0"
group = "io.github.ashisbored"

repositories {
Expand All @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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" }
21 changes: 13 additions & 8 deletions src/main/java/io/github/ashisbored/playerpronouns/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
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;
Expand All @@ -19,15 +21,15 @@
public class Config {
private static final Codec<Config> 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<String> single;
private final List<String> pairs;
private final List<Pronoun> single;
private final List<Pronoun> pairs;

private Config(boolean allowCustom, List<String> single, List<String> pairs) {
private Config(boolean allowCustom, List<Pronoun> single, List<Pronoun> pairs) {
this.allowCustom = allowCustom;
this.single = single;
this.pairs = pairs;
Expand All @@ -41,11 +43,11 @@ public boolean allowCustom() {
return allowCustom;
}

public List<String> getSingle() {
public List<Pronoun> getSingle() {
return single;
}

public List<String> getPairs() {
public List<Pronoun> getPairs() {
return pairs;
}

Expand All @@ -69,7 +71,10 @@ public static Config load() {
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<Config> result = CODEC.decode(JsonOps.INSTANCE, ele).map(Pair::getFirst);
Optional<DataResult.PartialResult<Config>> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
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;
Expand All @@ -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
Expand All @@ -40,7 +41,7 @@ public void onInitialize() {
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);
}
Expand Down Expand Up @@ -69,8 +70,26 @@ public void onInitialize() {
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());
});
}

Expand All @@ -82,7 +101,7 @@ private static void savePronounDatabase(MinecraftServer server) throws IOExcepti
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static RequiredArgumentBuilder<ServerCommandSource, String> pronouns(Stri
.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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
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;
Expand All @@ -19,17 +23,26 @@ public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
.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<String, Text> 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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

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 {
Expand Down Expand Up @@ -48,6 +51,19 @@ public synchronized void save(Path path) throws IOException {
}
}

public static PalettePronounDatabase convert(Path path) throws IOException {
Object2ObjectMap<UUID, Pronouns> pronouns = new Object2ObjectOpenHashMap<>();
Map<String, Text> 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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<UUID, Pronouns> data;

protected PalettePronounDatabase(Object2ObjectMap<UUID, Pronouns> 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<List<Pronouns>, Object2IntMap<UUID>> pair = this.convertToPalette();
List<Pronouns> palette = pair.getLeft();
Object2IntMap<UUID> 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<List<Pronouns>, Object2IntMap<UUID>> convertToPalette() {
List<Pronouns> palette = new ArrayList<>();
Object2IntMap<UUID> 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<Pronouns> palette = new ArrayList<>();
int paletteSize = in.readInt();
for (int i = 0; i < paletteSize; i++) {
String s = in.readUTF();
Optional<Pronouns> 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<UUID, Pronouns> 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);
}
}
}
Loading

0 comments on commit 915fd82

Please sign in to comment.