diff --git a/src/main/java/com/github/fabricservertools/htm/HTMContainerLock.java b/src/main/java/com/github/fabricservertools/htm/HTMContainerLock.java index 57506a7..48b151e 100644 --- a/src/main/java/com/github/fabricservertools/htm/HTMContainerLock.java +++ b/src/main/java/com/github/fabricservertools/htm/HTMContainerLock.java @@ -1,5 +1,6 @@ package com.github.fabricservertools.htm; +import com.github.fabricservertools.htm.api.ProtectionGroup; import com.github.fabricservertools.htm.api.Lock; import me.lucko.fabric.api.permissions.v0.Permissions; import net.minecraft.nbt.NbtCompound; @@ -11,20 +12,19 @@ import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.UUID; +import java.util.*; public class HTMContainerLock { private Lock type; private UUID owner; + private HashSet groups; private HashSet trusted; private Map flags; public HTMContainerLock() { type = null; owner = null; + groups = new HashSet<>(); trusted = new HashSet<>(); initFlags(); } @@ -48,9 +48,14 @@ public void toTag(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) for (UUID uuid : trusted) { trustedTag.add(NbtHelper.fromUuid(uuid)); } - tag.put("Trusted", trustedTag); + NbtList groupsTag = new NbtList(); + for (UUID groupId : groups) { + groupsTag.add(NbtHelper.fromUuid(groupId)); + } + tag.put("Groups", groupsTag); + NbtList flagsTag = new NbtList(); for (Map.Entry entry : flags.entrySet()) { NbtCompound flagTag = new NbtCompound(); @@ -76,13 +81,17 @@ public void fromTag(NbtCompound tag, RegistryWrapper.WrapperLookup registryLooku } type.fromTag(tag.getCompound("TypeData"), registryLookup); owner = tag.getUuid("Owner"); - + NbtList trustedTag = tag.getList("Trusted", NbtElement.INT_ARRAY_TYPE); - for (NbtElement value : trustedTag) { trusted.add(NbtHelper.toUuid(value)); } + NbtList groupsTag = tag.getList("Groups", NbtElement.INT_ARRAY_TYPE); + for (NbtElement value : groupsTag) { + groups.add(NbtHelper.toUuid(value)); + } + NbtList flagTags = tag.getList("Flags", NbtElement.COMPOUND_TYPE); for (NbtElement flagTag : flagTags) { NbtCompound compoundTag = (NbtCompound) flagTag; @@ -119,6 +128,10 @@ public HashSet getTrusted() { return trusted; } + public Set getGroups() { + return groups; + } + public void setType(Lock type, ServerPlayerEntity owner) { this.type = type; this.owner = owner.getUuid(); @@ -140,6 +153,14 @@ public boolean removeTrust(UUID id) { return trusted.remove(id); } + public boolean addGroup(ProtectionGroup protectionGroup) { + return groups.add(protectionGroup.getId()); + } + + public boolean removeGroup(ProtectionGroup protectionGroup) { + return groups.remove(protectionGroup.getId()); + } + public boolean isTrusted(UUID id) { return trusted.contains(id); } diff --git a/src/main/java/com/github/fabricservertools/htm/Utility.java b/src/main/java/com/github/fabricservertools/htm/Utility.java index 66517ee..f9261e7 100644 --- a/src/main/java/com/github/fabricservertools/htm/Utility.java +++ b/src/main/java/com/github/fabricservertools/htm/Utility.java @@ -2,6 +2,7 @@ import com.github.fabricservertools.htm.interactions.InteractionManager; import com.github.fabricservertools.htm.world.data.GlobalTrustState; +import com.github.fabricservertools.htm.world.data.LockGroupState; import com.mojang.authlib.GameProfile; import net.minecraft.datafixer.DataFixTypes; import net.minecraft.entity.player.PlayerEntity; @@ -25,6 +26,13 @@ public static GlobalTrustState getGlobalTrustState(MinecraftServer server) { "globalTrust"); } + public static LockGroupState getLockGroupState(MinecraftServer server) { + return server.getOverworld().getPersistentStateManager().getOrCreate( + new PersistentState.Type<>(LockGroupState::new, LockGroupState::fromNbt, DataFixTypes.PLAYER), + "lockGroups" + ); + } + public static void sendMessage(PlayerEntity player, Text message) { sendMessage(player, message, false); } diff --git a/src/main/java/com/github/fabricservertools/htm/api/LockProtectionGroup.java b/src/main/java/com/github/fabricservertools/htm/api/LockProtectionGroup.java new file mode 100644 index 0000000..bf97967 --- /dev/null +++ b/src/main/java/com/github/fabricservertools/htm/api/LockProtectionGroup.java @@ -0,0 +1,175 @@ +package com.github.fabricservertools.htm.api; + +import net.minecraft.server.network.ServerPlayerEntity; + +import java.util.*; + +/** + * Describes a group which provides permissions globally. + */ +public class LockProtectionGroup implements ProtectionGroup { + private final UUID id; + private final UUID owner; + private final HashSet players; + private final HashSet managers; + private String name; + + + /** + * Creates a new instance of @see LockGroup. + * @param name The non-unique display name of the group. + */ + public LockProtectionGroup(String name, ServerPlayerEntity owner) { + this.id = UUID.randomUUID(); + this.owner = owner.getUuid(); + this.players = new HashSet<>(); + this.managers = new HashSet<>(); + this.name = name; + } + + public LockProtectionGroup(UUID id, UUID owner, HashSet players, HashSet managers, String name) { + this.id = id; + this.owner = owner; + this.players = players; + this.managers = managers; + this.name = name; + } + + /** + * Gets the unique id of this group. + * + * @return The group id. + */ + @Override + public UUID getId() { + return this.id; + } + + /** + * Gets the display name of the collection. + * + * @return The display name. + */ + @Override + public String getName() { + return name; + } + + /** + * Sets the name of the collection. + * + * @param name The new display name. + */ + @Override + public void setName(String name) { + this.name = name; + } + + /** + * Gets the id of the user who created this group. + * + * @return The {@see java.util.UUID} of the user who created this group. + */ + @Override + public UUID getOwner() { + return owner; + } + + /** + * Gets a readonly list of all group managers. + * + * @return A readonly list of group managers. + */ + @Override + public Set getManagers() { + return Collections.unmodifiableSet(managers); +} + + /** + * Makes the specified player a manager. + * + * @param player The id of the player to add. + * @return true if the player was added to the collection; otherwise, + * false if the player was already a member of the collection. + */ + @Override + public boolean addManager(UUID player) { + return managers.add(player); + } + + /** + * Removes the specified player being a group manager. + * + * @param player The unique id of the player to remove. + * @return true if the player was removed; otherwise, false. + */ + @Override + public boolean removeManager(UUID player) { + return managers.remove(player); + } + + /** + * Removes all players from the collection. + */ + @Override + public void clearManagers() { + managers.clear(); + } + + /** + * Gets a readonly list of all members in this collection. + * + * @return A readonly list of all members in this collection. + */ + @Override + public Set getMembers() { + return Collections.unmodifiableSet(players); + } + + /** + * Adds the specified player to the collection. + * + * @param player The unique id of the player to add. + * @return true if the player was added to the collection; otherwise, + * false if the player was already a member of the collection. + */ + @Override + public boolean addMember(UUID player) { + return players.add(player); + } + + /** + * Removes the specified player from the collection. + * + * @param player The unique id of the player to remove. + * @return true if the player was removed; otherwise, false. + */ + @Override + public boolean removeMember(UUID player) { + return players.remove(player); + } + + /** + * Removes all players from the collection. + */ + @Override + public void clearMembers() { + players.clear(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof LockProtectionGroup lockGroup)) { + return false; + } + return Objects.equals(id, lockGroup.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } +} diff --git a/src/main/java/com/github/fabricservertools/htm/api/ProtectionGroup.java b/src/main/java/com/github/fabricservertools/htm/api/ProtectionGroup.java new file mode 100644 index 0000000..0d5e20d --- /dev/null +++ b/src/main/java/com/github/fabricservertools/htm/api/ProtectionGroup.java @@ -0,0 +1,85 @@ +package com.github.fabricservertools.htm.api; + +import java.util.Set; +import java.util.UUID; + +/** + * Describes a group which is able to be applied to protection and holds users. + */ +public interface ProtectionGroup { + /** + * Gets the unique id of this group. + * @return The group id. + */ + UUID getId(); + + /** + * Gets the display name of the collection. + * @return The display name. + */ + String getName(); + + /** + * Sets the name of the collection. + * @param Name The new display name. + */ + void setName(String Name); + + /** + * Gets the id of the user who created this group. + * @return The {@see java.util.UUID} of the user who created this group. + */ + UUID getOwner(); + + /** + * Gets a readonly list of all group managers. + * @return A readonly list of group managers. + */ + Set getManagers(); + + /** + * Makes the specified player a manager. + * @param player The unique id of the player to add. + * @return true if the player was added to the collection; otherwise, + * false if the player was already a member of the collection. + */ + boolean addManager(UUID player); + + /** + * Removes the specified player being a group manager. + * @param player The unique id of the player to remove. + * @return true if the player was removed; otherwise, false. + */ + boolean removeManager(UUID player); + + /** + * Removes all players from the collection. + */ + void clearManagers(); + + /** + * Gets a readonly list of all members in this collection. + * @return A readonly list of all members in this collection. + */ + Set getMembers(); + + /** + * Adds the specified player to the collection. + * @param player The unique id of the player to add. + * @return true if the player was added to the collection; otherwise, + * false if the player was already a member of the collection. + */ + boolean addMember(UUID player); + + /** + * Removes the specified player from the collection. + * @param player The unique id of the player to remove. + * @return true if the player was removed; otherwise, false. + */ + boolean removeMember(UUID player); + + /** + * Removes all players from the collection. + */ + void clearMembers(); +} diff --git a/src/main/java/com/github/fabricservertools/htm/command/arguments/GroupArgument.java b/src/main/java/com/github/fabricservertools/htm/command/arguments/GroupArgument.java new file mode 100644 index 0000000..e8615e0 --- /dev/null +++ b/src/main/java/com/github/fabricservertools/htm/command/arguments/GroupArgument.java @@ -0,0 +1,13 @@ +package com.github.fabricservertools.htm.command.arguments; + +import com.github.fabricservertools.htm.api.ProtectionGroup; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +public class GroupArgument implements ArgumentType { + @Override + public ProtectionGroup parse(StringReader reader) throws CommandSyntaxException { + return null; + } +} diff --git a/src/main/java/com/github/fabricservertools/htm/command/subcommands/GroupCommands.java b/src/main/java/com/github/fabricservertools/htm/command/subcommands/GroupCommands.java new file mode 100644 index 0000000..decce5b --- /dev/null +++ b/src/main/java/com/github/fabricservertools/htm/command/subcommands/GroupCommands.java @@ -0,0 +1,150 @@ +package com.github.fabricservertools.htm.command.subcommands; + +import com.github.fabricservertools.htm.Utility; +import com.github.fabricservertools.htm.command.SubCommand; +import com.github.fabricservertools.htm.command.suggestors.LockGroupSuggestionProvider; +import com.github.fabricservertools.htm.interactions.InteractionManager; +import com.github.fabricservertools.htm.interactions.TrustAction; +import com.github.fabricservertools.htm.world.data.GlobalTrustState; +import com.mojang.authlib.GameProfile; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.LiteralCommandNode; +import me.lucko.fabric.api.permissions.v0.Permissions; +import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry; +import net.fabricmc.fabric.mixin.command.ArgumentTypesAccessor; +import net.minecraft.command.argument.ArgumentTypes; +import net.minecraft.command.argument.GameProfileArgumentType; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.apache.commons.lang3.NotImplementedException; + +import java.util.Collection; +import java.util.UUID; +import java.util.stream.Collectors; + +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +/** + * Defines commands which exist under the group node + */ +public class GroupCommands implements SubCommand { + + @Override + public LiteralCommandNode build() { + return literal("group") + .requires(Permissions.require("htm.command.group", true)) + .executes(ctx -> groupList(ctx, true)) + .then(literal("list") + .executes(ctx -> groupList(ctx, true)) + .then( + argument("owner", GameProfileArgumentType.gameProfile()) + .requires(Permissions.require("htm.command.group.listOther", false)) + .executes(ctx -> groupList(ctx, false)) + ) + ) + .then(literal("create") + .requires(Permissions.require("htm.command.group.create", true)) + .then( + argument("name", StringArgumentType.string()) + .executes(ctx -> createGroup(ctx.getSource(), StringArgumentType.getString(ctx, "name"))) + .then( + argument("owner", GameProfileArgumentType.gameProfile()) + .requires(Permissions.require("htm.command.group.create.other")) + .executes(ctx -> createGroupFor( + ctx.getSource(), + StringArgumentType.getString(ctx, "name"), + GameProfileArgumentType.getProfileArgument(ctx, "owner") + .stream() + .findFirst() + .orElseThrow() + .getId() + ) + ) + ) + ) + ) + .then(literal("delete") + .requires(Permissions.require("htm.command.group.delete")) + ) + .then(literal("search") + .requires(Permissions.require("htm.command.group.search")) + // TODO: By name + // TODO: By owner + // TODO: By UUID + ) + .then(literal("edit") + .requires(Permissions.require("htm.command.group.edit", true)) + .then(literal("name").then( + argument("group", StringArgumentType.string()) + .suggests(new LockGroupSuggestionProvider(false)) + .then( + argument("newName", StringArgumentType.string()) + .executes(ctx -> editGroupName( + ctx.getSource(), + StringArgumentType.getString(ctx, "group"), + StringArgumentType.getString(ctx, "newName") + ) + ) + ) + + ) + ) + .then(literal("trusted").then( + argument("group", StringArgumentType.string()) + .suggests(new LockGroupSuggestionProvider(true)) + .then(literal("add") + .requires() + .then( + argument("target", GameProfileArgumentType.gameProfile()) + )) + .then(literal("remove")) + ) + ) + .then(literal("managers")) + .then(literal("transfer")) + .then( + literal("other") + .requires(Permissions.require("htm.command.group.edit.others")) + .then( + argument("owner", GameProfileArgumentType.gameProfile()) + .then(literal("name").then( + argument("group", StringArgumentType.string() + //.suggests(new LockGroupSuggestionProvider(false, GameProfileArgumentType.getProfileArgument())) + ) + ) + .then(literal("trusted")) + .then(literal("managers")) + .then(literal("transfer")) + ) + ) + ) + .build(); + + + + } + + private int groupList(CommandContext context, boolean showForSelf) throws CommandSyntaxException { + return 1; + } + + private int createGroup(ServerCommandSource source, String groupName) throws CommandSyntaxException { + return createGroupFor(source, groupName, source.getPlayerOrThrow().getUuid()); + } + + private int createGroupFor(ServerCommandSource source, String groupName, UUID owner) { + return 1; + } + + private int editGroupName(ServerCommandSource source, String groupName, String newName) { + return 1; + } + + private void hasAddRemoveTrustPermissions(ServerCommandSource source, String groupName) { + + } +} diff --git a/src/main/java/com/github/fabricservertools/htm/command/suggestors/LockGroupSuggestionProvider.java b/src/main/java/com/github/fabricservertools/htm/command/suggestors/LockGroupSuggestionProvider.java new file mode 100644 index 0000000..15e9640 --- /dev/null +++ b/src/main/java/com/github/fabricservertools/htm/command/suggestors/LockGroupSuggestionProvider.java @@ -0,0 +1,81 @@ +package com.github.fabricservertools.htm.command.suggestors; + +import com.github.fabricservertools.htm.Utility; +import com.github.fabricservertools.htm.api.ProtectionGroup; +import com.github.fabricservertools.htm.world.data.LockGroupState; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; + +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +/** + * Provides suggestions for the names of Lock Groups. + */ +public class LockGroupSuggestionProvider implements SuggestionProvider { + + private final boolean suggestManagedGroups; + private Set groupsForOwner; + private boolean initialized = false; + private Optional suggestedOwner; + + public LockGroupSuggestionProvider(boolean suggestManagedGroups) + { + this(suggestManagedGroups, null); + } + + public LockGroupSuggestionProvider( + boolean suggestManagedGroups, + Optional owner + ) { + this.suggestManagedGroups = suggestManagedGroups; + this.suggestedOwner = owner; + } + + @Override + public CompletableFuture getSuggestions( + CommandContext context, + SuggestionsBuilder builder + ) throws CommandSyntaxException { + init(context.getSource()); + String current = builder.getRemaining(); + + for (ProtectionGroup protectionGroup : groupsForOwner) { + if (protectionGroup.getName().contains(current)) { + builder.suggest(protectionGroup.getName()); + } + } + + return builder.buildFuture(); + } + + private void init(ServerCommandSource source) throws CommandSyntaxException { + if (initialized) { + return; + } + LockGroupState lockGroupState = Utility.getLockGroupState(source.getServer()); + ServerPlayerEntity owner = suggestedOwner.orElse(source.getPlayerOrThrow()); + if (suggestManagedGroups) { + groupsForOwner = lockGroupState.getGroups() + .values() + .stream() + .filter(group -> group.getOwner() == owner.getUuid() || group.getManagers().contains(owner.getUuid())) + .collect(Collectors.toSet()); + } else { + groupsForOwner = lockGroupState.getGroups() + .values() + .stream() + .filter(group -> group.getOwner() == owner.getUuid()) + .collect(Collectors.toSet()); + } + + initialized = true; + } +} diff --git a/src/main/java/com/github/fabricservertools/htm/listeners/PlayerEventListener.java b/src/main/java/com/github/fabricservertools/htm/listeners/PlayerEventListener.java index 76da56c..c667e09 100644 --- a/src/main/java/com/github/fabricservertools/htm/listeners/PlayerEventListener.java +++ b/src/main/java/com/github/fabricservertools/htm/listeners/PlayerEventListener.java @@ -10,9 +10,15 @@ import com.github.fabricservertools.htm.interactions.InteractionManager; import net.fabricmc.fabric.api.event.player.AttackBlockCallback; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; +import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage; +import net.fabricmc.fabric.impl.transfer.item.InventoryStorageImpl; import net.minecraft.block.BlockState; +import net.minecraft.block.ComparatorBlock; +import net.minecraft.block.InventoryProvider; import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.InventoryChangedListener; import net.minecraft.item.ItemPlacementContext; import net.minecraft.registry.Registries; import net.minecraft.server.network.ServerPlayerEntity; @@ -23,7 +29,6 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.world.World; - import java.util.Optional; public class PlayerEventListener { @@ -31,6 +36,7 @@ public static void init() { PlayerPlaceBlockCallback.EVENT.register(PlayerEventListener::onPlace); PlayerBlockBreakEvents.BEFORE.register(PlayerEventListener::onBeforeBreak); AttackBlockCallback.EVENT.register(PlayerEventListener::onAttackBlock); + } private static ActionResult onAttackBlock(PlayerEntity player, World world, Hand hand, BlockPos pos, Direction direction) { diff --git a/src/main/java/com/github/fabricservertools/htm/locks/KeyLock.java b/src/main/java/com/github/fabricservertools/htm/locks/KeyLock.java index 919f174..157b114 100644 --- a/src/main/java/com/github/fabricservertools/htm/locks/KeyLock.java +++ b/src/main/java/com/github/fabricservertools/htm/locks/KeyLock.java @@ -24,8 +24,13 @@ public class KeyLock implements Lock { @Override public boolean canOpen(ServerPlayerEntity player, HTMContainerLock lock) { if (lock.isTrusted(player.getUuid())) return true; - if (Utility.getGlobalTrustState(player.server).isTrusted(lock.getOwner(), player.getUuid())) + + if (Utility.getLockGroupState(player.server).isTrusted(lock.getGroups(), player.getUuid())) { + return true; + } + if (Utility.getGlobalTrustState(player.server).isTrusted(lock.getOwner(), player.getUuid())) { return true; + } ItemStack itemStack = player.getMainHandStack(); return ItemStack.areItemsAndComponentsEqual(itemStack, key); diff --git a/src/main/java/com/github/fabricservertools/htm/locks/PrivateLock.java b/src/main/java/com/github/fabricservertools/htm/locks/PrivateLock.java index 285301d..6705b5c 100644 --- a/src/main/java/com/github/fabricservertools/htm/locks/PrivateLock.java +++ b/src/main/java/com/github/fabricservertools/htm/locks/PrivateLock.java @@ -12,6 +12,11 @@ public class PrivateLock implements Lock { @Override public boolean canOpen(ServerPlayerEntity player, HTMContainerLock lock) { if (lock.isTrusted(player.getUuid())) return true; + + if (Utility.getLockGroupState(player.server).isTrusted(lock.getGroupIds(), player.getUuid())) { + return true; + } + return Utility.getGlobalTrustState(player.server).isTrusted(lock.getOwner(), player.getUuid()); } diff --git a/src/main/java/com/github/fabricservertools/htm/world/data/LockGroupState.java b/src/main/java/com/github/fabricservertools/htm/world/data/LockGroupState.java new file mode 100644 index 0000000..a1787de --- /dev/null +++ b/src/main/java/com/github/fabricservertools/htm/world/data/LockGroupState.java @@ -0,0 +1,153 @@ +package com.github.fabricservertools.htm.world.data; + +import com.github.fabricservertools.htm.api.ProtectionGroup; +import com.github.fabricservertools.htm.api.LockProtectionGroup; +import com.google.common.collect.*; +import net.minecraft.nbt.*; +import net.minecraft.world.PersistentState; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Describes a persistent state which stores groups. + */ +public class LockGroupState extends PersistentState { + private final HashMap groups; + + private static final String LockGroups = "LockGroups"; + private static final String LockGroupId = "LockGroupId"; + private static final String LockGroupOwner = "LockGroupOwner"; + private static final String LockGroupName = "LockGroupName"; + private static final String LockGroupList = "LockGroupList"; + private static final String LockGroupManagers = "LockGroupManagers"; + + public LockGroupState() { + groups = new HashMap<>(); + } + + @Override + public NbtCompound writeNbt(NbtCompound nbt) { + NbtList trustList = new NbtList(); + + for (ProtectionGroup protectionGroup : groups.values()) { + NbtCompound groupTag = new NbtCompound(); + groupTag.putUuid(LockGroupId, protectionGroup.getId()); + groupTag.putUuid(LockGroupOwner, protectionGroup.getOwner()); + groupTag.putString(LockGroupName, protectionGroup.getName()); + + NbtList trustedUsers = new NbtList(); + for (UUID userId : protectionGroup.getMembers()) { + trustedUsers.add(NbtHelper.fromUuid(userId)); + } + groupTag.put(LockGroupList, trustedUsers); + + NbtList managers = new NbtList(); + for (UUID userId : protectionGroup.getManagers()) { + managers.add(NbtHelper.fromUuid(userId)); + } + groupTag.put(LockGroupManagers, managers); + + trustList.add(groupTag); + } + + nbt.put(LockGroups, trustList); + return nbt; + } + + public static LockGroupState fromNbt(NbtCompound tag) { + LockGroupState lockGroupState = new LockGroupState(); + NbtList lockGroupList = tag.getList(LockGroups, NbtElement.COMPOUND_TYPE); + + lockGroupList.forEach(it -> { + NbtCompound compoundTag = (NbtCompound)it; + UUID groupId = compoundTag.getUuid(LockGroupId); + UUID ownerId = compoundTag.getUuid(LockGroupOwner); + String groupName = compoundTag.getString(LockGroupName); + HashSet trusted = new HashSet<>(); + HashSet managers = new HashSet<>(); + + NbtList trustedList = compoundTag.getList(LockGroupList, NbtElement.INT_ARRAY_TYPE); + for (NbtElement value : trustedList) { + trusted.add(NbtHelper.toUuid(value)); + } + + NbtList managerList = compoundTag.getList(LockGroupManagers, NbtElement.INT_ARRAY_TYPE); + for (NbtElement value : managerList) { + managers.add(NbtHelper.toUuid(value)); + } + + lockGroupState.groups.put(groupId, new LockProtectionGroup(groupId, ownerId, trusted, managers, groupName)); + }); + + return lockGroupState; + } + + public boolean isTrusted(Set groupIds, UUID playerId) { + return groupIds.stream().anyMatch(it -> { + ProtectionGroup protectionGroup = groups.get(it); + if (protectionGroup == null) { + return false; + } + + if (protectionGroup.getMembers().contains(playerId)) return true; + if (protectionGroup.getManagers().contains(playerId)) return true; + return false; + }); + } + + public boolean isMember(UUID groupId, UUID trusted) { + return groups.get(groupId).getMembers().contains(trusted); + } + + public boolean isManager(UUID groupId, UUID manager) { + return groups.get(groupId).getManagers().contains(manager); + } + + public boolean addToGroup(UUID groupId, UUID trusted) { + ProtectionGroup protectionGroup = groups.get(groupId); + if (protectionGroup == null) { + return false; + } + + if (protectionGroup.addMember(trusted)) { + markDirty(); + return true; + } + + return false; + } + + public boolean removeFromGroup(UUID group, UUID trusted) { + if(groups.remove(group, trusted)) { + markDirty(); + return true; + } + return false; + } + + public boolean addGroup(ProtectionGroup protectionGroup) { + if (groups.containsKey(protectionGroup.getId())) { + return false; + } + + groups.put(protectionGroup.getId(), protectionGroup); + markDirty(); + return true; + } + + public boolean removeGroup(ProtectionGroup protectionGroup) { + if (groups.containsKey(protectionGroup.getId())) { + groups.remove(protectionGroup.getId()); + markDirty(); + return true; + } + return false; + } + + public ImmutableMap getGroups() { + return ImmutableMap.copyOf(groups); + } +}