feat: the big refactor

- Change the package from io.github.ashisbored to dev.ashhhleyyy
- Split into API and implementation packages
- Adjust command to allow querying other players' pronouns
- Removed some static fields
This commit is contained in:
Ashhhleyyy 2023-06-22 10:56:03 +01:00
parent 91d9c5c3cd
commit 9a84ade7f5
Signed by: ash
GPG key ID: 83B789081A0878FB
12 changed files with 204 additions and 70 deletions

View file

@ -1,17 +1,23 @@
package io.github.ashisbored.playerpronouns.data;
package dev.ashhhleyyy.playerpronouns.api;
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 dev.ashhhleyyy.playerpronouns.impl.PlayerPronouns;
import net.minecraft.text.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* A single pronoun, which consists of the word itself, along with an associated style.
* @param pronoun The text of this pronoun
* @param style An associated style that is used for display as {@link Text}
*/
public record Pronoun(
String pronoun,
Style style
@ -91,6 +97,9 @@ public record Pronoun(
return ret;
}
/**
* @return A version of this pronoun formatted as a {@link MutableText}
*/
public MutableText toText() {
return Text.literal(this.pronoun).setStyle(this.style);
}

View file

@ -1,10 +1,16 @@
package io.github.ashisbored.playerpronouns.data;
package dev.ashhhleyyy.playerpronouns.api;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.text.Text;
import net.minecraft.util.dynamic.Codecs;
/**
* A combined set of {@link Pronoun}s
* @param raw The plain text version of this pronoun set
* @param formatted The styled version of this pronoun set
* @param remote Whether the pronouns were fetched from a remote API
*/
public record Pronouns(
String raw,
Text formatted,

View file

@ -0,0 +1,85 @@
package dev.ashhhleyyy.playerpronouns.api;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import net.minecraft.server.network.ServerPlayerEntity;
/**
* Entrypoint to the API, and provides access to a {@link PronounReader} and {@link PronounSetter}
*/
public final class PronounsApi {
private static @Nullable PronounReader READER = null;
private static @Nullable PronounSetter SETTER = null;
/**
* @return The currently initialised {@link PronounReader}
*/
public static PronounReader getReader() {
if (READER == null) {
throw new IllegalStateException("PronounReader has not been initialised");
}
return READER;
}
/**
* @return The currently initialised {@link PronounSetter}
*/
public static PronounSetter getSetter() {
if (SETTER == null) {
throw new IllegalStateException("PronounSetter has not been initialised");
}
return SETTER;
}
/**
* Makes the passed reader be set as the default.
*
* This should not be called by most mods, unless they are implementing a custom backend.
*
* @param reader The reader to configure
*/
public static void initReader(PronounReader reader) {
if (READER != null) {
throw new IllegalStateException("PronounReader has already been initialised");
}
READER = reader;
}
/**
* Makes the passed setter be set as the default.
*
* This should not be called by most mods, unless they are implementing a custom backend.
*
* @param reader The reader to configure
*/
public static void initSetter(PronounSetter setter) {
if (SETTER != null) {
throw new IllegalStateException("PronounSetter has already been initialised");
}
SETTER = setter;
}
/**
* Allows updating a player's {@link Pronouns}.
*
* Methods in this class may invoke blocking IO operations to save the database to disk.
*/
public interface PronounSetter {
default boolean setPronouns(ServerPlayerEntity player, @Nullable Pronouns pronouns) {
return this.setPronouns(player.getUuid(), pronouns);
}
boolean setPronouns(UUID playerId, @Nullable Pronouns pronouns);
}
/**
* Allows obtaining a player's {@link Pronouns}
*/
public interface PronounReader {
default @Nullable Pronouns getPronouns(ServerPlayerEntity player) {
return this.getPronouns(player.getUuid());
}
@Nullable Pronouns getPronouns(UUID playerId);
}
}

View file

@ -1,4 +1,4 @@
package io.github.ashisbored.playerpronouns;
package dev.ashhhleyyy.playerpronouns.impl;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
@ -8,7 +8,8 @@ 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 dev.ashhhleyyy.playerpronouns.api.Pronoun;
import net.fabricmc.loader.api.FabricLoader;
import java.io.IOException;

View file

@ -1,17 +1,12 @@
package io.github.ashisbored.playerpronouns;
package dev.ashhhleyyy.playerpronouns.impl;
import eu.pb4.placeholders.api.PlaceholderContext;
import eu.pb4.placeholders.api.PlaceholderResult;
import eu.pb4.placeholders.api.Placeholders;
import io.github.ashisbored.playerpronouns.command.PronounsCommand;
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.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
@ -20,6 +15,12 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.ashhhleyyy.playerpronouns.api.Pronouns;
import dev.ashhhleyyy.playerpronouns.api.PronounsApi;
import dev.ashhhleyyy.playerpronouns.impl.command.PronounsCommand;
import dev.ashhhleyyy.playerpronouns.impl.data.PronounDatabase;
import dev.ashhhleyyy.playerpronouns.impl.data.PronounList;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@ -31,10 +32,9 @@ import java.nio.file.Path;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
public class PlayerPronouns implements ModInitializer {
public class PlayerPronouns implements ModInitializer, PronounsApi.PronounReader, PronounsApi.PronounSetter {
public static final Logger LOGGER = LoggerFactory.getLogger(PlayerPronouns.class);
public static final String MOD_ID = "playerpronouns";
private static final String USER_AGENT = "player-pronouns/1.0 (+https://ashhhleyyy.dev/projects/2021/player-pronouns)";
@ -63,7 +63,7 @@ public class PlayerPronouns implements ModInitializer {
put("avoid", "avoid");
}};
private static PronounDatabase pronounDatabase;
private PronounDatabase pronounDatabase;
public static Config config;
@Override
@ -88,7 +88,7 @@ public class PlayerPronouns implements ModInitializer {
ServerLifecycleEvents.SERVER_STOPPING.register(server -> {
if (pronounDatabase != null) {
try {
savePronounDatabase(server);
pronounDatabase.save();
} catch (IOException e) {
LOGGER.error("Failed to save pronoun database!", e);
}
@ -139,14 +139,17 @@ public class PlayerPronouns implements ModInitializer {
PronounsCommand.register(dispatcher);
});
Placeholders.register(new Identifier(MOD_ID, "pronouns"), (ctx, argument)->
PlayerPronouns.fromContext(ctx, argument, true));
Placeholders.register(new Identifier(MOD_ID, "pronouns"), (ctx, argument) ->
fromContext(ctx, argument, true));
Placeholders.register(new Identifier(MOD_ID, "raw_pronouns"), (ctx, argument)->
PlayerPronouns.fromContext(ctx, argument, false));
Placeholders.register(new Identifier(MOD_ID, "raw_pronouns"), (ctx, argument) ->
fromContext(ctx, argument, false));
PronounsApi.initReader(this);
PronounsApi.initSetter(this);
}
private static PlaceholderResult fromContext(PlaceholderContext ctx, @Nullable String argument, boolean formatted) {
private PlaceholderResult fromContext(PlaceholderContext ctx, @Nullable String argument, boolean formatted) {
if (!ctx.hasPlayer()) {
return PlaceholderResult.invalid("missing player");
}
@ -172,20 +175,12 @@ public class PlayerPronouns implements ModInitializer {
PronounList.load(config);
}
private static void savePronounDatabase(MinecraftServer server) throws IOException {
Path playerData = server.getSavePath(WorldSavePath.PLAYERDATA);
if (!Files.exists(playerData)) {
Files.createDirectories(playerData);
}
pronounDatabase.save(playerData.resolve("pronouns.dat"));
}
public static boolean setPronouns(ServerPlayerEntity player, @Nullable Pronouns pronouns) {
public boolean setPronouns(UUID playerId, @Nullable Pronouns pronouns) {
if (pronounDatabase == null) return false;
pronounDatabase.put(player.getUuid(), pronouns);
pronounDatabase.put(playerId, pronouns);
try {
savePronounDatabase(Objects.requireNonNull(player.getServer()));
pronounDatabase.save();
} catch (IOException e) {
LOGGER.error("Failed to save pronoun database!", e);
}
@ -193,11 +188,11 @@ public class PlayerPronouns implements ModInitializer {
return true;
}
public static @Nullable Pronouns getPronouns(ServerPlayerEntity player) {
public @Nullable Pronouns getPronouns(ServerPlayerEntity player) {
return getPronouns(player.getUuid());
}
public static @Nullable Pronouns getPronouns(UUID playerId) {
public @Nullable Pronouns getPronouns(UUID playerId) {
if (pronounDatabase == null) return null;
return pronounDatabase.get(playerId);
}

View file

@ -1,8 +1,9 @@
package io.github.ashisbored.playerpronouns.command;
package dev.ashhhleyyy.playerpronouns.impl.command;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import io.github.ashisbored.playerpronouns.data.PronounList;
import dev.ashhhleyyy.playerpronouns.impl.data.PronounList;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;

View file

@ -1,11 +1,14 @@
package io.github.ashisbored.playerpronouns.command;
package dev.ashhhleyyy.playerpronouns.impl.command;
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 dev.ashhhleyyy.playerpronouns.api.Pronouns;
import dev.ashhhleyyy.playerpronouns.api.PronounsApi;
import dev.ashhhleyyy.playerpronouns.impl.data.PronounList;
import dev.ashhhleyyy.playerpronouns.impl.PlayerPronouns;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
@ -14,13 +17,15 @@ 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 dev.ashhhleyyy.playerpronouns.impl.command.PronounsArgument.pronouns;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
public class PronounsCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
dispatcher.register(literal("pronouns")
.then(pronouns("pronouns")
.then(literal("set")
.then(pronouns("pronouns")
.executes(ctx -> {
ServerPlayerEntity player = ctx.getSource().getPlayer();
String pronounsString = getString(ctx, "pronouns");
@ -38,7 +43,7 @@ public class PronounsCommand {
pronouns = new Pronouns(pronounsString, Text.literal(pronounsString), false);
}
if (!PlayerPronouns.setPronouns(player, pronouns)) {
if (!PronounsApi.getSetter().setPronouns(player, pronouns)) {
ctx.getSource().sendError(Text.literal("Failed to update pronouns, sorry"));
} else {
ctx.getSource().sendFeedback(() -> Text.literal("Updated your pronouns to ")
@ -48,6 +53,7 @@ public class PronounsCommand {
return Command.SINGLE_SUCCESS;
})
)
).then(literal("reload-config")
.requires(ctx -> Permissions.check(ctx, "playerpronouns.reload_config", 4))
.executes(ctx -> {
@ -58,7 +64,7 @@ public class PronounsCommand {
).then(literal("unset")
.executes(ctx -> {
ServerPlayerEntity player = ctx.getSource().getPlayer();
if (!PlayerPronouns.setPronouns(player, null)) {
if (!PronounsApi.getSetter().setPronouns(player, null)) {
ctx.getSource().sendError(Text.literal("Failed to update pronouns, sorry"));
} else {
ctx.getSource().sendFeedback(() -> Text.literal("Cleared your pronouns!")
@ -66,6 +72,24 @@ public class PronounsCommand {
}
return Command.SINGLE_SUCCESS;
})
).then(literal("show")
.then(argument("player", EntityArgumentType.player())
.executes(ctx -> {
ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
Pronouns pronouns = PronounsApi.getReader().getPronouns(player);
if (pronouns != null) {
ctx.getSource().sendFeedback(() -> Text.literal("")
.append(player.getDisplayName())
.append(Text.literal("'s pronouns are ")
.append(pronouns.formatted())), false);
} else {
ctx.getSource().sendFeedback(() -> Text.literal("")
.append(player.getDisplayName())
.append(Text.literal(" has not set any pronouns.")), false);
}
return Command.SINGLE_SUCCESS;
})
)
)
);
}

View file

@ -1,10 +1,12 @@
package io.github.ashisbored.playerpronouns.data;
package dev.ashhhleyyy.playerpronouns.impl.data;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import dev.ashhhleyyy.playerpronouns.api.Pronouns;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
@ -12,14 +14,16 @@ import java.util.Map;
import java.util.UUID;
public class BinaryPronounDatabase {
private final Path databasePath;
private final Object2ObjectMap<UUID, String> data;
private BinaryPronounDatabase(Object2ObjectMap<UUID, String> data) {
private BinaryPronounDatabase(Path databasePath, Object2ObjectMap<UUID, String> data) {
this.databasePath = databasePath;
this.data = data;
}
private BinaryPronounDatabase() {
this(new Object2ObjectOpenHashMap<>());
private BinaryPronounDatabase(Path databasePath) {
this(databasePath, new Object2ObjectOpenHashMap<>());
}
public void put(UUID uuid, @Nullable String pronouns) {
@ -34,8 +38,8 @@ public class BinaryPronounDatabase {
return this.data.get(uuid);
}
public synchronized void save(Path path) throws IOException {
try (OutputStream os = Files.newOutputStream(path);
public synchronized void save() throws IOException {
try (OutputStream os = Files.newOutputStream(this.databasePath);
DataOutputStream out = new DataOutputStream(os)) {
out.writeShort(0x4567); // some form of magic, idk
@ -60,12 +64,12 @@ public class BinaryPronounDatabase {
}
pronouns.put(entry.getKey(), new Pronouns(entry.getValue(), formatted, false));
}
return new PalettePronounDatabase(pronouns);
return new PalettePronounDatabase(path, pronouns);
}
public static BinaryPronounDatabase load(Path path) throws IOException {
if (!Files.exists(path)) {
return new BinaryPronounDatabase();
return new BinaryPronounDatabase(path);
}
try (InputStream is = Files.newInputStream(path);
@ -89,7 +93,7 @@ public class BinaryPronounDatabase {
}
}
return new BinaryPronounDatabase(data);
return new BinaryPronounDatabase(path, data);
}
}
}

View file

@ -1,8 +1,10 @@
package io.github.ashisbored.playerpronouns.data;
package dev.ashhhleyyy.playerpronouns.impl.data;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
import io.github.ashisbored.playerpronouns.PlayerPronouns;
import dev.ashhhleyyy.playerpronouns.api.Pronouns;
import dev.ashhhleyyy.playerpronouns.impl.PlayerPronouns;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
@ -19,20 +21,22 @@ import java.util.Optional;
import java.util.UUID;
/**
* An improved version of {@link io.github.ashisbored.playerpronouns.data.BinaryPronounDatabase} that uses a palette
* An improved version of {@link dev.ashhhleyyy.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 final Path databasePath;
private final Object2ObjectMap<UUID, Pronouns> data;
protected PalettePronounDatabase(Object2ObjectMap<UUID, Pronouns> data) {
protected PalettePronounDatabase(Path databasePath, Object2ObjectMap<UUID, Pronouns> data) {
this.databasePath = databasePath;
this.data = data;
}
private PalettePronounDatabase() {
this(new Object2ObjectOpenHashMap<>());
private PalettePronounDatabase(Path databasePath) {
this(databasePath, new Object2ObjectOpenHashMap<>());
}
@Override
@ -50,8 +54,8 @@ public class PalettePronounDatabase implements PronounDatabase {
}
@Override
public synchronized void save(Path path) throws IOException {
try (OutputStream os = Files.newOutputStream(path);
public synchronized void save() throws IOException {
try (OutputStream os = Files.newOutputStream(this.databasePath);
DataOutputStream out = new DataOutputStream(os)) {
out.writeShort(0x4568);
@ -90,7 +94,7 @@ public class PalettePronounDatabase implements PronounDatabase {
public static PalettePronounDatabase load(Path path) throws IOException {
if (!Files.exists(path)) {
return new PalettePronounDatabase();
return new PalettePronounDatabase(path);
}
try (InputStream is = Files.newInputStream(path);
@ -136,7 +140,7 @@ public class PalettePronounDatabase implements PronounDatabase {
}
}
return new PalettePronounDatabase(data);
return new PalettePronounDatabase(path, data);
}
}
}

View file

@ -1,8 +1,10 @@
package io.github.ashisbored.playerpronouns.data;
package dev.ashhhleyyy.playerpronouns.impl.data;
import io.github.ashisbored.playerpronouns.PlayerPronouns;
import org.jetbrains.annotations.Nullable;
import dev.ashhhleyyy.playerpronouns.api.Pronouns;
import dev.ashhhleyyy.playerpronouns.impl.PlayerPronouns;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -13,7 +15,7 @@ import java.util.UUID;
public interface PronounDatabase {
void put(UUID player, @Nullable Pronouns pronouns);
@Nullable Pronouns get(UUID player);
void save(Path path) throws IOException;
void save() throws IOException;
static PronounDatabase load(Path path) throws IOException {
if (!Files.exists(path)) {

View file

@ -1,10 +1,12 @@
package io.github.ashisbored.playerpronouns.data;
package dev.ashhhleyyy.playerpronouns.impl.data;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.github.ashisbored.playerpronouns.Config;
import io.github.ashisbored.playerpronouns.PlayerPronouns;
import dev.ashhhleyyy.playerpronouns.api.Pronoun;
import dev.ashhhleyyy.playerpronouns.impl.Config;
import dev.ashhhleyyy.playerpronouns.impl.PlayerPronouns;
import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
import net.minecraft.text.Text;

View file

@ -9,13 +9,14 @@
],
"contact": {
"sources": "https://github.com/ashhhleyyy/player-pronouns",
"issues": "https://github.com/ashhhleyyy/player-pronouns/issues"
"issues": "https://github.com/ashhhleyyy/player-pronouns/issues",
"homepage": "https://ashhhleyyy.dev/projects/2021/player-pronouns"
},
"license": "MIT",
"environment": "*",
"entrypoints": {
"main": [
"io.github.ashisbored.playerpronouns.PlayerPronouns"
"dev.ashhhleyyy.playerpronouns.impl.PlayerPronouns"
]
},
"depends": {