Skip to content

Commit

Permalink
Load roles and command aliases from datapack
Browse files Browse the repository at this point in the history
  • Loading branch information
Gegy committed Jul 27, 2024
1 parent a9cd660 commit 0c4a6d8
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 118 deletions.
57 changes: 26 additions & 31 deletions src/main/java/com/lovetropics/perms/CommandAliasConfiguration.java
Original file line number Diff line number Diff line change
@@ -1,56 +1,51 @@
package com.lovetropics.perms;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.lovetropics.lib.codec.MoreCodecs;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Optional;

public record CommandAliasConfiguration(Map<String, String[]> aliases) {
private static final CommandAliasConfiguration EMPTY = new CommandAliasConfiguration(Map.of());

public static final Codec<CommandAliasConfiguration> CODEC = Codec.unboundedMap(Codec.STRING, MoreCodecs.arrayOrUnit(Codec.STRING, String[]::new))
.xmap(CommandAliasConfiguration::new, CommandAliasConfiguration::aliases);

public static CommandAliasConfiguration load() {
Path path = Paths.get("config/command_aliases.json");
if (!Files.exists(path)) {
if (!setupDefaultConfig(path)) {
return EMPTY;
}
}
private static final ThreadLocal<ResourceManager> RESOURCE_MANAGER = new ThreadLocal<>();

try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
JsonObject root = JsonParser.parseReader(reader).getAsJsonObject();
DataResult<CommandAliasConfiguration> result = CODEC.parse(JsonOps.INSTANCE, root);
result.error().ifPresent(error -> {
LTPermissions.LOGGER.warn("Malformed command aliases configuration: {}", error);
});
public static void setResourceManager(ResourceManager resourceManager) {
RESOURCE_MANAGER.set(resourceManager);
}

return result.result().orElse(EMPTY);
} catch (IOException e) {
LTPermissions.LOGGER.warn("Failed to load command_aliases.json configuration", e);
return EMPTY;
}
public static void clearResourceManager() {
RESOURCE_MANAGER.remove();
}

private static boolean setupDefaultConfig(Path path) {
try (InputStream input = LTPermissions.class.getResourceAsStream("/data/" + LTPermissions.ID + "/default_command_aliases.json")) {
Files.copy(input, path);
return true;
public static CommandAliasConfiguration load() {
ResourceManager resourceManager = RESOURCE_MANAGER.get();
if (resourceManager == null) {
LTPermissions.LOGGER.error("Resources not available, not loading command aliases");
return CommandAliasConfiguration.EMPTY;
}
Optional<Resource> resource = resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(LTPermissions.ID, "command_aliases.json"));
if (resource.isEmpty()) {
return CommandAliasConfiguration.EMPTY;
}
try (BufferedReader reader = resource.get().openAsReader()) {
return CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader))
.resultOrPartial(error -> LTPermissions.LOGGER.warn("Malformed command aliases configuration: {}", error))
.orElse(EMPTY);
} catch (IOException e) {
LTPermissions.LOGGER.warn("Failed to load default command_aliases.json configuration", e);
return false;
LTPermissions.LOGGER.warn("Failed to load command_aliases.json configuration", e);
return EMPTY;
}
}
}
13 changes: 0 additions & 13 deletions src/main/java/com/lovetropics/perms/LTPermissions.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.IExtensionPoint;
import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.neoforge.common.NeoForge;
Expand All @@ -38,7 +36,6 @@
import org.slf4j.Logger;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Map;

@Mod(LTPermissions.ID)
Expand Down Expand Up @@ -110,16 +107,6 @@ public LTPermissions(IEventBus modBus) {
}

private void setup(FMLCommonSetupEvent event) {
List<String> errors = RolesConfig.setup();
if (!errors.isEmpty()) {
LOGGER.warn("Failed to load roles config! ({} errors)", errors.size());
for (String error : errors) {
LOGGER.warn(" - {}", error);
}
}

CommandAliasConfiguration.load();

AuthorityShape.register();
}

Expand Down
34 changes: 14 additions & 20 deletions src/main/java/com/lovetropics/perms/command/RoleCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.GameProfileArgument;
import net.minecraft.server.MinecraftServer;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.server.MinecraftServer;

import javax.annotation.Nullable;
import java.util.Collection;
Expand Down Expand Up @@ -116,23 +116,17 @@ private static int listRoles(CommandSourceStack source, GameProfile player) {

private static int reloadRoles(CommandSourceStack source) {
MinecraftServer server = source.getServer();

server.execute(() -> {
List<String> errors = RolesConfig.setup();

PlayerRoleManager roleManager = PlayerRoleManager.get();
roleManager.onRoleReload(server, RolesConfig.get());

if (errors.isEmpty()) {
source.sendSuccess(() -> Component.literal("Role configuration successfully reloaded"), false);
} else {
MutableComponent errorFeedback = Component.literal("Failed to reload roles configuration!");
for (String error : errors) {
errorFeedback = errorFeedback.append("\n - " + error);
}
source.sendFailure(errorFeedback);
List<String> errors = RolesConfig.reload(server.getResourceManager());

if (errors.isEmpty()) {
source.sendSuccess(() -> Component.literal("Role configuration successfully reloaded"), false);
} else {
MutableComponent errorFeedback = Component.literal("Failed to reload roles configuration!");
for (String error : errors) {
errorFeedback = errorFeedback.append("\n - " + error);
}
});
source.sendFailure(errorFeedback);
}

return Command.SINGLE_SUCCESS;
}
Expand Down
102 changes: 66 additions & 36 deletions src/main/java/com/lovetropics/perms/config/RolesConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.lovetropics.perms.config;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
Expand All @@ -10,24 +9,37 @@
import com.lovetropics.lib.permission.role.RoleProvider;
import com.lovetropics.perms.LTPermissions;
import com.lovetropics.perms.role.SimpleRole;
import com.lovetropics.perms.store.PlayerRoleManager;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Dynamic;
import com.mojang.serialization.JsonOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimplePreparableReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.event.AddReloadListenerEvent;
import net.neoforged.neoforge.server.ServerLifecycleHooks;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

@EventBusSubscriber(modid = LTPermissions.ID)
public final class RolesConfig implements RoleProvider {
private static RolesConfig instance = new RolesConfig(Collections.emptyList(), SimpleRole.empty(Role.EVERYONE));
private static final RolesConfig DEFAULT_CONFIG = new RolesConfig(List.of(), SimpleRole.empty(Role.EVERYONE));

private static RolesConfig instance = DEFAULT_CONFIG;

private final Map<String, Role> roles;
private final Role everyone;
Expand All @@ -46,48 +58,63 @@ public static RolesConfig get() {
return instance;
}

public static List<String> setup() {
Path path = Paths.get("config/roles.json");
if (!Files.exists(path)) {
if (!createDefaultConfig(path)) {
return ImmutableList.of();
@SubscribeEvent
public static void onAddReloadListeners(AddReloadListenerEvent event) {
event.addListener(new SimplePreparableReloadListener<LoadResult>() {
@Override
protected LoadResult prepare(ResourceManager resourceManager, ProfilerFiller profiler) {
return load(resourceManager);
}

@Override
protected void apply(LoadResult result, ResourceManager resourceManager, ProfilerFiller profiler) {
RolesConfig.apply(result);
}
});
}

private static void apply(LoadResult result) {
instance = result.config;
PermissionsApi.setRoleProvider(result.config);
LTPermissions.LOGGER.debug("Loaded {} roles", result.config.roles.size());
result.config.roles.forEach((name, role) -> LTPermissions.LOGGER.debug("Role {} has configuration: {}", name, role));

MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null) {
PlayerRoleManager roleManager = PlayerRoleManager.get();
roleManager.onRoleReload(server, RolesConfig.get());
}
}

private static LoadResult load(ResourceManager resourceManager) {
Optional<Resource> resource = resourceManager.getResource(ResourceLocation.fromNamespaceAndPath(LTPermissions.ID, "roles.json"));
if (resource.isEmpty()) {
return new LoadResult(DEFAULT_CONFIG, List.of("Found no roles config"));
}

List<String> errors = new ArrayList<>();
ConfigErrorConsumer errorConsumer = errors::add;
ConfigErrorConsumer errorConsumer = error -> {
LTPermissions.LOGGER.warn("{}", error);
errors.add(error);
};

try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
try (BufferedReader reader = resource.get().openAsReader()) {
JsonElement root = JsonParser.parseReader(reader);
instance = parse(new Dynamic<>(JsonOps.INSTANCE, root), errorConsumer);
PermissionsApi.setRoleProvider(instance);
LTPermissions.LOGGER.debug("Loaded {} roles", instance.roles.size());
instance.roles.forEach((name, role) -> LTPermissions.LOGGER.debug("Role {} has configuration: {}", name, role));
RolesConfig config = parse(new Dynamic<>(JsonOps.INSTANCE, root), errorConsumer);
return new LoadResult(config, errors);
} catch (IOException e) {
errorConsumer.report("Failed to read roles.json configuration", e);
LTPermissions.LOGGER.warn("Failed to load roles.json configuration", e);
} catch (JsonSyntaxException e) {
errorConsumer.report("Malformed syntax in roles.json configuration", e);
LTPermissions.LOGGER.warn("Malformed syntax in roles.json configuration", e);
}

return errors;
return new LoadResult(DEFAULT_CONFIG, errors);
}

private static boolean createDefaultConfig(Path path) {
try {
if (!Files.exists(path.getParent())) {
Files.createDirectories(path.getParent());
}

try (InputStream input = LTPermissions.class.getResourceAsStream("/data/" + LTPermissions.ID + "/default_roles.json")) {
Files.copy(input, path);
return true;
}
} catch (IOException e) {
LTPermissions.LOGGER.warn("Failed to load default roles.json configuration", e);
return false;
}
public static List<String> reload(ResourceManager resourceManager) {
LoadResult results = load(resourceManager);
apply(results);
return results.errors;
}

private static <T> RolesConfig parse(Dynamic<T> root, ConfigErrorConsumer error) {
Expand Down Expand Up @@ -132,4 +159,7 @@ public Iterator<Role> iterator() {
public Stream<Role> stream() {
return this.roles.values().stream();
}

private record LoadResult(RolesConfig config, List<String> errors) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.lovetropics.perms.mixin;

import com.lovetropics.perms.CommandAliasConfiguration;
import net.minecraft.commands.Commands;
import net.minecraft.core.LayeredRegistryAccess;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.ReloadableServerResources;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.flag.FeatureFlagSet;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;

@Mixin(ReloadableServerResources.class)
public class ReloadableServerResourcesMixin {
@Inject(method = "lambda$loadResources$5", at = @At("HEAD"))
private static void beforeLoadResources(FeatureFlagSet featureFlags, Commands.CommandSelection commandSelection, int functionCompilationLevel, ResourceManager resourceManager, Executor backgroundExecutor, Executor gameExecutor, LayeredRegistryAccess<RegistryLayer> registryAccess, CallbackInfoReturnable<CompletionStage<?>> ci) {
CommandAliasConfiguration.setResourceManager(resourceManager);
}

@Inject(method = "lambda$loadResources$5", at = @At("TAIL"))
private static void afterLoadResources(FeatureFlagSet featureFlags, Commands.CommandSelection commandSelection, int functionCompilationLevel, ResourceManager resourceManager, Executor backgroundExecutor, Executor gameExecutor, LayeredRegistryAccess<RegistryLayer> registryAccess, CallbackInfoReturnable<CompletionStage<?>> ci) {
CommandAliasConfiguration.clearResourceManager();
}
}

This file was deleted.

10 changes: 0 additions & 10 deletions src/main/resources/data/ltpermissions/default_roles.json

This file was deleted.

1 change: 1 addition & 0 deletions src/main/resources/ltpermissions.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"compatibilityLevel": "JAVA_21",
"refmap": "ltpermissions.refmap.json",
"mixins": [
"ReloadableServerResourcesMixin",
"rule.LecternMenuMixin",
"rule.SignBlockMixin"
],
Expand Down
6 changes: 0 additions & 6 deletions src/main/resources/pack.mcmeta

This file was deleted.

0 comments on commit 0c4a6d8

Please sign in to comment.