New modules again + some pom refactor

This commit is contained in:
Marc Baloup 2022-07-22 00:17:36 +02:00
parent f4d436671c
commit aff229164c
17 changed files with 1314 additions and 50 deletions

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>pandalib-parent</artifactId>
<groupId>fr.pandacube.lib</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pandalib-bungee-permissions</artifactId>
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-players-permissible</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-permissions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>${bungeecord.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,57 @@
package fr.pandacube.lib.bungee.permissions;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.PermissionCheckEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import fr.pandacube.lib.permissions.Permissions;
import fr.pandacube.lib.players.permissible.PermissibleOnlinePlayer;
import fr.pandacube.lib.players.permissible.PermissiblePlayerManager;
public class PandalibBungeePermissions implements Listener {
public static void init(Plugin bungeePlugin) {
ProxyServer.getInstance().getPluginManager().registerListener(bungeePlugin, new PandalibBungeePermissions());
}
@EventHandler(priority = Byte.MAX_VALUE)
public void onPermissionCheck(PermissionCheckEvent event)
{
CommandSender s = event.getSender();
if (s instanceof ProxiedPlayer p) {
event.setHasPermission(hasPerm(p, event.getPermission()));
}
else {
event.setHasPermission(true);
}
}
private volatile boolean tryPermPlayerManager = true;
private boolean hasPerm(ProxiedPlayer p, String permission) {
if (tryPermPlayerManager) {
try {
PermissiblePlayerManager<?, ?> pm = PermissiblePlayerManager.getInstance();
if (pm != null) {
PermissibleOnlinePlayer op = pm.get(p.getUniqueId());
if (op != null) {
return op.hasPermission(permission);
}
}
} catch (NoClassDefFoundError ignored) {
tryPermPlayerManager = false;
}
}
// if not using player manager, fallback to directly call permissions API
Server sv = p.getServer();
String server = sv == null ? null : sv.getInfo().getName();
return Permissions.getPlayer(p.getUniqueId()).hasPermissionOr(permission, server, null, false);
}
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>pandalib-parent</artifactId>
<groupId>fr.pandacube.lib</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pandalib-bungee-players</artifactId>
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-players-standalone</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-reflect</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>${bungeecord.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>${bungeecord.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,26 @@
package fr.pandacube.lib.bungee.players;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import fr.pandacube.lib.players.standalone.StandaloneOffPlayer;
public interface BungeeOffPlayer extends StandaloneOffPlayer {
/*
* Related class instances
*/
/**
* @return l'instance Bungee du joueur en ligne, ou null si il n'est pas en
* ligne
*/
default ProxiedPlayer getBungeeProxiedPlayer() {
return ProxyServer.getInstance().getPlayer(getUniqueId());
}
@Override
BungeeOnlinePlayer getOnlineInstance();
}

View File

@ -0,0 +1,208 @@
package fr.pandacube.lib.bungee.players;
import java.util.Locale;
import java.util.UUID;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import net.kyori.adventure.identity.Identified;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.SkinConfiguration;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.ProxiedPlayer.ChatMode;
import net.md_5.bungee.api.connection.ProxiedPlayer.MainHand;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.packet.ClientSettings;
import net.md_5.bungee.protocol.packet.PluginMessage;
import fr.pandacube.lib.chat.Chat;
import fr.pandacube.lib.players.standalone.StandaloneOnlinePlayer;
import fr.pandacube.lib.reflect.Reflect;
public interface BungeeOnlinePlayer extends BungeeOffPlayer, StandaloneOnlinePlayer {
@Override
ProxiedPlayer getBungeeProxiedPlayer();
/*
* Sending packet and stuff to player
*/
@Override
default void sendMessage(Component message) {
getBungeeProxiedPlayer().sendMessage(Chat.toBungee(message));
}
@Override
default void sendMessage(ComponentLike message, UUID sender) {
getBungeeProxiedPlayer().sendMessage(sender, Chat.toBungee(message.asComponent()));
}
@Override
default void sendMessage(Component message, Identified sender) {
getBungeeProxiedPlayer().sendMessage(sender.identity().uuid(), Chat.toBungee(message));
}
default void sendMessage(ComponentLike message, ProxiedPlayer sender) {
getBungeeProxiedPlayer().sendMessage(sender.getUniqueId(), Chat.toBungee(message.asComponent()));
}
@Override
default void sendTitle(Component title, Component subtitle, int fadeIn, int stay, int fadeOut) {
ProxyServer.getInstance().createTitle()
.title(Chat.toBungee(title)).subTitle(Chat.toBungee(subtitle))
.fadeIn(fadeIn).stay(stay).fadeOut(fadeOut)
.send(getBungeeProxiedPlayer());
}
@Override
default void sendServerBrand(String legacyText) {
try {
ByteBuf payload = ByteBufAllocator.DEFAULT.heapBuffer();
DefinedPacket.writeString(legacyText, payload);
getBungeeProxiedPlayer().unsafe().sendPacket(new PluginMessage("minecraft:brand", DefinedPacket.toArray(payload), false));
payload.release();
} catch (Exception ignored) { }
}
/*
* Client options
*/
@Override
default BungeeClientOptions getClientOptions() {
return new BungeeClientOptions(this);
}
class BungeeClientOptions implements StandaloneOnlinePlayer.ClientOptions {
BungeeOnlinePlayer op;
public BungeeClientOptions(BungeeOnlinePlayer op) {
this.op = op;
}
private ClientSettings getBungeeSettings() {
ProxiedPlayer pp = op.getBungeeProxiedPlayer();
try {
return (ClientSettings) Reflect.ofClassOfInstance(pp).method("getSettings").invoke(pp);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean hasChatColorEnabled() {
return op.getBungeeProxiedPlayer().hasChatColors();
}
public ChatMode getChatMode() {
return op.getBungeeProxiedPlayer().getChatMode();
}
@Override
public boolean isChatFullyVisible() {
ChatMode v = getChatMode();
return v == ChatMode.SHOWN || v == null;
}
@Override
public boolean isChatOnlyDisplayingSystemMessages() {
return getChatMode() == ChatMode.COMMANDS_ONLY;
}
@Override
public boolean isChatHidden() {
return getChatMode() == ChatMode.HIDDEN;
}
@Override
public Locale getLocale() {
return op.getBungeeProxiedPlayer().getLocale();
}
@Override
public int getViewDistance() {
return op.getBungeeProxiedPlayer().getViewDistance();
}
public MainHand getMainHand() {
return op.getBungeeProxiedPlayer().getMainHand();
}
@Override
public boolean isLeftHanded() {
return getMainHand() == MainHand.LEFT;
}
@Override
public boolean isRightHanded() {
return getMainHand() == MainHand.RIGHT;
}
@Override
public boolean isTextFilteringEnabled() {
ClientSettings settings = getBungeeSettings();
return settings != null && settings.isDisableTextFiltering(); // Bungee badly named the method
}
@Override
public boolean allowsServerListing() {
ClientSettings settings = getBungeeSettings();
return settings != null && settings.isAllowServerListing();
}
public SkinConfiguration getSkinParts() {
return op.getBungeeProxiedPlayer().getSkinParts();
}
@Override
public boolean hasSkinCapeEnabled() {
return getSkinParts().hasCape();
}
@Override
public boolean hasSkinJacketEnabled() {
return getSkinParts().hasJacket();
}
@Override
public boolean hasSkinLeftSleeveEnabled() {
return getSkinParts().hasLeftSleeve();
}
@Override
public boolean hasSkinRightSleeveEnabled() {
return getSkinParts().hasRightSleeve();
}
@Override
public boolean hasSkinLeftPantsEnabled() {
return getSkinParts().hasLeftPants();
}
@Override
public boolean hasSkinRightPantsEnabled() {
return getSkinParts().hasRightPants();
}
@Override
public boolean hasSkinHatsEnabled() {
return getSkinParts().hasHat();
}
}
}

View File

@ -18,20 +18,12 @@
</repositories> </repositories>
<dependencies> <dependencies>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId> <artifactId>bungeecord-api</artifactId>
<version>${bungeecord.version}</version> <version>${bungeecord.version}</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -30,6 +30,13 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-reflect</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-log</artifactId> <artifactId>bungeecord-log</artifactId>

View File

@ -35,36 +35,5 @@
<artifactId>pandalib-chat</artifactId> <artifactId>pandalib-chat</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-db</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-reflect</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-permissions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-players-standalone</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>2.0-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,10 +1,10 @@
# pandacube-net # pandalib-net
A TCP network library that uses the standard Java socket API, to ease the ommunication between the different processes A TCP network library that uses the standard Java socket API, to ease the ommunication between the different processes
running the server network Pandacube. running the server network Pandacube.
Its still in development (actually not touched since years), and its supposed to be a replacement for the old Its still in development (actually not touched since years), and its supposed to be a replacement for the old
`pandalib-network-api`. This module is then marked as Beta using the Google Guava annotation. `pandalib-netapi`. This module is then marked as Beta using the Google Guava annotation.
- Packet based communication - Packet based communication
- Supports Request/Answer packets - Supports Request/Answer packets

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>pandalib-parent</artifactId>
<groupId>fr.pandacube.lib</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pandalib-paper-permissions</artifactId>
<repositories>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<!-- WorldEdit -->
<repository>
<id>sk89q-repo</id>
<url>https://maven.enginehub.org/repo/</url>
</repository>
<!-- Vault and maybe other dependecies -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-players-permissible</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-permissions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-reflect</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Paper -->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${paper.version}-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Vault -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- WorldEdit -->
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId>
<version>7.2.9</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.sk89q.bukkit</groupId>
<artifactId>bukkit-classloader-check</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,143 @@
package fr.pandacube.lib.paper.permissions;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.ServerOperator;
import org.bukkit.plugin.java.JavaPlugin;
import fr.pandacube.lib.permissions.Permissions;
import fr.pandacube.lib.util.Log;
public class PandalibPaperPermissions implements Listener {
/* package */ static JavaPlugin plugin;
/* package */ static String serverName;
/* package */ static final Map<String, String> permissionMap = new HashMap<>();
public static void init(JavaPlugin plugin, String serverName) {
PandalibPaperPermissions.plugin = plugin;
PandalibPaperPermissions.serverName = serverName;
PermissionsInjectorBukkit.inject(Bukkit.getConsoleSender());
PermissionsInjectorVault.inject();
PermissionsInjectorWEPIF.inject();
Bukkit.getPluginManager().registerEvents(new PandalibPaperPermissions(), plugin);
}
/**
* Add the provided pair of permission into an internal permission map. This is used when a plugin asks the value of
* the sourcePerm, then the permission system actually check for the destPerm.
* <p>
* This mapping is useful, for instance, when the bukkit dispatcher force the fake vanilla commands to have a
* permission starting with {@code minecraft.command.} even if we defined a custom permission in the plugin.
* @param sourcePerm the source permission to replace
* @param destPerm the replacement permission
*/
public static void addPermissionMapping(String sourcePerm, String destPerm) {
Objects.requireNonNull(sourcePerm, "sourcePerm");
if (destPerm == null) {
permissionMap.remove(sourcePerm);
}
else {
permissionMap.put(sourcePerm, destPerm);
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent event) {
Permissions.clearPlayerCache(event.getPlayer().getUniqueId());
Permissions.precachePlayerAsync(event.getPlayer().getUniqueId());
PermissionsInjectorBukkit.inject(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
PermissionsInjectorBukkit.uninject(event.getPlayer());
Permissions.clearPlayerCache(event.getPlayer().getUniqueId());
}
/* package */ static Function<String, List<Permission>> SUPERPERMS_PARENT_PERMISSION_GETTER = childPerm -> {
return Bukkit.getPluginManager().getPermissions().stream()
.filter(p -> p.getChildren().containsKey(childPerm))
.collect(Collectors.toList());
};
/* package */ static ServerOperator dummyOperator(boolean isOp) {
return new ServerOperator() {
@Override public void setOp(boolean op) { }
@Override public boolean isOp() { return isOp; }
};
}
/* package */ static Boolean hasSuperPermsPermission(ServerOperator opable, String permission, Predicate<String> parentPermissionChecker) {
if (opable instanceof CommandSender sender) {
Permissible permissible = PermissionsInjectorBukkit.getPermissible(sender);
if (permissible instanceof PermissionsInjectorBukkit.PandaPermissible pPerm)
return hasSuperPermsPermission(opable, permission, parentPermissionChecker, pPerm);
}
return hasSuperPermsPermission(opable, permission, parentPermissionChecker, null);
}
/* package */ static Boolean hasSuperPermsPermission(ServerOperator opable, String permission, Predicate<String> parentPermissionChecker, PermissionsInjectorBukkit.PandaPermissible pandaPermissible) {
boolean reversed = permission.startsWith("-");
if (reversed) {
permission = permission.substring(1);
}
boolean defined = false;
Permission perm = Bukkit.getPluginManager().getPermission(permission);
if (perm != null) {
if (perm.getDefault().getValue(opable.isOp()))
return !reversed;
defined = true;
}
try {
List<Permission> parents = pandaPermissible != null ? pandaPermissible.superPermsPermissionCache.get(permission) : SUPERPERMS_PARENT_PERMISSION_GETTER.apply(permission);
for (Permission parent : parents) {
Boolean childValue = parent.getChildren().get(permission);
if (childValue == null)
continue;
boolean parentPerm = parentPermissionChecker.test(parent.getName());
if (parentPerm == childValue)
return !reversed;
defined = true;
}
} catch (ExecutionException e) {
Log.severe("Unable to compute SuperPerms permission", e);
}
Boolean ret = defined ? reversed : null;
if (Log.isDebugEnabled()) {
String name = (opable instanceof CommandSender cs) ? cs.getName() : "unknown entity";
String actualPerm = permission;
if (reversed) actualPerm = "-" + permission;
Log.debug("[SuperPerms] For " + name + ", '" + actualPerm + "' is " + ret);
}
return ret;
}
}

View File

@ -0,0 +1,313 @@
package fr.pandacube.lib.paper.permissions;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import fr.pandacube.lib.permissions.Permissions;
import fr.pandacube.lib.reflect.Reflect;
import fr.pandacube.lib.util.Log;
public class PermissionsInjectorBukkit
{
// to be called : onEnable for console, onPlayerLogin (not Join) (Priority LOWEST) for players
public static void inject(CommandSender sender) {
Permissible oldP = getPermissible(sender);
if (oldP instanceof PandaPermissible)
return;
if (!oldP.getClass().equals(PermissibleBase.class)) {
Log.warning("Another plugin is already injecting permissions into Bukkit for " + sender.getName() + ": " + oldP.getClass().getName());
Log.warning("Not injecting our own permissions.");
}
PandaPermissible p = new PandaPermissible(sender, getPermissible(sender));
setPermissible(p.sender, p);
p.recalculatePermissions();
}
// to be called : onDisable for console, onPlayerQuit () and onDisable for players
public static void uninject(CommandSender sender)
{
Permissible perm = getPermissible(sender);
if (perm instanceof PandaPermissible p) {
setPermissible(sender, p.oldPermissible);
p.oldPermissible.recalculatePermissions();
}
}
private static void setPermissible(CommandSender sender, Permissible newpermissible)
{
try {
Field perm = getPermField(sender);
if (perm == null)
return;
perm.setAccessible(true);
perm.set(sender, newpermissible);
}
catch (Exception e) {
Log.severe(e);
}
}
/* package */ static Permissible getPermissible(CommandSender sender)
{
Field perm = getPermField(sender);
if (perm == null)
return null;
try {
perm.setAccessible(true);
Permissible p = (Permissible) perm.get(sender);
if (p == null) {
Log.warning("Null permissible instance found in provided CommandSender: " + sender, new Throwable());
}
return p;
}
catch (Exception e) {
Log.severe(e);
}
return null;
}
private static Field getPermField(CommandSender sender)
{
if (sender == null) {
throw new IllegalArgumentException("sender cannot be null");
}
try {
if (sender instanceof Player || sender instanceof ConsoleCommandSender)
return Reflect.ofClassOfInstance(sender).field("perm").get();
else
throw new IllegalArgumentException("Unsupported type for sender: " + sender.getClass());
}
catch (Exception e) {
Log.severe(e);
}
return null;
}
/* package */ static class PandaPermissible extends PermissibleBase
{
private final CommandSender sender;
private final Permissible oldPermissible;
/* package */ LoadingCache<String, List<Permission>> superPermsPermissionCache = CacheBuilder.newBuilder()
.build(CacheLoader.from(PandalibPaperPermissions.SUPERPERMS_PARENT_PERMISSION_GETTER::apply));
private boolean init = false;
private PandaPermissible(CommandSender sender, Permissible oldPermissible)
{
super(sender);
this.sender = sender;
this.oldPermissible = oldPermissible;
init = true;
recalculatePermissions();
}
private Boolean hasPermissionOnServerInWorld(String permission) {
if (sender instanceof Player player) {
String world = player.getWorld().getName();
return Permissions.getPlayer(player.getUniqueId()).hasPermission(permission, PandalibPaperPermissions.serverName, world);
}
return true;
}
@Override
public boolean hasPermission(String permission)
{
/*
* WARNING: dont call PermissibleOnlinePlayer#hasPermission(String) here or it will result on a stack overflow
*/
if (permission.toLowerCase().startsWith("minecraft.command."))
permission = PandalibPaperPermissions.permissionMap.getOrDefault(permission.toLowerCase(), permission);
Boolean res = hasPermissionOnServerInWorld(permission); // supports negative permission
if (res != null)
return res;
res = PandalibPaperPermissions.hasSuperPermsPermission(sender, permission, this::hasPermission, this); // supports negative permission
if (res != null)
return res;
boolean reversed = permission.startsWith("-");
if (reversed) {
permission = permission.substring(1);
}
return oldPermissible.hasPermission(permission) != reversed;
}
@Override
public boolean hasPermission(Permission permission)
{
if (permission.getName().toLowerCase().startsWith("minecraft.command.") && PandalibPaperPermissions.permissionMap.containsKey(permission.getName().toLowerCase())) {
return hasPermission(PandalibPaperPermissions.permissionMap.get(permission.getName().toLowerCase()));
}
Boolean res = hasPermissionOnServerInWorld(permission.getName()); // supports negative permission
if (res != null)
return res;
res = PandalibPaperPermissions.hasSuperPermsPermission(sender, permission.getName(), this::hasPermission, this); // supports negative permission
if (res != null)
return res;
return oldPermissible.hasPermission(permission); // doesnt need to manage negative permission (should not happend)
}
@Override
public void recalculatePermissions()
{
// need this check because super class constructor calls this method,
// thus before oldPermissible has its value assigned
if (!init)
return;
oldPermissible.recalculatePermissions();
superPermsPermissionCache.invalidateAll();
effectivePermissionsListCache.invalidateAll();
}
private Map<String, Boolean> getEffectivePermissionsOnServerInWorld() {
if (sender instanceof Player player) {
String world = player.getWorld().getName();
return Permissions.getPlayer(player.getUniqueId()).listEffectivePermissions(PandalibPaperPermissions.serverName, world);
}
return new HashMap<>();
}
// key is world
private final Cache<String, Set<PermissionAttachmentInfo>> effectivePermissionsListCache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build();
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions()
{
// PlotSquared uses this method to optimize permission range (plots.limit.10 for example)
// MobArena uses this method when a player leave the arena
// LibsDisguises uses this method (and only this one) to parse all the permissions
//Log.warning("There is a plugin calling CommandSender#getEffectivePermissions(). See the stacktrace to understand the reason for that.", new Throwable());
String world = null;
if (sender instanceof Player player) {
world = player.getWorld().getName();
}
try {
return effectivePermissionsListCache.get(world, () -> {
// first get the superperms effective permissions (taht take isOp into accound)
Map<String, PermissionAttachmentInfo> perms = oldPermissible.getEffectivePermissions().stream()
.collect(Collectors.toMap(PermissionAttachmentInfo::getPermission, Function.identity()));
// then override them with the permissions from our permission system (that has priority, and take current world into account)
for (Map.Entry<String, Boolean> permE : getEffectivePermissionsOnServerInWorld().entrySet()) {
perms.put(permE.getKey(), new PermissionAttachmentInfo(this, permE.getKey(), null, permE.getValue()));
}
return new LinkedHashSet<>(perms.values());
});
} catch (ExecutionException e) {
Log.severe(e);
return oldPermissible.getEffectivePermissions();
}
}
@Override
public boolean isOp()
{
return oldPermissible.isOp();
}
@Override
public void setOp(boolean value)
{
oldPermissible.setOp(value);
}
@Override
public boolean isPermissionSet(String permission)
{
Boolean res = hasPermissionOnServerInWorld(permission);
if (res != null)
return true;
return oldPermissible.isPermissionSet(permission);
}
@Override
public boolean isPermissionSet(Permission permission)
{
Boolean res = hasPermissionOnServerInWorld(permission.getName());
if (res != null)
return true;
return oldPermissible.isPermissionSet(permission);
}
@Override
public PermissionAttachment addAttachment(Plugin plugin)
{
return oldPermissible.addAttachment(plugin);
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks)
{
return oldPermissible.addAttachment(plugin, ticks);
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value)
{
return oldPermissible.addAttachment(plugin, name, value);
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks)
{
return oldPermissible.addAttachment(plugin, name, value, ticks);
}
@Override
public void removeAttachment(PermissionAttachment attachment)
{
oldPermissible.removeAttachment(attachment);
}
@Override
public synchronized void clearPermissions()
{
if (oldPermissible instanceof PermissibleBase)
((PermissibleBase) oldPermissible).clearPermissions();
}
}
}

View File

@ -0,0 +1,255 @@
package fr.pandacube.lib.paper.permissions;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.plugin.ServicePriority;
import fr.pandacube.lib.permissions.PermGroup;
import fr.pandacube.lib.permissions.Permissions;
import fr.pandacube.lib.util.Log;
public class PermissionsInjectorVault {
public static PandaVaultPermission permInstance;
public static void inject() {
try {
permInstance = new PandaVaultPermission();
PandaVaultChat chat = new PandaVaultChat(permInstance);
Bukkit.getServicesManager().register(net.milkbowl.vault.permission.Permission.class, permInstance,
PandalibPaperPermissions.plugin, ServicePriority.High);
Bukkit.getServicesManager().register(net.milkbowl.vault.chat.Chat.class, chat,
PandalibPaperPermissions.plugin, ServicePriority.High);
Log.info("Providing permissions and chat prefix/suffix through Vault API.");
} catch (NoClassDefFoundError e) {
Log.warning("Vault plugin not detected. Not using it to provide permissions and prefix/suffix." + e.getMessage());
}
}
public static class PandaVaultPermission extends net.milkbowl.vault.permission.Permission {
private PandaVaultPermission() { }
@Override
public String getName() {
return "Pandalib";
}
@Override
public boolean isEnabled() {
return PandalibPaperPermissions.plugin != null && PandalibPaperPermissions.plugin.isEnabled();
}
@Override
public boolean hasSuperPermsCompat() {
return true;
}
@Deprecated
@Override
public boolean playerHas(String world, String player, String permission) {
return playerHas(world, Bukkit.getOfflinePlayer(player), permission);
}
@Override
public boolean playerHas(String world, OfflinePlayer player, String permission) {
Boolean res = Permissions.getPlayer(player.getUniqueId()).hasPermission(permission, PandalibPaperPermissions.serverName, world);
if (res != null)
return res;
res = PandalibPaperPermissions.hasSuperPermsPermission(player, permission, p -> playerHas(world, player, p));
if (res != null)
return res;
return permission.startsWith("-");
}
@Override
public boolean playerAdd(String world, String player, String permission) {
return false;
}
@Override
public boolean playerRemove(String world, String player, String permission) {
return false;
}
@Override
public boolean groupHas(String world, String group, String permission) {
Boolean res = Permissions.getGroup(group).hasPermission(permission, PandalibPaperPermissions.serverName, world);
if (res != null)
return res;
res = PandalibPaperPermissions.hasSuperPermsPermission(PandalibPaperPermissions.dummyOperator(false), permission, p -> groupHas(world, group, p), null);
if (res != null)
return res;
return permission.startsWith("-");
}
@Override
public boolean groupAdd(String world, String group, String permission) {
return false;
}
@Override
public boolean groupRemove(String world, String group, String permission) {
return false;
}
@Deprecated
@Override
public boolean playerInGroup(String world, String player, String group) {
return playerInGroup(world, Bukkit.getOfflinePlayer(player), group);
}
@Override
public boolean playerInGroup(String world, OfflinePlayer player, String group) {
return Permissions.getPlayer(player.getUniqueId()).isInGroup(group);
}
@Override
public boolean playerAddGroup(String world, String player, String group) {
return false;
}
@Override
public boolean playerRemoveGroup(String world, String player, String group) {
return false;
}
@Deprecated
@Override
public String[] getPlayerGroups(String world, String player) {
return getPlayerGroups(world, Bukkit.getOfflinePlayer(player));
}
@Override
public String[] getPlayerGroups(String world, OfflinePlayer player) {
List<String> groups = Permissions.getPlayer(player.getUniqueId()).getGroupsString();
return groups.toArray(new String[0]);
}
@Deprecated
@Override
public String getPrimaryGroup(String world, String player) {
return getPrimaryGroup(world, Bukkit.getOfflinePlayer(player));
}
@Override
public String getPrimaryGroup(String world, OfflinePlayer player) {
return Permissions.getPlayer(player.getUniqueId()).getGroupsString().stream()
.findFirst().orElse(null);
}
@Override
public String[] getGroups() {
return Permissions.getGroups().stream()
.map(PermGroup::getName).toArray(String[]::new);
}
@Override
public boolean hasGroupSupport() {
return true;
}
}
private static class PandaVaultChat extends net.milkbowl.vault.chat.Chat {
public PandaVaultChat(net.milkbowl.vault.permission.Permission perms) {
super(perms);
}
@Override
public String getName() {
return "Pandalib";
}
@Override
public boolean isEnabled() {
return PandalibPaperPermissions.plugin != null && PandalibPaperPermissions.plugin.isEnabled();
}
@Deprecated
@Override
public String getPlayerPrefix(String world, String player) {
return getPlayerPrefix(world, Bukkit.getOfflinePlayer(player));
}
@Override
public String getPlayerPrefix(String world, OfflinePlayer player) {
return Permissions.getPlayer(player.getUniqueId()).getPrefix();
}
@Deprecated
@Override
public String getPlayerSuffix(String world, String player) {
return getPlayerSuffix(world, Bukkit.getOfflinePlayer(player));
}
@Override
public String getPlayerSuffix(String world, OfflinePlayer player) {
return Permissions.getPlayer(player.getUniqueId()).getSuffix();
}
@Override
public String getGroupPrefix(String world, String group) {
return Permissions.getGroup(group).getPrefix();
}
@Override
public String getGroupSuffix(String world, String group) {
return Permissions.getGroup(group).getSuffix();
}
@Override
public void setPlayerPrefix(String world, String player, String prefix) { /* unsupported */ }
@Override
public void setPlayerSuffix(String world, String player, String suffix) { /* unsupported */ }
@Override
public void setGroupPrefix(String world, String group, String prefix) { /* unsupported */ }
@Override
public void setGroupSuffix(String world, String group, String suffix) { /* unsupported */ }
@Override
public int getPlayerInfoInteger(String world, String player, String node, int defaultValue) { return defaultValue; }
@Override
public void setPlayerInfoInteger(String world, String player, String node, int value) { /* unsupported */ }
@Override
public int getGroupInfoInteger(String world, String group, String node, int defaultValue) { return defaultValue; }
@Override
public void setGroupInfoInteger(String world, String group, String node, int value) { /* unsupported */ }
@Override
public double getPlayerInfoDouble(String world, String player, String node, double defaultValue) { return defaultValue; }
@Override
public void setPlayerInfoDouble(String world, String player, String node, double value) { /* unsupported */ }
@Override
public double getGroupInfoDouble(String world, String group, String node, double defaultValue) { return defaultValue; }
@Override
public void setGroupInfoDouble(String world, String group, String node, double value) { /* unsupported */ }
@Override
public boolean getPlayerInfoBoolean(String world, String player, String node, boolean defaultValue) { return defaultValue; }
@Override
public void setPlayerInfoBoolean(String world, String player, String node, boolean value) { /* unsupported */ }
@Override
public boolean getGroupInfoBoolean(String world, String group, String node, boolean defaultValue) { return defaultValue; }
@Override
public void setGroupInfoBoolean(String world, String group, String node, boolean value) { /* unsupported */ }
@Override
public String getPlayerInfoString(String world, String player, String node, String defaultValue) { return defaultValue; }
@Override
public void setPlayerInfoString(String world, String player, String node, String value) { /* unsupported */ }
@Override
public String getGroupInfoString(String world, String group, String node, String defaultValue) { return defaultValue; }
@Override
public void setGroupInfoString(String world, String group, String node, String value) { /* unsupported */ }
}
}

View File

@ -0,0 +1,93 @@
package fr.pandacube.lib.paper.permissions;
import java.util.List;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.ServicePriority;
import fr.pandacube.lib.permissions.PermPlayer;
import fr.pandacube.lib.permissions.Permissions;
import fr.pandacube.lib.util.Log;
public class PermissionsInjectorWEPIF {
public static PandaWEPIFPermissionsProvider permInstance;
public static void inject() {
try {
permInstance = new PandaWEPIFPermissionsProvider();
Bukkit.getServicesManager().register(com.sk89q.wepif.PermissionsProvider.class, permInstance,
PandalibPaperPermissions.plugin, ServicePriority.Highest);
Log.info("Providing permissions through WEPIF");
Plugin pl = Bukkit.getPluginManager().getPlugin("WorldEdit");
if (pl == null || !pl.isEnabled())
return;
((WorldEditPlugin) pl).getPermissionsResolver().findResolver();
} catch (NoClassDefFoundError e) {
Log.warning("WorldEdit plugin not detected. Not using WEPIF to provide permissions and prefix/suffix." + e.getMessage());
}
}
public static class PandaWEPIFPermissionsProvider implements com.sk89q.wepif.PermissionsProvider {
private PandaWEPIFPermissionsProvider() { }
private PermPlayer getPlayer(OfflinePlayer player) {
return Permissions.getPlayer(player.getUniqueId());
}
@Override
public String[] getGroups(OfflinePlayer player) {
List<String> groups = getPlayer(player).getGroupsString();
return groups.toArray(new String[0]);
}
@Deprecated
@Override
public String[] getGroups(String player) {
return getGroups(Bukkit.getOfflinePlayer(player));
}
@Override
public boolean hasPermission(OfflinePlayer player, String permission) {
Player p = Bukkit.getPlayer(player.getUniqueId());
return hasPermission(p != null ? p.getWorld().getName() : null, player, permission);
}
@Deprecated
@Override
public boolean hasPermission(String player, String permission) {
return hasPermission(Bukkit.getOfflinePlayer(player), permission);
}
@Override
public boolean hasPermission(String worldName, OfflinePlayer player, String permission) {
Boolean res = Permissions.getPlayer(player.getUniqueId()).hasPermission(permission, PandalibPaperPermissions.serverName, worldName);
if (res != null)
return res;
res = PandalibPaperPermissions.hasSuperPermsPermission(player, permission, p -> hasPermission(worldName, player, p));
if (res != null)
return res;
return permission.startsWith("-");
}
@Deprecated
@Override
public boolean hasPermission(String worldName, String player, String permission) {
return hasPermission(worldName, Bukkit.getOfflinePlayer(player), permission);
}
@Override
public boolean inGroup(OfflinePlayer player, String group) {
return getPlayer(player).isInGroup(group);
}
@Deprecated
@Override
public boolean inGroup(String player, String group) {
return inGroup(Bukkit.getOfflinePlayer(player), group);
}
}
}

View File

@ -24,7 +24,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>fr.pandacube.lib</groupId> <groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-core</artifactId> <artifactId>pandalib-chat</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -38,16 +38,30 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-reflect</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-util</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Paper --> <!-- Paper -->
<dependency> <dependency>
<groupId>io.papermc.paper</groupId> <groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId> <artifactId>paper-api</artifactId>
<version>${paper.version}-SNAPSHOT</version> <version>${paper.version}-SNAPSHOT</version>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.papermc.paper</groupId> <groupId>io.papermc.paper</groupId>
<artifactId>paper-mojangapi</artifactId> <artifactId>paper-mojangapi</artifactId>
<version>${paper.version}-SNAPSHOT</version> <version>${paper.version}-SNAPSHOT</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -23,7 +23,6 @@
<groupId>fr.pandacube.lib</groupId> <groupId>fr.pandacube.lib</groupId>
<artifactId>pandalib-players-standalone</artifactId> <artifactId>pandalib-players-standalone</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -59,6 +59,9 @@
<module>pandalib-reflect</module> <module>pandalib-reflect</module>
<module>pandalib-util</module> <module>pandalib-util</module>
<module>pandalib-players-permissible</module> <module>pandalib-players-permissible</module>
<module>pandalib-bungee-permissions</module>
<module>pandalib-bungee-players</module>
<module>pandalib-paper-permissions</module>
</modules> </modules>
<dependencies> <dependencies>