diff --git a/README.md b/README.md index 7daf90c..a660c9d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # LitePermissions (Spigot / BungeeCord Plugin) **LitePermissions** is a lightweight, hybrid permissions plugin designed for **Spigot and BungeeCord** servers. -It aims to offer clean, fast, and flexible permission management across single or multi-server networks, with future support for web and GUI configuration. +It aims to offer clean, fast, and flexible permission management across single or multi-server networks, with full support for **multi-world** and **per-user overrides**. --- @@ -16,10 +16,10 @@ This plugin is currently in **active development**. Major features are being imp ### Core Features - βœ… **Custom Permissible Injection** (Spigot) -- πŸ”„ **GroupsData** – Group-based permission management -- πŸ”„ **UserData** – Per-player permission storage and overrides -- πŸ”„ **Reload Command** – Reload permissions without restarting -- πŸ”„ **Multi-world Permission Support** +- βœ… **Multi-world Permission Support** – Define permissions per world +- πŸ”„ **GroupsData** – Group-based permission management with weights +- πŸ”„ **UserData** – Per-player permission overrides +- πŸ”„ **Reload Command** – Reload permission data without restarting ### GUI & Web - πŸ”„ **GUI Editor** – In-game menus to manage permissions and groups @@ -32,16 +32,27 @@ This plugin is currently in **active development**. Major features are being imp --- -## πŸ“Š Permission Resolution Logic +## πŸ“Š How Permissions Are Decided -When checking permissions, **user-defined permissions always override group-defined ones**, and groups are sorted by weight: +LitePermissions checks permissions in this priority order: -| User Permission | Group Permission | Final Result | Source | -| --------------- | ---------------- | ------------ | ------------ | -| `true` | `false` | βœ… `true` | `user` | -| `false` | `true` | ❌ `false` | `user` | -| *(none)* | `true` | βœ… `true` | `group:name` | -| *(none)* | *(none)* | ❌ `false` | `none` | +1. βœ… User's **world-specific** permission +2. βœ… User's **global** permission +3. βœ… Group’s **world-specific** permission (highest-weight group only) +4. βœ… Group’s **global** permission (highest-weight group only) +5. ❌ If not found anywhere, permission is **denied by default** + +--- + +### πŸ” Examples + +| Where is it set? | User Value | Group Value | Final Result | Why? | +|---------------------|------------|-------------|--------------|----------------------------| +| In world settings | `true` | `false` | βœ… `true` | User's world setting wins | +| In world settings | `false` | `true` | ❌ `false` | User's world setting wins | +| Global (not world) | `true` | `false` | βœ… `true` | User's global setting wins | +| Not set for user | *(none)* | `true` | βœ… `true` | Comes from group | +| Not set anywhere | *(none)* | *(none)* | ❌ `false` | Denied by default | --- @@ -49,6 +60,6 @@ When checking permissions, **user-defined permissions always override group-defi > ⚠️ No public releases yet. Clone and build manually. -### Spigot +### Spigot Installation ```bash git clone https://git.triler.eu/JernejTDO/LitePermissions.git diff --git a/src/main/java/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.java b/src/main/java/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.java index 1093d73..63b64a1 100644 --- a/src/main/java/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.java +++ b/src/main/java/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.java @@ -1,6 +1,7 @@ package si.jernejtdo.LitePermissions.Spigot.Permissible; import java.util.Comparator; +import java.util.Map; import java.util.Optional; import org.bukkit.entity.Player; @@ -41,20 +42,35 @@ public class CustomPermissible extends PermissibleBase { boolean result = false; String source = "none"; + String world = player.getWorld().getName().toLowerCase(); + if (user != null) { - // 1. User-defined permission ALWAYS overrides - if (user.getPermissions().containsKey(inName)) { - result = user.getPermissions().get(inName); // true or false - source = "user"; + Map userWorld = user.getWorldPermissions(world); + Map userGlobal = user.getGlobalPermissions(); + + if (userWorld.containsKey(inName)) { + result = userWorld.get(inName); + source = "user (world)"; + } else if (userGlobal.containsKey(inName)) { + result = userGlobal.get(inName); + source = "user (global)"; } else { - // 2. Find highest-weight group that defines it - Optional groupWithPermission = user.getGroups().stream() - .filter(g -> g.getPermissions().containsKey(inName)) + Optional groupMatch = user.getGroups().stream() + .filter(g -> g.getWorldPermissions(world).containsKey(inName)) .max(Comparator.comparingInt(GroupData::getWeight)); - if (groupWithPermission.isPresent()) { - result = groupWithPermission.get().getPermissions().get(inName); - source = "group:" + groupWithPermission.get().getName(); + if (groupMatch.isPresent()) { + result = groupMatch.get().getWorldPermissions(world).get(inName); + source = "group:" + groupMatch.get().getName() + " (world)"; + } else { + groupMatch = user.getGroups().stream() + .filter(g -> g.getGlobalPermissions().containsKey(inName)) + .max(Comparator.comparingInt(GroupData::getWeight)); + + if (groupMatch.isPresent()) { + result = groupMatch.get().getGlobalPermissions().get(inName); + source = "group:" + groupMatch.get().getName() + " (global)"; + } } } } @@ -67,6 +83,7 @@ public class CustomPermissible extends PermissibleBase { + public Player getPlayer() { return player; } diff --git a/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/GroupData.java b/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/GroupData.java index fe74903..c53b661 100644 --- a/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/GroupData.java +++ b/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/GroupData.java @@ -7,18 +7,45 @@ public class GroupData { private String name; private String prefix; private String suffix; - private int weight = 0; // Default weight - private Map permissions = new HashMap<>(); + private int weight = 0; + + private final Map globalPermissions = new HashMap<>(); + private final Map> worldPermissions = new HashMap<>(); public GroupData(String name) { this.name = name; } - public boolean hasPermission(String permission) { - return permissions.getOrDefault(permission, false); + // Global + public void setPermission(String permission, boolean value) { + globalPermissions.put(permission.toLowerCase(), value); } - // --- Getters and setters --- + public Map getGlobalPermissions() { + return globalPermissions; + } + + // World-specific + public void setWorldPermission(String world, String permission, boolean value) { + worldPermissions + .computeIfAbsent(world.toLowerCase(), k -> new HashMap<>()) + .put(permission.toLowerCase(), value); + } + + public Map getWorldPermissions(String world) { + return worldPermissions.getOrDefault(world.toLowerCase(), new HashMap<>()); + } + + public boolean hasPermission(String permission, String world) { + return getWorldPermissions(world).getOrDefault(permission.toLowerCase(), + globalPermissions.getOrDefault(permission.toLowerCase(), false)); + } + + // Getters and setters... + + public String getName() { + return name; + } public int getWeight() { return weight; @@ -28,14 +55,6 @@ public class GroupData { this.weight = weight; } - public Map getPermissions() { - return permissions; - } - - public String getName() { - return name; - } - public String getPrefix() { return prefix; } @@ -51,8 +70,4 @@ public class GroupData { public void setSuffix(String suffix) { this.suffix = suffix; } - - public void setPermission(String perm, boolean value) { - permissions.put(perm, value); - } -} \ No newline at end of file +} diff --git a/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/UserData.java b/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/UserData.java index 30b60f1..29b1857 100644 --- a/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/UserData.java +++ b/src/main/java/si/jernejtdo/LitePermissions/Spigot/data/UserData.java @@ -1,26 +1,78 @@ package si.jernejtdo.LitePermissions.Spigot.data; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - import java.util.*; public class UserData { - private UUID uuid; + private final UUID uuid; private String prefix; private String suffix; - private final Map permissions = new HashMap<>(); + + private final Map globalPermissions = new HashMap<>(); + private final Map> worldPermissions = new HashMap<>(); private final Set groups = new HashSet<>(); public UserData(UUID uuid) { this.uuid = uuid; } + // --- Permission Management --- + + public void setPermission(String permission, boolean value) { + globalPermissions.put(permission.toLowerCase(), value); + } + + public Map getGlobalPermissions() { + return Collections.unmodifiableMap(globalPermissions); + } + + public void setWorldPermission(String world, String permission, boolean value) { + worldPermissions + .computeIfAbsent(world.toLowerCase(), k -> new HashMap<>()) + .put(permission.toLowerCase(), value); + } + + public Map getWorldPermissions(String world) { + return worldPermissions.getOrDefault(world.toLowerCase(), Collections.emptyMap()); + } + + public boolean hasPermission(String rawPermission, String rawWorld) { + final String permission = rawPermission.toLowerCase(); // βœ… FIXED + final String world = rawWorld.toLowerCase(); // βœ… FIXED + + // 1. User world-specific + Boolean userWorld = getWorldPermissions(world).get(permission); + if (userWorld != null) return userWorld; + + // 2. User global + Boolean userGlobal = globalPermissions.get(permission); + if (userGlobal != null) return userGlobal; + + // 3. Group world-specific + Optional groupMatch = groups.stream() + .filter(g -> g.getWorldPermissions(world).containsKey(permission)) + .max(Comparator.comparingInt(GroupData::getWeight)); + + if (groupMatch.isPresent()) { + return groupMatch.get().getWorldPermissions(world).get(permission); + } + + // 4. Group global + groupMatch = groups.stream() + .filter(g -> g.getGlobalPermissions().containsKey(permission)) + .max(Comparator.comparingInt(GroupData::getWeight)); + + if (groupMatch.isPresent()) { + return groupMatch.get().getGlobalPermissions().get(permission); + } + + // 5. Default deny + return false; + } + // --- Group Management --- public void addGroup(GroupData group) { - groups.add(group); + if (group != null) groups.add(group); } public void removeGroup(GroupData group) { @@ -28,7 +80,7 @@ public class UserData { } public Set getGroups() { - return groups; + return Collections.unmodifiableSet(groups); } public GroupData getPrimaryGroup() { @@ -37,23 +89,7 @@ public class UserData { .orElse(null); } - // --- Permission Logic --- - - public boolean hasPermission(String permission) { - // 1. User override - if (permissions.containsKey(permission)) { - return permissions.get(permission); - } - - // 2. Highest-weight group that defines the permission - return groups.stream() - .filter(g -> g.getPermissions().containsKey(permission)) - .max(Comparator.comparingInt(GroupData::getWeight)) - .map(g -> g.getPermissions().get(permission)) - .orElse(false); // default deny - } - - // --- Prefix/Suffix from Primary Group --- + // --- Prefix / Suffix --- public String getPrefix() { return prefix != null ? prefix : getPrimaryGroupPrefix(); @@ -65,21 +101,25 @@ public class UserData { private String getPrimaryGroupPrefix() { GroupData primary = getPrimaryGroup(); - return primary != null ? primary.getPrefix() : ""; + return primary != null && primary.getPrefix() != null ? primary.getPrefix() : ""; } private String getPrimaryGroupSuffix() { GroupData primary = getPrimaryGroup(); - return primary != null ? primary.getSuffix() : ""; + return primary != null && primary.getSuffix() != null ? primary.getSuffix() : ""; } - // --- Permission Map Access --- + // --- Setters / Misc --- - public Map getPermissions() { - return permissions; + public void setPrefix(String prefix) { + this.prefix = prefix; } - public void setPermission(String permission, boolean value) { - permissions.put(permission, value); + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public UUID getUuid() { + return uuid; } } diff --git a/target/classes/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.class b/target/classes/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.class index 4bf023f..918612c 100644 Binary files a/target/classes/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.class and b/target/classes/si/jernejtdo/LitePermissions/Spigot/Permissible/CustomPermissible.class differ