This commit is contained in:
i509VCB 2021-01-04 09:23:54 -06:00 committed by GitHub
parent 0a99a96188
commit 21abb94796
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2709 additions and 5 deletions

View File

@ -23,4 +23,4 @@ The project is split up into a few separate modules.
* **API** - The public, semantically versioned API used by other plugins wishing to integrate with and retrieve data from LuckPerms. This module (for the most part) does not contain any implementation itself, and is provided by the plugin.
* **Common** - The common module contains most of the code which implements the respective LuckPerms plugins. This abstract module reduces duplicated code throughout the project.
* **Bukkit, BungeeCord, Sponge, Nukkit & Velocity** - Each use the common module to implement plugins on the respective server platforms.
* **Bukkit, BungeeCord, Sponge, Nukkit, Velocity & Fabric** - Each use the common module to implement plugins on the respective server platforms.

View File

@ -73,7 +73,8 @@ public interface Platform {
BUNGEECORD("BungeeCord"),
SPONGE("Sponge"),
NUKKIT("Nukkit"),
VELOCITY("Velocity");
VELOCITY("Velocity"),
FABRIC("Fabric");
private final String friendlyName;

View File

@ -9,7 +9,10 @@ dependencies {
testCompile 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
compile project(':api')
compile 'org.checkerframework:checker-qual:2.5.5'
compile 'org.checkerframework:checker-qual:3.8.0'
compileOnly 'org.slf4j:slf4j-api:1.7.30'
compileOnly 'org.apache.logging.log4j:log4j-api:2.14.0'
// This is a special re-packaged version of 'net.kyori:adventure-*' for our own use.
// Don't use it in other projects, you want the net.kyori version instead.

View File

@ -483,6 +483,11 @@ public final class ConfigKeys {
*/
public static final ConfigKey<Boolean> VAULT_IGNORE_WORLD = booleanKey("vault-ignore-world", false);
/**
* If the owner of an integrated server should automatically bypasses all permission checks. On fabric, this only applies on an Integrated Server.
*/
public static final ConfigKey<Boolean> FABRIC_INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS = booleanKey("integrated-server-owner-bypasses-checks", true);
/**
* The world rewrites map
*/

View File

@ -138,6 +138,7 @@ public interface LuckPermsBootstrap {
* <p>Bukkit: /root/plugins/LuckPerms</p>
* <p>Bungee: /root/plugins/LuckPerms</p>
* <p>Sponge: /root/luckperms/</p>
* <p>Fabric: /root/mods/LuckPerms</p>
*
* @return the platforms data folder
*/

View File

@ -0,0 +1,61 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.plugin.logging;
import org.apache.logging.log4j.Logger;
public class Log4jPluginLogger implements PluginLogger {
private final Logger logger;
public Log4jPluginLogger(Logger logger) {
this.logger = logger;
}
@Override
public void info(String s) {
this.logger.info(s);
}
@Override
public void warn(String s) {
this.logger.warn(s);
}
@Override
public void warn(String s, Throwable t) {
this.logger.warn(s, t);
}
@Override
public void severe(String s) {
this.logger.error(s);
}
@Override
public void severe(String s, Throwable t) {
this.logger.error(s, t);
}
}

94
fabric/build.gradle Normal file
View File

@ -0,0 +1,94 @@
import net.fabricmc.loom.task.RemapJarTask
plugins {
id 'com.github.johnrengelman.shadow' version '4.0.1'
id 'fabric-loom' version '0.5-SNAPSHOT'
}
repositories {
maven { url 'https://maven.fabricmc.net/' }
mavenLocal()
}
def minecraftVersion = '1.16.4'
def yarnBuild = 7
def loaderVersion = '0.10.8'
def fabricApiVersion = '0.28.4+1.16'
dependencies {
// Fabric Stuff, We don't specifically target only a single version but yarn mappings require a version to be specified.
minecraft "com.mojang:minecraft:${minecraftVersion}"
mappings "net.fabricmc:yarn:${minecraftVersion}+build.${yarnBuild}:v2"
modImplementation "net.fabricmc:fabric-loader:${loaderVersion}"
Set<String> apiModules = [
'fabric-api-base',
'fabric-command-api-v1',
'fabric-lifecycle-events-v1',
'fabric-networking-api-v1'
]
apiModules.forEach {
modImplementation(fabricApi.module(it, fabricApiVersion))
}
include(modImplementation('me.lucko:fabric-permissions-api:0.1-SNAPSHOT'))
compile project(':common')
}
processResources {
inputs.property 'version', project.ext.fullVersion
from(sourceSets.main.resources.srcDirs) {
include 'fabric.mod.json'
expand 'version': project.ext.fullVersion
}
from(sourceSets.main.resources.srcDirs) {
exclude 'fabric.mod.json'
}
}
shadowJar {
archiveName = "LuckPerms-Fabric-${project.ext.fullVersion}-dev.jar"
dependencies {
exclude('net.fabricmc:.*')
include(dependency('net.luckperms:.*'))
include(dependency('me.lucko.luckperms:.*'))
// We don't want to include the mappings in the jar do we?
exclude '/mappings/*'
}
relocate 'net.kyori.adventure', 'me.lucko.luckperms.lib.adventure'
relocate 'net.kyori.event', 'me.lucko.luckperms.lib.eventbus'
relocate 'com.github.benmanes.caffeine', 'me.lucko.luckperms.lib.caffeine'
relocate 'okio', 'me.lucko.luckperms.lib.okio'
relocate 'okhttp3', 'me.lucko.luckperms.lib.okhttp3'
relocate 'net.bytebuddy', 'me.lucko.luckperms.lib.bytebuddy'
relocate 'me.lucko.commodore', 'me.lucko.luckperms.lib.commodore'
relocate 'org.mariadb.jdbc', 'me.lucko.luckperms.lib.mariadb'
relocate 'com.mysql', 'me.lucko.luckperms.lib.mysql'
relocate 'org.postgresql', 'me.lucko.luckperms.lib.postgresql'
relocate 'com.zaxxer.hikari', 'me.lucko.luckperms.lib.hikari'
relocate 'com.mongodb', 'me.lucko.luckperms.lib.mongodb'
relocate 'org.bson', 'me.lucko.luckperms.lib.bson'
relocate 'redis.clients.jedis', 'me.lucko.luckperms.lib.jedis'
relocate 'org.apache.commons.pool2', 'me.lucko.luckperms.lib.commonspool2'
relocate 'ninja.leaping.configurate', 'me.lucko.luckperms.lib.configurate'
}
task remappedShadowJar(type: RemapJarTask) {
dependsOn tasks.shadowJar
input = tasks.shadowJar.archivePath
addNestedDependencies = true
archiveName = "LuckPerms-Fabric-${project.ext.fullVersion}.jar"
}
tasks.assemble.dependsOn tasks.remappedShadowJar
artifacts {
archives remappedShadowJar
shadow shadowJar
}

View File

@ -0,0 +1,76 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.common.cacheddata.CacheMetadata;
import me.lucko.luckperms.common.calculator.CalculatorFactory;
import me.lucko.luckperms.common.calculator.PermissionCalculator;
import me.lucko.luckperms.common.calculator.processor.MapProcessor;
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
import me.lucko.luckperms.common.calculator.processor.RegexProcessor;
import me.lucko.luckperms.common.calculator.processor.SpongeWildcardProcessor;
import me.lucko.luckperms.common.calculator.processor.WildcardProcessor;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.fabric.calculator.ServerOwnerProcessor;
import me.lucko.luckperms.fabric.context.FabricContextManager;
import net.luckperms.api.query.QueryOptions;
public class FabricCalculatorFactory implements CalculatorFactory {
private final LPFabricPlugin plugin;
public FabricCalculatorFactory(LPFabricPlugin plugin) {
this.plugin = plugin;
}
@Override
public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metadata) {
ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder();
processors.add(new MapProcessor());
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) {
processors.add(new RegexProcessor());
}
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) {
processors.add(new WildcardProcessor());
}
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS_SPONGE)) {
processors.add(new SpongeWildcardProcessor());
}
boolean integratedOwner = queryOptions.option(FabricContextManager.INTEGRATED_SERVER_OWNER).orElse(false);
if (integratedOwner && this.plugin.getConfiguration().get(ConfigKeys.FABRIC_INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS)) {
processors.add(new ServerOwnerProcessor());
}
return new PermissionCalculator(this.plugin, metadata, processors.build());
}
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
import net.fabricmc.loader.launch.common.FabricLauncherBase;
import java.net.MalformedURLException;
import java.nio.file.Path;
public class FabricClassLoader implements PluginClassLoader {
@Override
public void addJarToClasspath(Path file) {
try {
// Fabric abstracts class loading away to the FabricLauncher.
// TODO(i509VCB): Work on API for Fabric Loader which does not touch internals.
// Player wants to use project jigsaw in the future.
FabricLauncherBase.getLauncher().propose(file.toUri().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,118 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import me.lucko.luckperms.common.command.CommandManager;
import me.lucko.luckperms.common.command.utils.ArgumentTokenizer;
import me.lucko.luckperms.common.sender.Sender;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.minecraft.server.command.ServerCommandSource;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
public class FabricCommandExecutor extends CommandManager implements Command<ServerCommandSource>, SuggestionProvider<ServerCommandSource> {
private static final String[] COMMAND_ALIASES = new String[] {"luckperms", "lp", "perm", "perms", "permission", "permissions"};
private final LPFabricPlugin plugin;
public FabricCommandExecutor(LPFabricPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
public void register() {
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
for (String alias : COMMAND_ALIASES) {
LiteralCommandNode<ServerCommandSource> cmd = literal(alias)
.executes(this)
.build();
ArgumentCommandNode<ServerCommandSource, String> args = argument("args", greedyString())
.suggests(this)
.executes(this)
.build();
cmd.addChild(args);
dispatcher.getRoot().addChild(cmd);
}
});
}
@Override
public int run(CommandContext<ServerCommandSource> ctx) {
Sender wrapped = this.plugin.getSenderFactory().wrap(ctx.getSource());
int start = ctx.getRange().getStart();
List<String> arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(ctx.getInput().substring(start));
String label = arguments.remove(0);
if (label.startsWith("/")) {
label = label.substring(1);
}
executeCommand(wrapped, label, arguments);
return Command.SINGLE_SUCCESS;
}
@Override
public CompletableFuture<Suggestions> getSuggestions(CommandContext<ServerCommandSource> ctx, SuggestionsBuilder builder) {
Sender wrapped = this.plugin.getSenderFactory().wrap(ctx.getSource());
int idx = builder.getStart();
String buffer = ctx.getInput().substring(idx);
idx += buffer.length();
List<String> arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(buffer);
if (!arguments.isEmpty()) {
idx -= arguments.get(arguments.size() - 1).length();
}
List<String> completions = tabCompleteCommand(wrapped, arguments);
// Offset the builder from the current string range so suggestions are placed in the right spot
builder = builder.createOffset(idx);
for (String completion : completions) {
builder.suggest(completion);
}
return builder.buildFuture();
}
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import me.lucko.luckperms.common.config.generic.adapter.ConfigurateConfigAdapter;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
import ninja.leaping.configurate.loader.ConfigurationLoader;
import java.nio.file.Path;
public class FabricConfigAdapter extends ConfigurateConfigAdapter {
public FabricConfigAdapter(LuckPermsPlugin plugin, Path path) {
super(plugin, path);
}
@Override
protected ConfigurationLoader<? extends ConfigurationNode> createLoader(Path path) {
return HoconConfigurationLoader.builder().setPath(path).build();
}
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.event.AbstractEventBus;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.fabricmc.loader.api.ModContainer;
public class FabricEventBus extends AbstractEventBus<ModContainer> {
public FabricEventBus(LuckPermsPlugin plugin, LuckPermsApiProvider apiProvider) {
super(plugin, apiProvider);
}
@Override
protected ModContainer checkPlugin(Object mod) throws IllegalArgumentException {
if (mod instanceof ModContainer) {
return (ModContainer) mod;
}
throw new IllegalArgumentException("Object " + mod + " (" + mod.getClass().getName() + ") is not a ModContainer.");
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import me.lucko.luckperms.common.plugin.scheduler.AbstractJavaScheduler;
import java.util.concurrent.Executor;
public class FabricSchedulerAdapter extends AbstractJavaScheduler {
private final Executor sync;
public FabricSchedulerAdapter(LPFabricBootstrap bootstrap) {
this.sync = r -> bootstrap.getServer().orElseThrow(() -> new IllegalStateException("Server not ready")).submitAndJoin(r);
}
@Override
public Executor sync() {
return this.sync;
}
}

View File

@ -0,0 +1,110 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import me.lucko.fabric.api.permissions.v0.Permissions;
import me.lucko.luckperms.common.locale.TranslationManager;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.sender.SenderFactory;
import me.lucko.luckperms.fabric.model.MixinUser;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.luckperms.api.util.Tristate;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import java.util.Locale;
import java.util.UUID;
public class FabricSenderFactory extends SenderFactory<LPFabricPlugin, ServerCommandSource> {
private final LPFabricPlugin plugin;
public FabricSenderFactory(LPFabricPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
protected LPFabricPlugin getPlugin() {
return this.plugin;
}
@Override
protected UUID getUniqueId(ServerCommandSource commandSource) {
if (commandSource.getEntity() != null) {
return commandSource.getEntity().getUuid();
}
return Sender.CONSOLE_UUID;
}
@Override
protected String getName(ServerCommandSource commandSource) {
String name = commandSource.getName();
if (commandSource.getEntity() != null && name.equals("Server")) {
return Sender.CONSOLE_NAME;
}
return name;
}
@Override
protected void sendMessage(ServerCommandSource sender, Component message) {
Locale locale = null;
if (sender.getEntity() instanceof ServerPlayerEntity) {
locale = ((MixinUser) sender.getEntity()).getCachedLocale();
}
sender.sendFeedback(toNativeText(TranslationManager.render(message, locale)), false);
}
@Override
protected Tristate getPermissionValue(ServerCommandSource commandSource, String node) {
switch (Permissions.getPermissionValue(commandSource, node)) {
case TRUE:
return Tristate.TRUE;
case FALSE:
return Tristate.FALSE;
case DEFAULT:
return Tristate.UNDEFINED;
default:
throw new AssertionError();
}
}
@Override
protected boolean hasPermission(ServerCommandSource commandSource, String node) {
return getPermissionValue(commandSource, node).asBoolean();
}
@Override
protected void performCommand(ServerCommandSource sender, String command) {
sender.getMinecraftServer().getCommandManager().execute(sender, command);
}
public static Text toNativeText(Component component) {
return Text.Serializer.fromJson(GsonComponentSerializer.gson().serialize(component));
}
}

View File

@ -0,0 +1,280 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import com.mojang.authlib.GameProfile;
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
import me.lucko.luckperms.common.plugin.logging.Log4jPluginLogger;
import me.lucko.luckperms.common.plugin.logging.PluginLogger;
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.luckperms.api.platform.Platform;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
/**
* Bootstrap plugin for LuckPerms running on Fabric.
*/
public final class LPFabricBootstrap implements LuckPermsBootstrap, DedicatedServerModInitializer {
private static final String MODID = "luckperms";
private static final ModContainer MOD_CONTAINER = FabricLoader.getInstance().getModContainer(MODID)
.orElseThrow(() -> new RuntimeException("Could not get the LuckPerms mod container."));
/**
* The plugin logger
*/
private final PluginLogger logger;
/**
* A scheduler adapter for the platform
*/
private final SchedulerAdapter schedulerAdapter;
/**
* The plugin class loader.
*/
private final PluginClassLoader classLoader;
/**
* The plugin instance
*/
private LPFabricPlugin plugin;
/**
* The time when the plugin was enabled
*/
private Instant startTime;
// load/enable latches
private final CountDownLatch loadLatch = new CountDownLatch(1);
private final CountDownLatch enableLatch = new CountDownLatch(1);
/**
* The Minecraft server instance
*/
private MinecraftServer server;
public LPFabricBootstrap() {
this.logger = new Log4jPluginLogger(LogManager.getLogger(MODID));
this.schedulerAdapter = new FabricSchedulerAdapter(this);
this.classLoader = new FabricClassLoader();
this.plugin = new LPFabricPlugin(this);
}
// provide adapters
@Override
public PluginLogger getPluginLogger() {
return this.logger;
}
@Override
public SchedulerAdapter getScheduler() {
return this.schedulerAdapter;
}
@Override
public PluginClassLoader getPluginClassLoader() {
return this.classLoader;
}
// lifecycle
@Override
public void onInitializeServer() {
this.plugin = new LPFabricPlugin(this);
try {
this.plugin.load();
} finally {
this.loadLatch.countDown();
}
// Register the Server startup/shutdown events now
ServerLifecycleEvents.SERVER_STARTING.register(this::onServerStarting);
ServerLifecycleEvents.SERVER_STOPPING.register(this::onServerStopping);
this.plugin.registerFabricListeners();
}
private void onServerStarting(MinecraftServer server) {
this.server = server;
this.startTime = Instant.now();
this.plugin.enable();
}
private void onServerStopping(MinecraftServer server) {
this.plugin.disable();
this.server = null;
}
@Override
public CountDownLatch getLoadLatch() {
return this.loadLatch;
}
@Override
public CountDownLatch getEnableLatch() {
return this.enableLatch;
}
// MinecraftServer singleton getter
public Optional<MinecraftServer> getServer() {
return Optional.ofNullable(this.server);
}
// provide information about the plugin
@Override
public String getVersion() {
return MOD_CONTAINER.getMetadata().getVersion().getFriendlyString();
}
@Override
public Instant getStartupTime() {
return this.startTime;
}
// provide information about the platform
@Override
public Platform.Type getType() {
return Platform.Type.FABRIC;
}
@Override
public String getServerBrand() {
String fabricVersion = FabricLoader.getInstance().getModContainer("fabric")
.map(c -> c.getMetadata().getVersion().getFriendlyString())
.orElse("unknown");
return "fabric@" + fabricVersion;
}
@Override
public String getServerVersion() {
String fabricApiVersion = FabricLoader.getInstance().getModContainer("fabric-api-base")
.map(c -> c.getMetadata().getVersion().getFriendlyString())
.orElse("unknown");
return getServer().map(MinecraftServer::getVersion).orElse("null") + " - fabric-api@" + fabricApiVersion;
}
@Override
public Path getDataDirectory() {
return FabricLoader.getInstance().getGameDir().resolve("mods").resolve(MODID);
}
@Override
public Path getConfigDirectory() {
return FabricLoader.getInstance().getConfigDir().resolve(MODID);
}
@Override
public InputStream getResourceStream(String path) {
try {
return Files.newInputStream(LPFabricBootstrap.MOD_CONTAINER.getPath(path));
} catch (IOException e) {
return null;
}
}
@Override
public Optional<ServerPlayerEntity> getPlayer(UUID uniqueId) {
return getServer().map(MinecraftServer::getPlayerManager).map(s -> s.getPlayer(uniqueId));
}
@Override
public Optional<UUID> lookupUniqueId(String username) {
return getServer().map(MinecraftServer::getUserCache).map(c -> c.findByName(username)).map(GameProfile::getId);
}
@Override
public Optional<String> lookupUsername(UUID uniqueId) {
return getServer().map(MinecraftServer::getUserCache).map(c -> c.getByUuid(uniqueId)).map(GameProfile::getName);
}
@Override
public int getPlayerCount() {
return getServer().map(MinecraftServer::getCurrentPlayerCount).orElse(0);
}
@Override
public Collection<String> getPlayerList() {
return getServer().map(MinecraftServer::getPlayerManager)
.map(server -> {
List<ServerPlayerEntity> players = server.getPlayerList();
List<String> list = new ArrayList<>(players.size());
for (ServerPlayerEntity player : players) {
list.add(player.getGameProfile().getName());
}
return list;
})
.orElse(Collections.emptyList());
}
@Override
public Collection<UUID> getOnlinePlayers() {
return getServer().map(MinecraftServer::getPlayerManager)
.map(server -> {
List<ServerPlayerEntity> players = server.getPlayerList();
List<UUID> list = new ArrayList<>(players.size());
for (ServerPlayerEntity player : players) {
list.add(player.getGameProfile().getId());
}
return list;
})
.orElse(Collections.emptyList());
}
@Override
public boolean isPlayerOnline(UUID uniqueId) {
return getServer().map(MinecraftServer::getPlayerManager).map(s -> s.getPlayer(uniqueId) != null).orElse(false);
}
}

View File

@ -0,0 +1,248 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric;
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
import me.lucko.luckperms.common.calculator.CalculatorFactory;
import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter;
import me.lucko.luckperms.common.dependencies.Dependency;
import me.lucko.luckperms.common.event.AbstractEventBus;
import me.lucko.luckperms.common.locale.TranslationManager;
import me.lucko.luckperms.common.messaging.MessagingFactory;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.model.manager.group.StandardGroupManager;
import me.lucko.luckperms.common.model.manager.track.StandardTrackManager;
import me.lucko.luckperms.common.model.manager.user.StandardUserManager;
import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
import me.lucko.luckperms.common.sender.DummySender;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
import me.lucko.luckperms.common.util.MoreFiles;
import me.lucko.luckperms.fabric.context.FabricContextManager;
import me.lucko.luckperms.fabric.context.FabricPlayerCalculator;
import me.lucko.luckperms.fabric.listeners.FabricConnectionListener;
import me.lucko.luckperms.fabric.listeners.PermissionCheckListener;
import net.fabricmc.loader.api.ModContainer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.query.QueryOptions;
import net.minecraft.server.MinecraftServer;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
public class LPFabricPlugin extends AbstractLuckPermsPlugin {
private final LPFabricBootstrap bootstrap;
private FabricConnectionListener connectionListener;
private FabricCommandExecutor commandManager;
private FabricSenderFactory senderFactory;
private FabricContextManager contextManager;
private StandardUserManager userManager;
private StandardGroupManager groupManager;
private StandardTrackManager trackManager;
public LPFabricPlugin(LPFabricBootstrap bootstrap) {
this.bootstrap = bootstrap;
}
@Override
public LPFabricBootstrap getBootstrap() {
return this.bootstrap;
}
protected void registerFabricListeners() {
// Events are registered very early on, and persist between game states
this.connectionListener = new FabricConnectionListener(this);
this.connectionListener.registerListeners();
new PermissionCheckListener(this).registerListeners();
// Command registration also need to occur early, and will persist across game states as well.
this.commandManager = new FabricCommandExecutor(this);
this.commandManager.register();
}
@Override
protected void setupSenderFactory() {
this.senderFactory = new FabricSenderFactory(this);
}
@Override
protected Set<Dependency> getGlobalDependencies() {
Set<Dependency> dependencies = super.getGlobalDependencies();
dependencies.add(Dependency.CONFIGURATE_CORE);
dependencies.add(Dependency.CONFIGURATE_HOCON);
dependencies.add(Dependency.HOCON_CONFIG);
return dependencies;
}
@Override
protected ConfigurationAdapter provideConfigurationAdapter() {
Path configPath = this.getBootstrap().getConfigDirectory().resolve("luckperms.conf");
if (!Files.exists(configPath)) {
try {
MoreFiles.createDirectoriesIfNotExists(this.bootstrap.getConfigDirectory());
try (InputStream is = this.getBootstrap().getResourceStream("luckperms.conf")) {
Files.copy(is, configPath);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return new FabricConfigAdapter(this, configPath);
}
@Override
protected void registerPlatformListeners() {
// Too late for Fabric, registered in #registerFabricListeners
}
@Override
protected MessagingFactory<LPFabricPlugin> provideMessagingFactory() {
return new MessagingFactory<>(this);
}
@Override
protected void registerCommands() {
// Too late for Fabric, registered in #registerFabricListeners
}
@Override
protected void setupManagers() {
this.userManager = new StandardUserManager(this);
this.groupManager = new StandardGroupManager(this);
this.trackManager = new StandardTrackManager(this);
}
@Override
protected CalculatorFactory provideCalculatorFactory() {
return new FabricCalculatorFactory(this);
}
@Override
protected void setupContextManager() {
this.contextManager = new FabricContextManager(this);
FabricPlayerCalculator playerCalculator = new FabricPlayerCalculator(this);
playerCalculator.registerListeners();
this.contextManager.registerCalculator(playerCalculator);
}
@Override
protected void setupPlatformHooks() {
}
@Override
protected AbstractEventBus<ModContainer> provideEventBus(LuckPermsApiProvider provider) {
return new FabricEventBus(this, provider);
}
@Override
protected void registerApiOnPlatform(LuckPerms api) {
}
@Override
protected void registerHousekeepingTasks() {
this.bootstrap.getScheduler().asyncRepeating(new ExpireTemporaryTask(this), 3, TimeUnit.SECONDS);
this.bootstrap.getScheduler().asyncRepeating(new CacheHousekeepingTask(this), 2, TimeUnit.MINUTES);
}
@Override
protected void performFinalSetup() {
}
public FabricSenderFactory getSenderFactory() {
return this.senderFactory;
}
@Override
public FabricConnectionListener getConnectionListener() {
return this.connectionListener;
}
@Override
public FabricCommandExecutor getCommandManager() {
return this.commandManager;
}
@Override
public FabricContextManager getContextManager() {
return this.contextManager;
}
@Override
public StandardUserManager getUserManager() {
return this.userManager;
}
@Override
public StandardGroupManager getGroupManager() {
return this.groupManager;
}
@Override
public StandardTrackManager getTrackManager() {
return this.trackManager;
}
@Override
public Optional<QueryOptions> getQueryOptionsForUser(User user) {
return this.bootstrap.getPlayer(user.getUniqueId()).map(player -> this.contextManager.getQueryOptions(player));
}
@Override
public Stream<Sender> getOnlineSenders() {
return Stream.concat(
Stream.of(getConsoleSender()),
this.bootstrap.getServer().map(MinecraftServer::getPlayerManager).map(s -> s.getPlayerList().stream().map(p -> this.senderFactory.wrap(p.getCommandSource()))).orElseGet(Stream::empty)
);
}
@Override
public Sender getConsoleSender() {
return this.bootstrap.getServer()
.map(s -> this.senderFactory.wrap(s.getCommandSource()))
.orElseGet(() -> new DummySender(this, Sender.CONSOLE_UUID, Sender.CONSOLE_NAME) {
@Override
public void sendMessage(Component message) {
LPFabricPlugin.this.bootstrap.getPluginLogger().info(PlainComponentSerializer.plain().serialize(TranslationManager.render(message)));
}
});
}
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.calculator;
import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import net.luckperms.api.util.Tristate;
/**
* Permission processor which is added to the owner of an Integrated server to
* simply return true if no other processors match.
*/
public class ServerOwnerProcessor extends AbstractPermissionProcessor {
private static final TristateResult TRUE_RESULT = new TristateResult.Factory(ServerOwnerProcessor.class).result(Tristate.TRUE);
@Override
public TristateResult hasPermission(String permission) {
return TRUE_RESULT;
}
}

View File

@ -0,0 +1,81 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.context;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.ContextManager;
import me.lucko.luckperms.common.context.QueryOptionsCache;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.fabric.model.MixinUser;
import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.query.OptionKey;
import net.luckperms.api.query.QueryOptions;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.UUID;
public class FabricContextManager extends ContextManager<ServerPlayerEntity, ServerPlayerEntity> {
public static final OptionKey<Boolean> INTEGRATED_SERVER_OWNER = OptionKey.of("integrated_server_owner", Boolean.class);
public FabricContextManager(LuckPermsPlugin plugin) {
super(plugin, ServerPlayerEntity.class, ServerPlayerEntity.class);
}
@Override
public UUID getUniqueId(ServerPlayerEntity player) {
return player.getUuid();
}
public QueryOptionsCache<ServerPlayerEntity> newQueryOptionsCache(ServerPlayerEntity player) {
return new QueryOptionsCache<>(player, this);
}
@Override
public QueryOptionsCache<ServerPlayerEntity> getCacheFor(ServerPlayerEntity subject) {
if (subject == null) {
throw new NullPointerException("subject");
}
return ((MixinUser) subject).getQueryOptionsCache(this);
}
@Override
public void invalidateCache(ServerPlayerEntity subject) {
getCacheFor(subject).invalidate();
}
@Override
public QueryOptions formQueryOptions(ServerPlayerEntity subject, ImmutableContextSet contextSet) {
QueryOptions.Builder queryOptions = this.plugin.getConfiguration().get(ConfigKeys.GLOBAL_QUERY_OPTIONS).toBuilder();
if (subject.getServer().isHost(subject.getGameProfile())) {
queryOptions.option(INTEGRATED_SERVER_OWNER, true);
}
return queryOptions.context(contextSet).build();
}
}

View File

@ -0,0 +1,119 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.context;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
import me.lucko.luckperms.common.util.EnumNamer;
import me.lucko.luckperms.fabric.LPFabricPlugin;
import me.lucko.luckperms.fabric.event.PlayerChangeWorldCallback;
import me.lucko.luckperms.fabric.event.RespawnPlayerCallback;
import net.luckperms.api.context.Context;
import net.luckperms.api.context.ContextCalculator;
import net.luckperms.api.context.ContextConsumer;
import net.luckperms.api.context.ContextSet;
import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.ImmutableContextSet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.world.GameMode;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
public class FabricPlayerCalculator implements ContextCalculator<ServerPlayerEntity> {
private static final EnumNamer<GameMode> GAMEMODE_NAMER = new EnumNamer<>(
GameMode.class,
EnumNamer.LOWER_CASE_NAME
);
private final LPFabricPlugin plugin;
public FabricPlayerCalculator(LPFabricPlugin plugin) {
this.plugin = plugin;
}
public void registerListeners() {
PlayerChangeWorldCallback.EVENT.register(this::onWorldChange);
RespawnPlayerCallback.EVENT.register(this::onPlayerRespawn);
}
@Override
public void calculate(@NonNull ServerPlayerEntity target, @NonNull ContextConsumer consumer) {
GameMode mode = target.interactionManager.getGameMode();
if (mode != null && mode != GameMode.NOT_SET) {
consumer.accept(DefaultContextKeys.GAMEMODE_KEY, GAMEMODE_NAMER.name(mode));
}
// TODO: figure out dimension type context too
ServerWorld world = target.getServerWorld();
this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).rewriteAndSubmit(getContextKey(world.getRegistryKey().getValue()), consumer);
}
@Override
public ContextSet estimatePotentialContexts() {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
for (GameMode mode : GameMode.values()) {
builder.add(DefaultContextKeys.GAMEMODE_KEY, GAMEMODE_NAMER.name(mode));
}
// TODO: dimension type
Optional<MinecraftServer> server = this.plugin.getBootstrap().getServer();
if (server.isPresent()) {
Iterable<ServerWorld> worlds = server.get().getWorlds();
for (ServerWorld world : worlds) {
String worldName = getContextKey(world.getRegistryKey().getValue());
if (Context.isValidValue(worldName)) {
builder.add(DefaultContextKeys.WORLD_KEY, worldName);
}
}
}
return builder.build();
}
private static String getContextKey(Identifier key) {
if (key.getNamespace().equals("minecraft")) {
return key.getPath();
}
return key.toString();
}
private void onWorldChange(ServerWorld origin, ServerWorld destination, ServerPlayerEntity player) {
this.plugin.getContextManager().invalidateCache(player);
}
private void onPlayerRespawn(ServerPlayerEntity oldPlayer, ServerPlayerEntity newPlayer, boolean alive) {
this.plugin.getContextManager().invalidateCache(oldPlayer);
this.plugin.getContextManager().invalidateCache(newPlayer);
}
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.event;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
// TODO: Use Fabric API alternative when merged.
public interface PlayerChangeWorldCallback {
Event<PlayerChangeWorldCallback> EVENT = EventFactory.createArrayBacked(PlayerChangeWorldCallback.class, (callbacks) -> (originalWorld, destination, player) -> {
for (PlayerChangeWorldCallback callback : callbacks) {
callback.onChangeWorld(originalWorld, destination, player);
}
});
void onChangeWorld(ServerWorld originalWorld, ServerWorld destination, ServerPlayerEntity player);
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.event;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.network.ServerPlayerEntity;
// TODO: Use Fabric API alternative when merged.
// https://github.com/FabricMC/fabric/pull/957
public interface RespawnPlayerCallback {
Event<RespawnPlayerCallback> EVENT = EventFactory.createArrayBacked(RespawnPlayerCallback.class, (callbacks) -> (newPlayer, oldPlayer, alive) -> {
for (RespawnPlayerCallback callback : callbacks) {
callback.onRespawn(newPlayer, oldPlayer, alive);
}
});
void onRespawn(ServerPlayerEntity oldPlayer, ServerPlayerEntity newPlayer, boolean alive);
}

View File

@ -0,0 +1,133 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.listeners;
import com.mojang.authlib.GameProfile;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.locale.TranslationManager;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener;
import me.lucko.luckperms.fabric.FabricSenderFactory;
import me.lucko.luckperms.fabric.LPFabricPlugin;
import me.lucko.luckperms.fabric.mixin.ServerLoginNetworkHandlerAccessor;
import me.lucko.luckperms.fabric.model.MixinUser;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking.LoginSynchronizer;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.kyori.adventure.text.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerLoginNetworkHandler;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.concurrent.CompletableFuture;
public class FabricConnectionListener extends AbstractConnectionListener {
private final LPFabricPlugin plugin;
public FabricConnectionListener(LPFabricPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
public void registerListeners() {
ServerLoginConnectionEvents.QUERY_START.register(this::onPreLogin);
ServerPlayConnectionEvents.JOIN.register(this::onLogin);
ServerPlayConnectionEvents.DISCONNECT.register(this::onDisconnect);
}
private void onPreLogin(ServerLoginNetworkHandler netHandler, MinecraftServer server, PacketSender packetSender, LoginSynchronizer sync) {
/* Called when the player first attempts a connection with the server. */
// Get their profile from the net handler - it should have been initialised by now.
GameProfile profile = ((ServerLoginNetworkHandlerAccessor) netHandler).getGameProfile();
// Register with the LoginSynchronizer that we want to perform a task before the login proceeds.
sync.waitFor(CompletableFuture.runAsync(() -> onPreLoginAsync(netHandler, profile), this.plugin.getBootstrap().getScheduler().async()));
}
private void onPreLoginAsync(ServerLoginNetworkHandler netHandler, GameProfile e) {
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
this.plugin.getLogger().info("Processing pre-login for " + e.getId() + " - " + e.getName());
}
/* Actually process the login for the connection.
We do this here to delay the login until the data is ready.
If the login gets cancelled later on, then this will be cleaned up.
This includes:
- loading uuid data
- loading permissions
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
User user = loadUser(e.getId(), e.getName());
recordConnection(e.getId());
this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(e.getId(), e.getName(), user);
} catch (Exception ex) {
this.plugin.getLogger().severe("Exception occurred whilst loading data for " + e.getId() + " - " + e.getName(), ex);
// deny the connection
Component reason = TranslationManager.render(Message.LOADING_DATABASE_ERROR.build());
netHandler.disconnect(FabricSenderFactory.toNativeText(reason));
this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(e.getId(), e.getName(), null);
}
}
private void onLogin(ServerPlayNetworkHandler netHandler, PacketSender packetSender, MinecraftServer server) {
final ServerPlayerEntity player = netHandler.player;
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
this.plugin.getLogger().info("Processing login for " + player.getUuid() + " - " + player.getName());
}
final User user = this.plugin.getUserManager().getIfLoaded(player.getUuid());
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
if (user == null) {
this.plugin.getLogger().warn("User " + player.getUuid() + " - " + player.getName() +
" doesn't currently have data pre-loaded - denying login.");
Component reason = TranslationManager.render(Message.LOADING_STATE_ERROR.build());
netHandler.disconnect(FabricSenderFactory.toNativeText(reason));
return;
}
// init permissions handler
((MixinUser) player).initializePermissions(user);
this.plugin.getContextManager().signalContextUpdate(player);
}
private void onDisconnect(ServerPlayNetworkHandler netHandler, MinecraftServer server) {
handleDisconnect(netHandler.player.getUuid());
}
}

View File

@ -0,0 +1,89 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.listeners;
import me.lucko.fabric.api.permissions.v0.PermissionCheckEvent;
import me.lucko.luckperms.common.calculator.result.TristateResult;
import me.lucko.luckperms.common.query.QueryOptionsImpl;
import me.lucko.luckperms.fabric.LPFabricPlugin;
import me.lucko.luckperms.fabric.model.MixinUser;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Listener to route permission checks made via fabric-permissions-api to LuckPerms.
*/
public class PermissionCheckListener {
private final LPFabricPlugin plugin;
public PermissionCheckListener(LPFabricPlugin plugin) {
this.plugin = plugin;
}
public void registerListeners() {
PermissionCheckEvent.EVENT.register(this::onPermissionCheck);
}
private @NonNull TriState onPermissionCheck(CommandSource source, String permission) {
if (source instanceof ServerCommandSource) {
Entity entity = ((ServerCommandSource) source).getEntity();
if (entity instanceof ServerPlayerEntity) {
return onPlayerPermissionCheck((ServerPlayerEntity) entity, permission);
}
}
return onOtherPermissionCheck(source, permission);
}
private TriState onPlayerPermissionCheck(ServerPlayerEntity player, String permission) {
switch (((MixinUser) player).hasPermission(permission)) {
case TRUE:
return TriState.TRUE;
case FALSE:
return TriState.FALSE;
case UNDEFINED:
return TriState.DEFAULT;
default:
throw new AssertionError();
}
}
private TriState onOtherPermissionCheck(CommandSource source, String permission) {
if (source instanceof ServerCommandSource) {
String name = ((ServerCommandSource) source).getName();
this.plugin.getVerboseHandler().offerPermissionCheckEvent(me.lucko.luckperms.common.verbose.event.PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK, name, QueryOptionsImpl.DEFAULT_CONTEXTUAL, permission, TristateResult.UNDEFINED);
this.plugin.getPermissionRegistry().offer(permission);
}
return TriState.DEFAULT;
}
}

View File

@ -0,0 +1,39 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.mixin;
import net.minecraft.network.packet.c2s.play.ClientSettingsC2SPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ClientSettingsC2SPacket.class)
public interface ClientSettingsC2SPacketAccessor {
@Accessor("language")
String getLanguage();
}

View File

@ -0,0 +1,49 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.mixin;
import me.lucko.luckperms.fabric.event.RespawnPlayerCallback;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(PlayerManager.class)
public abstract class PlayerManagerMixin {
// Implement the callback for RespawnPlayerCallback
// We'll switch to Fabric's event when FabricMC/fabric#957 is merged.
@Inject(at = @At("TAIL"), method = "respawnPlayer", locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void luckperms_onRespawnPlayer(ServerPlayerEntity player, boolean alive, CallbackInfoReturnable<ServerPlayerEntity> cir) {
RespawnPlayerCallback.EVENT.invoker().onRespawn(player, cir.getReturnValue(), alive); // Transfer the old caches to the new player.
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.mixin;
import com.mojang.authlib.GameProfile;
import net.minecraft.server.network.ServerLoginNetworkHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
/**
* Accessor mixin to provide access to the underlying {@link GameProfile} during the server
* login handling.
*/
@Mixin(ServerLoginNetworkHandler.class)
public interface ServerLoginNetworkHandlerAccessor {
@Accessor("profile")
GameProfile getGameProfile();
}

View File

@ -0,0 +1,142 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.mixin;
import me.lucko.luckperms.common.cacheddata.type.PermissionCache;
import me.lucko.luckperms.common.context.QueryOptionsCache;
import me.lucko.luckperms.common.locale.TranslationManager;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent;
import me.lucko.luckperms.fabric.context.FabricContextManager;
import me.lucko.luckperms.fabric.event.PlayerChangeWorldCallback;
import me.lucko.luckperms.fabric.model.MixinUser;
import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.util.Tristate;
import net.minecraft.network.packet.c2s.play.ClientSettingsC2SPacket;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.Locale;
/**
* Mixin into {@link ServerPlayerEntity} to store LP caches and implement {@link MixinUser}.
*
* <p>This mixin is also temporarily used to implement our internal PlayerChangeWorldCallback,
* until a similar event is added to Fabric itself.</p>
*/
@Mixin(ServerPlayerEntity.class)
public abstract class ServerPlayerEntityMixin implements MixinUser {
/** Cache a reference to the LP {@link User} instance loaded for this player */
private User luckperms$user;
/**
* Hold a QueryOptionsCache instance on the player itself, so we can just cast instead of
* having to maintain a map of Player->Cache.
*/
private QueryOptionsCache<ServerPlayerEntity> luckperms$queryOptions;
// Cache player locale
private Locale luckperms$locale;
// Used by PlayerChangeWorldCallback hook below.
@Shadow public abstract ServerWorld getServerWorld();
@Override
public QueryOptionsCache<ServerPlayerEntity> getQueryOptionsCache(FabricContextManager contextManager) {
if (this.luckperms$queryOptions == null) {
this.luckperms$queryOptions = contextManager.newQueryOptionsCache(((ServerPlayerEntity) (Object) this));
}
return this.luckperms$queryOptions;
}
@Override
public Locale getCachedLocale() {
return this.luckperms$locale;
}
@Override
public void initializePermissions(User user) {
this.luckperms$user = user;
// ensure query options cache is initialised too.
if (this.luckperms$queryOptions == null) {
this.getQueryOptionsCache((FabricContextManager) user.getPlugin().getContextManager());
}
}
@Override
public Tristate hasPermission(String permission) {
if (permission == null) {
throw new NullPointerException("permission");
}
return hasPermission(permission, this.luckperms$queryOptions.getQueryOptions());
}
@Override
public Tristate hasPermission(String permission, QueryOptions queryOptions) {
if (permission == null) {
throw new NullPointerException("permission");
}
if (queryOptions == null) {
throw new NullPointerException("queryOptions");
}
final User user = this.luckperms$user;
if (user == null) {
throw new IllegalStateException("Permissions have not been initialised for this player yet.");
}
PermissionCache data = user.getCachedData().getPermissionData(queryOptions);
return data.checkPermission(permission, PermissionCheckEvent.Origin.PLATFORM_PERMISSION_CHECK).result();
}
@Inject(
at = @At("HEAD"),
method = "setClientSettings"
)
private void luckperms_setClientSettings(ClientSettingsC2SPacket information, CallbackInfo ci) {
String language = ((ClientSettingsC2SPacketAccessor) information).getLanguage();
this.luckperms$locale = TranslationManager.parseLocale(language);
}
@Inject(
at = @At("TAIL"),
method = "worldChanged",
locals = LocalCapture.CAPTURE_FAILEXCEPTION
)
private void luckperms_onChangeDimension(ServerWorld targetWorld, CallbackInfo ci) {
PlayerChangeWorldCallback.EVENT.invoker().onChangeWorld(this.getServerWorld(), targetWorld, (ServerPlayerEntity) (Object) this);
}
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.fabric.model;
import me.lucko.luckperms.common.context.QueryOptionsCache;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.fabric.context.FabricContextManager;
import net.luckperms.api.query.QueryOptions;
import net.luckperms.api.util.Tristate;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.Locale;
/**
* Mixin interface for {@link ServerPlayerEntity} implementing {@link User} related
* caches and functions.
*/
public interface MixinUser {
/**
* Gets (or creates using the manager) the objects {@link QueryOptionsCache}.
*
* @param contextManager the contextManager
* @return the cache
*/
QueryOptionsCache<ServerPlayerEntity> getQueryOptionsCache(FabricContextManager contextManager);
Locale getCachedLocale();
/**
* Initialises permissions for this player using the given {@link User}.
*
* @param user the user
*/
void initializePermissions(User user);
// methods to perform permission checks using the User instance initialised on login
Tristate hasPermission(String permission);
Tristate hasPermission(String permission, QueryOptions queryOptions);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,43 @@
{
"schemaVersion": 1,
"id": "luckperms",
"version": "${version}",
"name": "LuckPerms",
"icon": "assets/luckperms/icon.png",
"description": "A permissions plugin (mod)",
"authors": [
{
"name": "Luck",
"contact": {
"sources": "https://github.com/lucko",
"homepage": "https://lucko.me/"
}
}
],
"license": "MIT",
"contact": {
"homepage": "https://luckperms.net",
"source": "https://github.com/lucko/LuckPerms",
"issues": "https://github.com/lucko/LuckPerms/issues"
},
"environment": "server",
"entrypoints": {
"server": [
"me.lucko.luckperms.fabric.LPFabricBootstrap"
]
},
"mixins": [
"mixins.luckperms.json"
],
"depends": {
"fabricloader": ">=0.9.0",
"fabric-api-base": "*",
"fabric-command-api-v1": "*",
"fabric-lifecycle-events-v1": "*",
"fabric-networking-v0": "*",
"fabric-permissions-api-v0": "*"
},
"custom": {
"modmenu:api": true
}
}

View File

@ -0,0 +1,556 @@
####################################################################################################
# +----------------------------------------------------------------------------------------------+ #
# | __ __ ___ __ __ | #
# | | | | / ` |__/ |__) |__ |__) |\/| /__` | #
# | |___ \__/ \__, | \ | |___ | \ | | .__/ | #
# | | #
# | https://luckperms.net | #
# | | #
# | WIKI: https://luckperms.net/wiki | #
# | DISCORD: https://discord.gg/luckperms | #
# | BUG REPORTS: https://github.com/lucko/LuckPerms/issues | #
# | | #
# | Each option in this file is documented and explained here: | #
# | ==> https://luckperms.net/wiki/Configuration | #
# | | #
# | New options are not added to this file automatically. Default values are used if an | #
# | option cannot be found. The latest config versions can be obtained at the link above. | #
# +----------------------------------------------------------------------------------------------+ #
####################################################################################################
# +----------------------------------------------------------------------------------------------+ #
# | | #
# | ESSENTIAL SETTINGS | #
# | | #
# | Important settings that control how LuckPerms functions. | #
# | | #
# +----------------------------------------------------------------------------------------------+ #
# The name of the server, used for server specific permissions.
#
# - When set to "global" this setting is effectively ignored.
# - In all other cases, the value here is added to all players in a "server" context.
# - See: https://luckperms.net/wiki/Context
server = "global"
# If the servers own UUID cache/lookup facility should be used when there is no record for a player
# already in LuckPerms.
#
# - When this is set to 'false', commands using a player's username will not work unless the player
# has joined since LuckPerms was first installed.
# - To get around this, you can use a player's uuid directly in the command, or enable this option.
# - When this is set to 'true', the server facility is used. This may use a number of methods,
# including checking the servers local cache, or making a request to the Mojang API.
use-server-uuid-cache = false
# +----------------------------------------------------------------------------------------------+ #
# | | #
# | STORAGE SETTINGS | #
# | | #
# | Controls which storage method LuckPerms will use to store data. | #
# | | #
# +----------------------------------------------------------------------------------------------+ #
# How the plugin should store data
#
# - The various options are explained in more detail on the wiki:
# https://luckperms.net/wiki/Storage-types
#
# - Possible options:
#
# | Remote databases - require connection information to be configured below
# |=> MySQL
# |=> MariaDB (preferred over MySQL)
# |=> PostgreSQL
# |=> MongoDB
#
# | Flatfile/local database - don't require any extra configuration
# |=> H2 (preferred over SQLite)
# |=> SQLite
#
# | Readable & editable text files - don't require any extra configuration
# |=> YAML (.yml files)
# |=> JSON (.json files)
# |=> HOCON (.conf files)
# |=> TOML (.toml files)
# |
# | By default, user, group and track data is separated into different files. Data can be combined
# | and all stored in the same file by switching to a combined storage variant.
# | Just add '-combined' to the end of the storage-method, e.g. 'yaml-combined'
#
# - A H2 database is the default option.
# - If you want to edit data manually in "traditional" storage files, we suggest using YAML.
storage-method = "h2"
# The following block defines the settings for remote database storage methods.
#
# - You don't need to touch any of the settings here if you're using a local storage method!
# - The connection detail options are shared between all remote storage types.
data {
# Define the address and port for the database.
# - The standard DB engine port is used by default
# (MySQL = 3306, PostgreSQL = 5432, MongoDB = 27017)
# - Specify as "host:port" if differs
address = "localhost"
# The name of the database to store LuckPerms data in.
# - This must be created already. Don't worry about this setting if you're using MongoDB.
database = "minecraft"
# Credentials for the database.
username = "root"
password = ""
# These settings apply to the MySQL connection pool.
# - The default values will be suitable for the majority of users.
# - Do not change these settings unless you know what you're doing!
pool-settings {
# Sets the maximum size of the MySQL connection pool.
# - Basically this value will determine the maximum number of actual
# connections to the database backend.
# - More information about determining the size of connection pools can be found here:
# https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing
maximum-pool-size = 10
# Sets the minimum number of idle connections that the pool will try to maintain.
# - For maximum performance and responsiveness to spike demands, it is recommended to not set
# this value and instead allow the pool to act as a fixed size connection pool.
# (set this value to the same as 'maximum-pool-size')
minimum-idle = 10
# This setting controls the maximum lifetime of a connection in the pool in milliseconds.
# - The value should be at least 30 seconds less than any database or infrastructure imposed
# connection time limit.
maximum-lifetime = 1800000 # 30 minutes
# This setting controls the maximum number of milliseconds that the plugin will wait for a
# connection from the pool, before timing out.
connection-timeout = 5000 # 5 seconds
# This setting allows you to define extra properties for connections.
#
# By default, the following options are set to enable utf8 encoding. (you may need to remove
# these if you are using PostgreSQL)
# useUnicode = true
# characterEncoding = "utf8"
#
# You can also use this section to disable SSL connections, by uncommenting the 'useSSL' and
# 'verifyServerCertificate' options below.
properties {
useUnicode = true
characterEncoding = "utf8"
#useSSL: false
#verifyServerCertificate: false
}
}
# The prefix for all LuckPerms SQL tables.
# - Change this if you want to use different tables for different servers.
table-prefix = "luckperms_"
# The prefix to use for all LuckPerms collections. Change this if you want to use different
# collections for different servers. The default is no prefix.
mongodb-collection-prefix = ""
# MongoDB ClientConnectionURI for use with replica sets and custom connection options
# - See https://docs.mongodb.com/manual/reference/connection-string/
mongodb-connection-uri = ""
}
# Define settings for a "split" storage setup.
#
# - This allows you to define a storage method for each type of data.
# - The connection options above still have to be correct for each type here.
split-storage {
# Don't touch this if you don't want to use split storage!
enabled = false
methods {
# These options don't need to be modified if split storage isn't enabled.
user = "h2"
group = "h2"
track = "h2"
uuid = "h2"
log = "h2"
}
}
# +----------------------------------------------------------------------------------------------+ #
# | | #
# | UPDATE PROPAGATION & MESSAGING SERVICE | #
# | | #
# | Controls the ways in which LuckPerms will sync data & notify other servers of changes. | #
# | These options are documented on greater detail on the wiki under "Instant Updates". | #
# | | #
# +----------------------------------------------------------------------------------------------+ #
# This option controls how frequently LuckPerms will perform a sync task.
#
# - A sync task will refresh all data from the storage, and ensure that the most up-to-date data is
# being used by the plugin.
# - This is disabled by default, as most users will not need it. However, if you're using a remote
# storage type without a messaging service setup, you may wish to set this to something like 3.
# - Set to -1 to disable the task completely.
sync-minutes = -1
# If the file watcher should be enabled.
#
# - When using a file-based storage type, LuckPerms can monitor the data files for changes, and
# automatically update when changes are detected.
# - If you don't want this feature to be active, set this option to false.
watch-files = true
# Define which messaging service should be used by the plugin.
#
# - If enabled and configured, LuckPerms will use the messaging service to inform other connected
# servers of changes.
# - Use the command "/lp networksync" to manually push changes.
# - Data is NOT stored using this service. It is only used as a messaging platform.
#
# - If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need
# for LuckPerms to poll the database for changes.
#
# - Possible options:
# => sql Uses the SQL database to form a queue system for communication. Will only work when
# 'storage-method' is set to MySQL or MariaDB. This is chosen by default if the
# option is set to 'auto' and SQL storage is in use. Set to 'notsql' to disable this.
# => pluginmsg Uses the plugin messaging channels to communicate with the proxy.
# LuckPerms must be installed on your proxy & all connected servers backend servers.
# Won't work if you have more than one proxy.
# => redis Uses Redis pub-sub to push changes. Your server connection info must be configured
# below.
# => auto Attempts to automatically setup a messaging service using redis or sql.
messaging-service = "auto"
# If LuckPerms should automatically push updates after a change has been made with a command.
auto-push-updates = true
# If LuckPerms should push logging entries to connected servers via the messaging service.
push-log-entries = true
# If LuckPerms should broadcast received logging entries to players on this platform.
#
# - If you have LuckPerms installed on your backend servers as well as a BungeeCord proxy, you
# should set this option to false on either your backends or your proxies, to avoid players being
# messaged twice about log entries.
broadcast-received-log-entries = true
# Settings for Redis.
# Port 6379 is used by default; set address to "host:port" if differs
redis {
enabled = false
address = "localhost"
password = ""
}
# +----------------------------------------------------------------------------------------------+ #
# | | #
# | CUSTOMIZATION SETTINGS | #
# | | #
# | Settings that allow admins to customize the way LuckPerms operates. | #
# | | #
# +----------------------------------------------------------------------------------------------+ #
# Controls how temporary permissions/parents/meta should be accumulated.
#
# - The default behaviour is "deny".
# - This behaviour can also be specified when the command is executed. See the command usage
# documentation for more info.
#
# - Possible options:
# => accumulate durations will be added to the existing expiry time
# => replace durations will be replaced if the new duration is later than the current
# expiration
# => deny the command will just fail if you try to add another node with the same expiry
temporary-add-behaviour = "deny"
# Controls how LuckPerms will determine a users "primary" group.
#
# - The meaning and influence of "primary groups" are explained in detail on the wiki.
# - The preferred approach is to let LuckPerms automatically determine a users primary group
# based on the relative weight of their parent groups.
#
# - Possible options:
# => stored use the value stored against the users record in the file/database
# => parents-by-weight just use the users most highly weighted parent
# => all-parents-by-weight same as above, but calculates based upon all parents inherited from
# both directly and indirectly
primary-group-calculation = "parents-by-weight"
# If the plugin should check for "extra" permissions with users run LP commands.
#
# - These extra permissions allow finer control over what users can do with each command, and who
# they have access to edit.
# - The nature of the checks are documented on the wiki under "Argument based command permissions".
# - Argument based permissions are *not* static, unlike the 'base' permissions, and will depend upon
# the arguments given within the command.
argument-based-command-permissions = false
# If the plugin should check whether senders are a member of a given group before they're able to
# edit the groups data or add/remove other users to/from it.
# Note: these limitations do not apply to the web editor!
require-sender-group-membership-to-modify = false
# If the plugin should send log notifications to users whenever permissions are modified.
#
# - Notifications are only sent to those with the appropriate permission to receive them
# - They can also be temporarily enabled/disabled on a per-user basis using
# '/lp log notify <on|off>'
log-notify = true
# Defines a list of log entries which should not be sent as notifications to users.
#
# - Each entry in the list is a RegEx expression which is matched against the log entry description.
log-notify-filtered-descriptions = [
# "parent add example"
]
# If LuckPerms should automatically install translation bundles and periodically update them.
auto-install-translations = true
# Defines the options for prefix and suffix stacking.
#
# - The feature allows you to display multiple prefixes or suffixes alongside a players username in
# chat.
# - It is explained and documented in more detail on the wiki under "Prefix & Suffix Stacking".
#
# - The options are divided into separate sections for prefixes and suffixes.
# - The 'duplicates' setting refers to how duplicate elements are handled. Can be 'retain-all',
# 'first-only' or 'last-only'.
# - The value of 'start-spacer' is included at the start of the resultant prefix/suffix.
# - The value of 'end-spacer' is included at the end of the resultant prefix/suffix.
# - The value of 'middle-spacer' is included between each element in the resultant prefix/suffix.
#
# - Possible format options:
# => highest Selects the value with the highest weight, from all values
# held by or inherited by the player.
#
# => lowest Same as above, except takes the one with the lowest weight.
#
# => highest_own Selects the value with the highest weight, but will not
# accept any inherited values.
#
# => lowest_own Same as above, except takes the value with the lowest weight.
#
# => highest_inherited Selects the value with the highest weight, but will only
# accept inherited values.
#
# => lowest_inherited Same as above, except takes the value with the lowest weight.
#
# => highest_on_track_<track> Selects the value with the highest weight, but only if the
# value was inherited from a group on the given track.
#
# => lowest_on_track_<track> Same as above, except takes the value with the lowest weight.
#
# => highest_not_on_track_<track> Selects the value with the highest weight, but only if the
# value was inherited from a group not on the given track.
#
# => lowest_not_on_track_<track> Same as above, except takes the value with the lowest weight.
#
# => highest_from_group_<group> Selects the value with the highest weight, but only if the
# value was inherited from the given group.
#
# => lowest_from_group_<group> Same as above, except takes the value with the lowest weight.
#
# => highest_not_from_group_<group> Selects the value with the highest weight, but only if the
# value was not inherited from the given group.
#
# => lowest_not_from_group_<group> Same as above, except takes the value with the lowest weight.
meta-formatting {
prefix {
format = [
"highest"
]
duplicates = "first-only"
start-spacer = ""
middle-spacer = " "
end-spacer = ""
}
suffix {
format = [
"highest"
]
duplicates = "first-only"
start-spacer = ""
middle-spacer = " "
end-spacer = ""
}
}
# +----------------------------------------------------------------------------------------------+ #
# | | #
# | PERMISSION CALCULATION AND INHERITANCE | #
# | | #
# | Modify the way permission checks, meta lookups and inheritance resolutions are handled. | #
# | | #
# +----------------------------------------------------------------------------------------------+ #
# The algorithm LuckPerms should use when traversing the "inheritance tree".
#
# - Possible options:
# => breadth-first See: https://en.wikipedia.org/wiki/Breadth-first_search
# => depth-first-pre-order See: https://en.wikipedia.org/wiki/Depth-first_search
# => depth-first-post-order See: https://en.wikipedia.org/wiki/Depth-first_search
inheritance-traversal-algorithm = "depth-first-pre-order"
# If a final sort according to "inheritance rules" should be performed after the traversal algorithm
# has resolved the inheritance tree.
#
# "Inheritance rules" refers to things such as group weightings, primary group status, and the
# natural contextual ordering of the group nodes.
#
# Setting this to 'true' will allow for the inheritance rules to take priority over the structure of
# the inheritance tree.
#
# Effectively when this setting is 'true': the tree is flattened, and rules applied afterwards,
# and when this setting is 'false':, the rules are just applied during each step of the traversal.
post-traversal-inheritance-sort = false
# Defines the mode used to determine whether a set of contexts are satisfied.
#
# - Possible options:
# => at-least-one-value-per-key Set A will be satisfied by another set B, if at least one of the
# key-value entries per key in A are also in B.
# => all-values-per-key Set A will be satisfied by another set B, if all key-value
# entries in A are also in B.
context-satisfy-mode = "at-least-one-value-per-key"
# +----------------------------------------------------------------------------------------------+ #
# | Permission resolution settings | #
# +----------------------------------------------------------------------------------------------+ #
# If users on this server should have their global permissions applied.
# When set to false, only server specific permissions will apply for users on this server
include-global = true
# If users on this server should have their global world permissions applied.
# When set to false, only world specific permissions will apply for users on this server
include-global-world = true
# If users on this server should have global (non-server specific) groups applied
apply-global-groups = true
# If users on this server should have global (non-world specific) groups applied
apply-global-world-groups = true
# +----------------------------------------------------------------------------------------------+ #
# | Meta lookup settings | #
# +----------------------------------------------------------------------------------------------+ #
# Defines how meta values should be selected.
#
# - Possible options:
# => inheritance Selects the meta value that was inherited first
# => highest-number Selects the highest numerical meta value
# => lowest-number Selects the lowest numerical meta value
meta-value-selection-default = "inheritance"
# Defines how meta values should be selected per key.
meta-value-selection {
#max-homes = "highest-number"
}
# +----------------------------------------------------------------------------------------------+ #
# | Inheritance settings | #
# +----------------------------------------------------------------------------------------------+ #
# If the plugin should apply wildcard permissions.
#
# - If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered
# permissions matching the wildcard.
apply-wildcards = true
# If LuckPerms should resolve and apply permissions according to the Sponge style implicit wildcard
# inheritance system.
#
# - That being: If a user has been granted "example", then the player should have also be
# automatically granted "example.function", "example.another", "example.deeper.nesting",
# and so on.
apply-sponge-implicit-wildcards=true
# If the plugin should parse regex permissions.
#
# - If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the
# node, and resolve & apply all registered permissions matching the regex.
apply-regex = true
# If the plugin should complete and apply shorthand permissions.
#
# - If set to true, LuckPerms will detect and expand shorthand node patterns.
apply-shorthand = true
# If the owner of an integrated server should bypass permission checks.
#
# - This setting only applies when LuckPerms is active on a single-player world.
# - The owner of an integrated server is the player whose client instance is running the server.
integrated-server-owner-bypasses-checks = true
# +----------------------------------------------------------------------------------------------+ #
# | Extra settings | #
# +----------------------------------------------------------------------------------------------+ #
# Allows you to set "aliases" for the worlds sent forward for context calculation.
#
# - These aliases are provided in addition to the real world name. Applied recursively.
# - Remove the comment characters for the default aliases to apply.
world-rewrite {
#world_nether = "world"
#world_the_end = "world"
}
# Define special group weights for this server.
#
# - Group weights can also be applied directly to group data, using the setweight command.
# - This section allows weights to be set on a per-server basis.
group-weight {
#admin = 10
}
# +----------------------------------------------------------------------------------------------+ #
# | | #
# | FINE TUNING OPTIONS | #
# | | #
# | A number of more niche settings for tweaking and changing behaviour. The section also | #
# | contains toggles for some more specialised features. It is only necessary to make changes to | #
# | these options if you want to fine-tune LuckPerms behaviour. | #
# | | #
# +----------------------------------------------------------------------------------------------+ #
# +----------------------------------------------------------------------------------------------+ #
# | Miscellaneous (and rarely used) settings | #
# +----------------------------------------------------------------------------------------------+ #
# If LuckPerms should produce extra logging output when it handles logins.
#
# - Useful if you're having issues with UUID forwarding or data not being loaded.
debug-logins = false
# If LuckPerms should allow usernames with non alphanumeric characters.
#
# - Note that due to the design of the storage implementation, usernames must still be 16 characters
# or less.
allow-invalid-usernames = false
# If LuckPerms should allow a users primary group to be removed with the 'parent remove' command.
#
# - When this happens, the plugin will set their primary group back to default.
prevent-primary-group-removal = false
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
# See here for more info: https://minecraft.gamepedia.com/Commands#Target_selectors
resolve-command-selectors = false

View File

@ -0,0 +1,16 @@
{
"required": true,
"package": "me.lucko.luckperms.fabric.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"ClientSettingsC2SPacketAccessor",
"PlayerManagerMixin",
"ServerLoginNetworkHandlerAccessor",
"ServerPlayerEntityMixin"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}

2
gradle.properties Normal file
View File

@ -0,0 +1,2 @@
# Fabric requires some more ram.
org.gradle.jvmargs=-Xmx1G

View File

@ -1,3 +1,14 @@
// Fabric Needs this
pluginManagement {
repositories {
jcenter()
maven {
url 'https://maven.fabricmc.net/'
}
gradlePluginPortal()
}
}
rootProject.name = 'luckperms'
include (
'api',
@ -5,7 +16,8 @@ include (
'bukkit',
'bukkit-legacy',
'bungee',
'sponge', 'sponge:sponge-service', 'sponge:sponge-service-api6', 'sponge:sponge-service-api7',
'fabric',
'nukkit',
'sponge', 'sponge:sponge-service', 'sponge:sponge-service-api6', 'sponge:sponge-service-api7',
'velocity'
)

View File

@ -32,6 +32,7 @@ import me.lucko.luckperms.common.command.access.CommandPermission;
import me.lucko.luckperms.common.config.generic.adapter.ConfigurationAdapter;
import me.lucko.luckperms.common.dependencies.Dependency;
import me.lucko.luckperms.common.event.AbstractEventBus;
import me.lucko.luckperms.common.locale.TranslationManager;
import me.lucko.luckperms.common.messaging.MessagingFactory;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.model.manager.track.StandardTrackManager;
@ -275,7 +276,7 @@ public class LPSpongePlugin extends AbstractLuckPermsPlugin {
return new DummySender(this, Sender.CONSOLE_UUID, Sender.CONSOLE_NAME) {
@Override
public void sendMessage(Component message) {
LPSpongePlugin.this.bootstrap.getPluginLogger().info(LegacyComponentSerializer.legacySection().serialize(message));
LPSpongePlugin.this.bootstrap.getPluginLogger().info(LegacyComponentSerializer.legacySection().serialize(TranslationManager.render(message)));
}
};
}