This commit is contained in:
2025-07-11 21:33:16 +02:00
parent 36cd63941b
commit 84ffc409cb
5 changed files with 157 additions and 74 deletions

View File

@@ -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. ✅ Groups **world-specific** permission (highest-weight group only)
4. ✅ Groups **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

View File

@@ -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<String, Boolean> userWorld = user.getWorldPermissions(world);
Map<String, Boolean> 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<GroupData> groupWithPermission = user.getGroups().stream()
.filter(g -> g.getPermissions().containsKey(inName))
Optional<GroupData> 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;
}

View File

@@ -7,18 +7,45 @@ public class GroupData {
private String name;
private String prefix;
private String suffix;
private int weight = 0; // Default weight
private Map<String, Boolean> permissions = new HashMap<>();
private int weight = 0;
private final Map<String, Boolean> globalPermissions = new HashMap<>();
private final Map<String, Map<String, Boolean>> 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<String, Boolean> 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<String, Boolean> 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<String, Boolean> 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);
}
}
}

View File

@@ -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<String, Boolean> permissions = new HashMap<>();
private final Map<String, Boolean> globalPermissions = new HashMap<>();
private final Map<String, Map<String, Boolean>> worldPermissions = new HashMap<>();
private final Set<GroupData> 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<String, Boolean> 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<String, Boolean> 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<GroupData> 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<GroupData> 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<String, Boolean> 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;
}
}