mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-23 11:07:01 +01:00
Port to NeoForge (#3950)
This commit is contained in:
parent
b21753f5e9
commit
642f1d3019
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# These are explicitly windows files and should use crlf
|
||||
*.bat text eol=crlf
|
@ -76,6 +76,7 @@ public interface Platform {
|
||||
NUKKIT("Nukkit"),
|
||||
VELOCITY("Velocity"),
|
||||
FABRIC("Fabric"),
|
||||
NEOFORGE("NeoForge"),
|
||||
FORGE("Forge"),
|
||||
STANDALONE("Standalone");
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
[versions]
|
||||
shadow = "8.1.7"
|
||||
blossom = "1.3.1"
|
||||
moddevgradle = "1.0.17"
|
||||
forgegradle = "[6.0,6.2)"
|
||||
loom = "1.6-SNAPSHOT"
|
||||
licenser = "0.6.1"
|
||||
@ -8,6 +9,7 @@ licenser = "0.6.1"
|
||||
[plugins]
|
||||
blossom = { id = "net.kyori.blossom", version.ref = "blossom" }
|
||||
shadow = { id = "io.github.goooler.shadow", version.ref = "shadow" }
|
||||
moddevgradle = { id = "net.neoforged.moddev", version.ref = "moddevgradle" }
|
||||
forgegradle = { id = "net.minecraftforge.gradle", version.ref = "forgegradle" }
|
||||
loom = { id = "fabric-loom", version.ref = "loom" }
|
||||
licenser = { id = "org.cadixdev.licenser", version.ref = "licenser" }
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
7
gradlew
vendored
7
gradlew
vendored
@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@ -84,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
|
63
neoforge/build.gradle
Normal file
63
neoforge/build.gradle
Normal file
@ -0,0 +1,63 @@
|
||||
plugins {
|
||||
alias(libs.plugins.blossom)
|
||||
alias(libs.plugins.shadow)
|
||||
alias(libs.plugins.moddevgradle)
|
||||
}
|
||||
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 21
|
||||
|
||||
Configuration shade = configurations.create('shade')
|
||||
configurations.implementation {
|
||||
extendsFrom configurations.shade
|
||||
}
|
||||
|
||||
blossom {
|
||||
replaceTokenIn 'src/main/java/me/lucko/luckperms/neoforge/LPNeoForgeBootstrap.java'
|
||||
replaceToken '@version@', project.ext.fullVersion
|
||||
}
|
||||
|
||||
neoForge {
|
||||
version = project.neoForgeVersion
|
||||
|
||||
validateAccessTransformers = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add('shade', project(':common'))
|
||||
compileOnly project(':common:loader-utils')
|
||||
compileOnly project(':neoforge:neoforge-api')
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = "luckperms-neoforge.jarinjar"
|
||||
configurations = [shade]
|
||||
|
||||
dependencies {
|
||||
include(dependency('me.lucko.luckperms:.*'))
|
||||
}
|
||||
|
||||
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 'io.nats.client', 'me.lucko.luckperms.lib.nats'
|
||||
relocate 'com.rabbitmq', 'me.lucko.luckperms.lib.rabbitmq'
|
||||
relocate 'org.apache.commons.pool2', 'me.lucko.luckperms.lib.commonspool2'
|
||||
relocate 'ninja.leaping.configurate', 'me.lucko.luckperms.lib.configurate'
|
||||
relocate 'org.yaml.snakeyaml', 'me.lucko.luckperms.lib.yaml'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives shadowJar
|
||||
}
|
2
neoforge/gradle.properties
Normal file
2
neoforge/gradle.properties
Normal file
@ -0,0 +1,2 @@
|
||||
minecraftVersion=1.21
|
||||
neoForgeVersion=21.0.161
|
93
neoforge/loader/build.gradle
Normal file
93
neoforge/loader/build.gradle
Normal file
@ -0,0 +1,93 @@
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import net.neoforged.moddevgradle.internal.RunGameTask
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.shadow)
|
||||
alias(libs.plugins.moddevgradle)
|
||||
id("java-library")
|
||||
}
|
||||
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 21
|
||||
|
||||
neoForge {
|
||||
version = project.neoForgeVersion
|
||||
|
||||
validateAccessTransformers = true
|
||||
|
||||
runs {
|
||||
client {
|
||||
client()
|
||||
mods.set(new HashSet()) // Work around classpath issues by using the production jar for dev runs
|
||||
}
|
||||
server {
|
||||
server()
|
||||
mods.set(new HashSet()) // Work around classpath issues by using the production jar for dev runs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Work around classpath issues by using the production jar for dev runs
|
||||
tasks.withType(RunGameTask).configureEach {
|
||||
dependsOn(tasks.shadowJar)
|
||||
doFirst {
|
||||
File jar = file("run/mods/main.jar")
|
||||
jar.parentFile.mkdirs()
|
||||
Files.copy(tasks.shadowJar.archiveFile.get().asFile.toPath(), jar.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
}
|
||||
|
||||
Configuration shade = configurations.create('shade')
|
||||
configurations.implementation {
|
||||
extendsFrom configurations.shade
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add('shade', project(':api'))
|
||||
add('shade', project(':common:loader-utils'))
|
||||
add('shade', project(':neoforge:neoforge-api'))
|
||||
}
|
||||
|
||||
build {
|
||||
dependsOn(":neoforge:build")
|
||||
dependsOn(":neoforge:neoforge-api:build")
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
'Implementation-Title': 'LuckPerms',
|
||||
'Implementation-Vendor': 'LuckPerms',
|
||||
'Implementation-Version': project.ext.fullVersion,
|
||||
'Specification-Title': 'luckperms',
|
||||
'Specification-Vendor': 'LuckPerms',
|
||||
'Specification-Version': '1'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
filesMatching('META-INF/neoforge.mods.toml') {
|
||||
expand 'version': project.ext.fullVersion
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveFileName = "LuckPerms-NeoForge-${project.ext.fullVersion}.jar"
|
||||
configurations = [shade]
|
||||
|
||||
from {
|
||||
project(':neoforge').tasks.shadowJar.archiveFile
|
||||
}
|
||||
|
||||
dependencies {
|
||||
include(dependency('net.luckperms:.*'))
|
||||
include(dependency('me.lucko.luckperms:.*'))
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives shadowJar
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.neoforge.loader;
|
||||
|
||||
import me.lucko.luckperms.common.loader.JarInJarClassLoader;
|
||||
import me.lucko.luckperms.common.loader.LoaderBootstrap;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.neoforged.fml.loading.FMLEnvironment;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mod(value = "luckperms")
|
||||
public class NeoForgeLoaderPlugin implements Supplier<ModContainer> {
|
||||
private static final Logger LOGGER = LogManager.getLogger("luckperms");
|
||||
|
||||
private static final String JAR_NAME = "luckperms-neoforge.jarinjar";
|
||||
private static final String BOOTSTRAP_CLASS = "me.lucko.luckperms.neoforge.LPNeoForgeBootstrap";
|
||||
|
||||
private final ModContainer container;
|
||||
|
||||
private JarInJarClassLoader loader;
|
||||
private LoaderBootstrap plugin;
|
||||
|
||||
public NeoForgeLoaderPlugin(final ModContainer modContainer, final IEventBus modBus) {
|
||||
this.container = modContainer;
|
||||
|
||||
if (FMLEnvironment.dist.isClient()) {
|
||||
LOGGER.info("Skipping LuckPerms init (not supported on the client!)");
|
||||
return;
|
||||
}
|
||||
|
||||
this.loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME);
|
||||
modBus.addListener(this::onCommonSetup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModContainer get() {
|
||||
return this.container;
|
||||
}
|
||||
|
||||
public void onCommonSetup(FMLCommonSetupEvent event) {
|
||||
this.plugin = this.loader.instantiatePlugin(BOOTSTRAP_CLASS, Supplier.class, this);
|
||||
this.plugin.onLoad();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
modLoader="javafml"
|
||||
loaderVersion="[1,)"
|
||||
license="MIT"
|
||||
issueTrackerURL="https://github.com/LuckPerms/LuckPerms/issues"
|
||||
showAsResourcePack=false
|
||||
|
||||
[[mods]]
|
||||
modId="luckperms"
|
||||
version="${version}"
|
||||
displayName="LuckPerms"
|
||||
displayURL="https://luckperms.net/"
|
||||
logoFile="luckperms.png"
|
||||
credits="Luck"
|
||||
authors="Luck"
|
||||
description="A permissions plugin for Minecraft servers."
|
||||
displayTest="IGNORE_ALL_VERSION"
|
BIN
neoforge/loader/src/main/resources/luckperms.png
Normal file
BIN
neoforge/loader/src/main/resources/luckperms.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
6
neoforge/loader/src/main/resources/pack.mcmeta
Normal file
6
neoforge/loader/src/main/resources/pack.mcmeta
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"pack": {
|
||||
"description": "LuckPerms resources",
|
||||
"pack_format": 9
|
||||
}
|
||||
}
|
16
neoforge/neoforge-api/build.gradle
Normal file
16
neoforge/neoforge-api/build.gradle
Normal file
@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
alias(libs.plugins.moddevgradle)
|
||||
}
|
||||
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 21
|
||||
|
||||
neoForge {
|
||||
version = project.neoForgeVersion
|
||||
|
||||
validateAccessTransformers = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':api')
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.neoforge.capabilities;
|
||||
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.neoforged.neoforge.capabilities.EntityCapability;
|
||||
|
||||
/**
|
||||
* A NeoForge {@link EntityCapability} that attaches LuckPerms functionality onto {@link ServerPlayer}s.
|
||||
*/
|
||||
public interface UserCapability {
|
||||
|
||||
/**
|
||||
* The identifier used for the capability
|
||||
*/
|
||||
ResourceLocation IDENTIFIER = ResourceLocation.fromNamespaceAndPath("luckperms", "user");
|
||||
|
||||
/**
|
||||
* The capability instance.
|
||||
*/
|
||||
EntityCapability<UserCapability, Void> CAPABILITY = EntityCapability.createVoid(IDENTIFIER, UserCapability.class);
|
||||
|
||||
/**
|
||||
* Checks for a permission.
|
||||
*
|
||||
* @param permission the permission
|
||||
* @return the result
|
||||
*/
|
||||
default boolean hasPermission(String permission) {
|
||||
return checkPermission(permission).asBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a permission check.
|
||||
*
|
||||
* @param permission the permission
|
||||
* @return the result
|
||||
*/
|
||||
Tristate checkPermission(String permission);
|
||||
|
||||
/**
|
||||
* Runs a permission check.
|
||||
*
|
||||
* @param permission the permission
|
||||
* @param queryOptions the query options
|
||||
* @return the result
|
||||
*/
|
||||
Tristate checkPermission(String permission, QueryOptions queryOptions);
|
||||
|
||||
/**
|
||||
* Gets the user's currently query options.
|
||||
*
|
||||
* @return the current query options for the user
|
||||
*/
|
||||
QueryOptions getQueryOptions();
|
||||
|
||||
}
|
@ -0,0 +1,287 @@
|
||||
/*
|
||||
* 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.neoforge;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import me.lucko.luckperms.common.loader.LoaderBootstrap;
|
||||
import me.lucko.luckperms.common.plugin.bootstrap.BootstrappedWithLoader;
|
||||
import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
|
||||
import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender;
|
||||
import me.lucko.luckperms.common.plugin.classpath.JarInJarClassPathAppender;
|
||||
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 me.lucko.luckperms.neoforge.util.NeoForgeEventBusFacade;
|
||||
import net.luckperms.api.platform.Platform;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import net.neoforged.bus.api.EventPriority;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.ModList;
|
||||
import net.neoforged.fml.loading.FMLPaths;
|
||||
import net.neoforged.neoforge.event.server.ServerAboutToStartEvent;
|
||||
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
|
||||
import net.neoforged.neoforgespi.language.IModInfo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
|
||||
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;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Bootstrap plugin for LuckPerms running on Forge.
|
||||
*/
|
||||
public final class LPNeoForgeBootstrap implements LuckPermsBootstrap, LoaderBootstrap, BootstrappedWithLoader {
|
||||
public static final String ID = "luckperms";
|
||||
|
||||
/**
|
||||
* The plugin loader
|
||||
*/
|
||||
private final Supplier<ModContainer> loader;
|
||||
|
||||
/**
|
||||
* The plugin logger
|
||||
*/
|
||||
private final PluginLogger logger;
|
||||
|
||||
/**
|
||||
* A scheduler adapter for the platform
|
||||
*/
|
||||
private final SchedulerAdapter schedulerAdapter;
|
||||
|
||||
/**
|
||||
* The plugin class path appender
|
||||
*/
|
||||
private final ClassPathAppender classPathAppender;
|
||||
|
||||
/**
|
||||
* A facade for the forge event bus, compatible with LP's jar-in-jar packaging
|
||||
*/
|
||||
private final NeoForgeEventBusFacade forgeEventBus;
|
||||
|
||||
/**
|
||||
* The plugin instance
|
||||
*/
|
||||
private final LPNeoForgePlugin 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 LPNeoForgeBootstrap(Supplier<ModContainer> loader) {
|
||||
this.loader = loader;
|
||||
this.logger = new Log4jPluginLogger(LogManager.getLogger(LPNeoForgeBootstrap.ID));
|
||||
this.schedulerAdapter = new NeoForgeSchedulerAdapter(this);
|
||||
this.classPathAppender = new JarInJarClassPathAppender(getClass().getClassLoader());
|
||||
this.forgeEventBus = new NeoForgeEventBusFacade();
|
||||
this.plugin = new LPNeoForgePlugin(this);
|
||||
}
|
||||
|
||||
// provide adapters
|
||||
|
||||
@Override
|
||||
public Object getLoader() {
|
||||
return this.loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getPluginLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchedulerAdapter getScheduler() {
|
||||
return this.schedulerAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassPathAppender getClassPathAppender() {
|
||||
return this.classPathAppender;
|
||||
}
|
||||
|
||||
public void registerListeners(Object target) {
|
||||
this.forgeEventBus.register(target);
|
||||
}
|
||||
|
||||
// lifecycle
|
||||
|
||||
@Override
|
||||
public void onLoad() { // called by the loader on FMLCommonSetupEvent
|
||||
this.startTime = Instant.now();
|
||||
try {
|
||||
this.plugin.load();
|
||||
} finally {
|
||||
this.loadLatch.countDown();
|
||||
}
|
||||
|
||||
this.forgeEventBus.register(this);
|
||||
this.plugin.registerEarlyListeners();
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void onServerAboutToStart(ServerAboutToStartEvent event) {
|
||||
this.server = event.getServer();
|
||||
try {
|
||||
this.plugin.enable();
|
||||
} finally {
|
||||
this.enableLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onServerStopping(ServerStoppingEvent event) {
|
||||
this.plugin.disable();
|
||||
this.forgeEventBus.unregisterAll();
|
||||
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 "@version@";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getStartupTime() {
|
||||
return this.startTime;
|
||||
}
|
||||
|
||||
// provide information about the platform
|
||||
|
||||
@Override
|
||||
public Platform.Type getType() {
|
||||
return Platform.Type.NEOFORGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerBrand() {
|
||||
return ModList.get().getModContainerById("neoforge")
|
||||
.map(ModContainer::getModInfo)
|
||||
.map(IModInfo::getDisplayName)
|
||||
.orElse("null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerVersion() {
|
||||
String forgeVersion = ModList.get().getModContainerById("neoforge")
|
||||
.map(ModContainer::getModInfo)
|
||||
.map(IModInfo::getVersion)
|
||||
.map(ArtifactVersion::toString)
|
||||
.orElse("null");
|
||||
|
||||
return getServer().map(MinecraftServer::getServerVersion).orElse("null") + "-" + forgeVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getDataDirectory() {
|
||||
return FMLPaths.CONFIGDIR.get().resolve(LPNeoForgeBootstrap.ID).toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ServerPlayer> getPlayer(UUID uniqueId) {
|
||||
return getServer().map(MinecraftServer::getPlayerList).map(playerList -> playerList.getPlayer(uniqueId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UUID> lookupUniqueId(String username) {
|
||||
return getServer().map(MinecraftServer::getProfileCache).flatMap(profileCache -> profileCache.get(username)).map(GameProfile::getId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> lookupUsername(UUID uniqueId) {
|
||||
return getServer().map(MinecraftServer::getProfileCache).flatMap(profileCache -> profileCache.get(uniqueId)).map(GameProfile::getName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerCount() {
|
||||
return getServer().map(MinecraftServer::getPlayerCount).orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getPlayerList() {
|
||||
return getServer().map(MinecraftServer::getPlayerList).map(PlayerList::getPlayers).map(players -> {
|
||||
List<String> list = new ArrayList<>(players.size());
|
||||
for (ServerPlayer player : players) {
|
||||
list.add(player.getGameProfile().getName());
|
||||
}
|
||||
return list;
|
||||
}).orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<UUID> getOnlinePlayers() {
|
||||
return getServer().map(MinecraftServer::getPlayerList).map(PlayerList::getPlayers).map(players -> {
|
||||
List<UUID> list = new ArrayList<>(players.size());
|
||||
for (ServerPlayer player : players) {
|
||||
list.add(player.getGameProfile().getId());
|
||||
}
|
||||
return list;
|
||||
}).orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerOnline(UUID uniqueId) {
|
||||
return getServer().map(MinecraftServer::getPlayerList).map(playerList -> playerList.getPlayer(uniqueId)).isPresent();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.neoforge;
|
||||
|
||||
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
|
||||
import me.lucko.luckperms.common.calculator.CalculatorFactory;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
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.DummyConsoleSender;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.neoforge.calculator.NeoForgeCalculatorFactory;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapabilityListener;
|
||||
import me.lucko.luckperms.neoforge.context.NeoForgeContextManager;
|
||||
import me.lucko.luckperms.neoforge.context.NeoForgePlayerCalculator;
|
||||
import me.lucko.luckperms.neoforge.listeners.NeoForgeAutoOpListener;
|
||||
import me.lucko.luckperms.neoforge.listeners.NeoForgeCommandListUpdater;
|
||||
import me.lucko.luckperms.neoforge.listeners.NeoForgeConnectionListener;
|
||||
import me.lucko.luckperms.neoforge.listeners.NeoForgePlatformListener;
|
||||
import me.lucko.luckperms.neoforge.messaging.NeoForgeMessagingFactory;
|
||||
import me.lucko.luckperms.neoforge.messaging.PluginMessageMessenger;
|
||||
import me.lucko.luckperms.neoforge.service.NeoForgePermissionHandlerListener;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* LuckPerms implementation for Forge.
|
||||
*/
|
||||
public class LPNeoForgePlugin extends AbstractLuckPermsPlugin {
|
||||
private final LPNeoForgeBootstrap bootstrap;
|
||||
|
||||
private NeoForgeSenderFactory senderFactory;
|
||||
private NeoForgeConnectionListener connectionListener;
|
||||
private NeoForgeCommandExecutor commandManager;
|
||||
private StandardUserManager userManager;
|
||||
private StandardGroupManager groupManager;
|
||||
private StandardTrackManager trackManager;
|
||||
private NeoForgeContextManager contextManager;
|
||||
|
||||
public LPNeoForgePlugin(LPNeoForgeBootstrap bootstrap) {
|
||||
this.bootstrap = bootstrap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LPNeoForgeBootstrap getBootstrap() {
|
||||
return this.bootstrap;
|
||||
}
|
||||
|
||||
protected void registerEarlyListeners() {
|
||||
this.connectionListener = new NeoForgeConnectionListener(this);
|
||||
this.bootstrap.registerListeners(this.connectionListener);
|
||||
|
||||
NeoForgePlatformListener platformListener = new NeoForgePlatformListener(this);
|
||||
this.bootstrap.registerListeners(platformListener);
|
||||
|
||||
UserCapabilityListener userCapabilityListener = new UserCapabilityListener();
|
||||
this.bootstrap.registerListeners(userCapabilityListener);
|
||||
|
||||
NeoForgePermissionHandlerListener permissionHandlerListener = new NeoForgePermissionHandlerListener(this);
|
||||
this.bootstrap.registerListeners(permissionHandlerListener);
|
||||
|
||||
this.commandManager = new NeoForgeCommandExecutor(this);
|
||||
this.bootstrap.registerListeners(this.commandManager);
|
||||
|
||||
PluginMessageMessenger.registerChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupSenderFactory() {
|
||||
this.senderFactory = new NeoForgeSenderFactory(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() {
|
||||
return new NeoForgeConfigAdapter(this, resolveConfig("luckperms.conf"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerPlatformListeners() {
|
||||
// Too late for Forge, registered in #registerEarlyListeners
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessagingFactory<?> provideMessagingFactory() {
|
||||
return new NeoForgeMessagingFactory(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerCommands() {
|
||||
// Too late for Forge, registered in #registerEarlyListeners
|
||||
}
|
||||
|
||||
@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 NeoForgeCalculatorFactory(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupContextManager() {
|
||||
this.contextManager = new NeoForgeContextManager(this);
|
||||
|
||||
NeoForgePlayerCalculator playerCalculator = new NeoForgePlayerCalculator(this, getConfiguration().get(ConfigKeys.DISABLED_CONTEXTS));
|
||||
this.bootstrap.registerListeners(playerCalculator);
|
||||
this.contextManager.registerCalculator(playerCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupPlatformHooks() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractEventBus<ModContainer> provideEventBus(LuckPermsApiProvider provider) {
|
||||
return new NeoForgeEventBus(this, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerApiOnPlatform(LuckPerms api) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performFinalSetup() {
|
||||
// register autoop listener
|
||||
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
getApiProvider().getEventBus().subscribe(new NeoForgeAutoOpListener(this));
|
||||
}
|
||||
|
||||
// register forge command list updater
|
||||
if (getConfiguration().get(ConfigKeys.UPDATE_CLIENT_COMMAND_LIST)) {
|
||||
getApiProvider().getEventBus().subscribe(new NeoForgeCommandListUpdater(this));
|
||||
}
|
||||
}
|
||||
|
||||
@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::getPlayerList)
|
||||
.map(PlayerList::getPlayers)
|
||||
.map(players -> players.stream().map(player -> this.senderFactory.wrap(player.createCommandSourceStack()))).orElseGet(Stream::empty)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getConsoleSender() {
|
||||
return this.bootstrap.getServer()
|
||||
.map(server -> this.senderFactory.wrap(server.createCommandSourceStack()))
|
||||
.orElseGet(() -> new DummyConsoleSender(this) {
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
LPNeoForgePlugin.this.bootstrap.getPluginLogger().info(PlainTextComponentSerializer.plainText().serialize(TranslationManager.render(message)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public NeoForgeSenderFactory getSenderFactory() {
|
||||
return this.senderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NeoForgeConnectionListener getConnectionListener() {
|
||||
return this.connectionListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NeoForgeCommandExecutor getCommandManager() {
|
||||
return this.commandManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardUserManager getUserManager() {
|
||||
return this.userManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardGroupManager getGroupManager() {
|
||||
return this.groupManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardTrackManager getTrackManager() {
|
||||
return this.trackManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NeoForgeContextManager getContextManager() {
|
||||
return this.contextManager;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.neoforge;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import me.lucko.luckperms.common.command.BrigadierCommandExecutor;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.neoforge.event.RegisterCommandsEvent;
|
||||
|
||||
public class NeoForgeCommandExecutor extends BrigadierCommandExecutor<CommandSourceStack> {
|
||||
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
public NeoForgeCommandExecutor(LPNeoForgePlugin plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRegisterCommands(RegisterCommandsEvent event) {
|
||||
for (String alias : COMMAND_ALIASES) {
|
||||
LiteralCommandNode<CommandSourceStack> command = Commands.literal(alias).executes(this).build();
|
||||
ArgumentCommandNode<CommandSourceStack, String> argument = Commands.argument("args", StringArgumentType.greedyString())
|
||||
.suggests(this)
|
||||
.executes(this)
|
||||
.build();
|
||||
|
||||
command.addChild(argument);
|
||||
event.getDispatcher().getRoot().addChild(command);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sender getSender(CommandSourceStack source) {
|
||||
return this.plugin.getSenderFactory().wrap(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> resolveSelectors(CommandSourceStack source, List<String> args) {
|
||||
// usage of @ selectors requires at least level 2 permission
|
||||
CommandSourceStack atAllowedSource = source.hasPermission(2) ? source : source.withPermission(2);
|
||||
for (ListIterator<String> it = args.listIterator(); it.hasNext(); ) {
|
||||
String arg = it.next();
|
||||
if (arg.isEmpty() || arg.charAt(0) != '@') {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<ServerPlayer> matchedPlayers;
|
||||
try {
|
||||
matchedPlayers = EntityArgument.entities().parse(new StringReader(arg)).findPlayers(atAllowedSource);
|
||||
} catch (CommandSyntaxException e) {
|
||||
this.plugin.getLogger().warn("Error parsing selector '" + arg + "' for " + source + " executing " + args, e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchedPlayers.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchedPlayers.size() > 1) {
|
||||
this.plugin.getLogger().warn("Error parsing selector '" + arg + "' for " + source + " executing " + args +
|
||||
": ambiguous result (more than one player matched) - " + matchedPlayers);
|
||||
continue;
|
||||
}
|
||||
|
||||
ServerPlayer player = matchedPlayers.get(0);
|
||||
it.set(player.getStringUUID());
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
}
|
@ -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.neoforge;
|
||||
|
||||
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 NeoForgeConfigAdapter extends ConfigurateConfigAdapter {
|
||||
public NeoForgeConfigAdapter(LuckPermsPlugin plugin, Path path) {
|
||||
super(plugin, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationLoader<? extends ConfigurationNode> createLoader(Path path) {
|
||||
return HoconConfigurationLoader.builder().setPath(path).build();
|
||||
}
|
||||
|
||||
}
|
@ -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.neoforge;
|
||||
|
||||
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
|
||||
import me.lucko.luckperms.common.event.AbstractEventBus;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
|
||||
public class NeoForgeEventBus extends AbstractEventBus<ModContainer> {
|
||||
public NeoForgeEventBus(LuckPermsPlugin plugin, LuckPermsApiProvider apiProvider) {
|
||||
super(plugin, apiProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModContainer checkPlugin(Object modContainer) throws IllegalArgumentException {
|
||||
if (modContainer instanceof ModContainer container) {
|
||||
return container;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Object " + modContainer + " (" + modContainer.getClass().getName() + ") is not a ModContainer.");
|
||||
}
|
||||
|
||||
}
|
@ -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.neoforge;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.scheduler.AbstractJavaScheduler;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class NeoForgeSchedulerAdapter extends AbstractJavaScheduler {
|
||||
private final Executor sync;
|
||||
|
||||
public NeoForgeSchedulerAdapter(LPNeoForgeBootstrap bootstrap) {
|
||||
super(bootstrap);
|
||||
this.sync = r -> bootstrap.getServer().orElseThrow(() -> new IllegalStateException("Server not ready")).executeBlocking(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor sync() {
|
||||
return this.sync;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.neoforge;
|
||||
|
||||
import com.mojang.brigadier.ParseResults;
|
||||
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
|
||||
import me.lucko.luckperms.common.locale.TranslationManager;
|
||||
import me.lucko.luckperms.common.query.QueryOptionsImpl;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.sender.SenderFactory;
|
||||
import me.lucko.luckperms.common.verbose.VerboseCheckTarget;
|
||||
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapability;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapabilityImpl;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
import net.minecraft.commands.CommandSource;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.network.chat.Component.Serializer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.rcon.RconConsoleSource;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public class NeoForgeSenderFactory extends SenderFactory<LPNeoForgePlugin, CommandSourceStack> {
|
||||
public NeoForgeSenderFactory(LPNeoForgePlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UUID getUniqueId(CommandSourceStack commandSource) {
|
||||
if (commandSource.getEntity() instanceof Player) {
|
||||
return commandSource.getEntity().getUUID();
|
||||
}
|
||||
return Sender.CONSOLE_UUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName(CommandSourceStack commandSource) {
|
||||
if (commandSource.getEntity() instanceof Player) {
|
||||
return commandSource.getTextName();
|
||||
}
|
||||
return Sender.CONSOLE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessage(CommandSourceStack sender, Component message) {
|
||||
Locale locale;
|
||||
if (sender.getEntity() instanceof ServerPlayer) {
|
||||
ServerPlayer player = (ServerPlayer) sender.getEntity();
|
||||
UserCapabilityImpl user = UserCapabilityImpl.get(player);
|
||||
locale = user.getLocale(player);
|
||||
} else {
|
||||
locale = null;
|
||||
}
|
||||
|
||||
sender.sendSuccess(() -> toNativeText(TranslationManager.render(message, locale)), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Tristate getPermissionValue(CommandSourceStack commandSource, String node) {
|
||||
if (commandSource.getEntity() instanceof ServerPlayer) {
|
||||
ServerPlayer player = (ServerPlayer) commandSource.getEntity();
|
||||
UserCapability user = UserCapabilityImpl.get(player);
|
||||
return user.checkPermission(node);
|
||||
}
|
||||
|
||||
VerboseCheckTarget target = VerboseCheckTarget.internal(commandSource.getTextName());
|
||||
getPlugin().getVerboseHandler().offerPermissionCheckEvent(CheckOrigin.PLATFORM_API_HAS_PERMISSION, target, QueryOptionsImpl.DEFAULT_CONTEXTUAL, node, TristateResult.UNDEFINED);
|
||||
getPlugin().getPermissionRegistry().offer(node);
|
||||
return Tristate.UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasPermission(CommandSourceStack commandSource, String node) {
|
||||
return getPermissionValue(commandSource, node).asBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performCommand(CommandSourceStack sender, String command) {
|
||||
ParseResults<CommandSourceStack> results = sender.getServer().getCommands().getDispatcher().parse(command, sender);
|
||||
sender.getServer().getCommands().performCommand(results, command);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConsole(CommandSourceStack sender) {
|
||||
CommandSource output = sender.source;
|
||||
return output == sender.getServer() || // Console
|
||||
output.getClass() == RconConsoleSource.class || // Rcon
|
||||
(output == CommandSource.NULL && sender.getTextName().equals("")); // Functions
|
||||
}
|
||||
|
||||
public static net.minecraft.network.chat.Component toNativeText(Component component) {
|
||||
return Serializer.fromJson(GsonComponentSerializer.gson().serializeToTree(component), RegistryAccess.EMPTY);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.neoforge.calculator;
|
||||
|
||||
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.DirectProcessor;
|
||||
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.neoforge.LPNeoForgePlugin;
|
||||
import me.lucko.luckperms.neoforge.context.NeoForgeContextManager;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NeoForgeCalculatorFactory implements CalculatorFactory {
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
public NeoForgeCalculatorFactory(LPNeoForgePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionCalculator build(QueryOptions queryOptions, CacheMetadata metadata) {
|
||||
List<PermissionProcessor> processors = new ArrayList<>(5);
|
||||
|
||||
processors.add(new DirectProcessor());
|
||||
|
||||
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(NeoForgeContextManager.INTEGRATED_SERVER_OWNER).orElse(false);
|
||||
if (integratedOwner && this.plugin.getConfiguration().get(ConfigKeys.INTEGRATED_SERVER_OWNER_BYPASSES_CHECKS)) {
|
||||
processors.add(ServerOwnerProcessor.INSTANCE);
|
||||
}
|
||||
|
||||
return new PermissionCalculator(this.plugin, metadata, processors);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.neoforge.calculator;
|
||||
|
||||
import me.lucko.luckperms.common.cacheddata.result.TristateResult;
|
||||
import me.lucko.luckperms.common.calculator.processor.AbstractPermissionProcessor;
|
||||
import me.lucko.luckperms.common.calculator.processor.PermissionProcessor;
|
||||
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 implements PermissionProcessor {
|
||||
private static final TristateResult TRUE_RESULT = new TristateResult.Factory(ServerOwnerProcessor.class).result(Tristate.TRUE);
|
||||
|
||||
public static final ServerOwnerProcessor INSTANCE = new ServerOwnerProcessor();
|
||||
|
||||
private ServerOwnerProcessor() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public TristateResult hasPermission(String permission) {
|
||||
return TRUE_RESULT;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.neoforge.capabilities;
|
||||
|
||||
import java.util.Optional;
|
||||
import me.lucko.luckperms.common.cacheddata.type.PermissionCache;
|
||||
import me.lucko.luckperms.common.context.manager.QueryOptionsCache;
|
||||
import me.lucko.luckperms.common.locale.TranslationManager;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
|
||||
import me.lucko.luckperms.neoforge.context.NeoForgeContextManager;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class UserCapabilityImpl implements UserCapability {
|
||||
|
||||
private static Optional<UserCapability> getCapability(Player player) {
|
||||
return Optional.ofNullable(player.getCapability(CAPABILITY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link UserCapability} for a given {@link ServerPlayer}.
|
||||
*
|
||||
* @param player the player
|
||||
* @return the capability
|
||||
*/
|
||||
public static @NotNull UserCapabilityImpl get(@NotNull Player player) {
|
||||
return (UserCapabilityImpl) getCapability(player).orElseThrow(() -> new IllegalStateException("Capability missing for " + player.getUUID()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link UserCapability} for a given {@link ServerPlayer}.
|
||||
*
|
||||
* @param player the player
|
||||
* @return the capability, or null
|
||||
*/
|
||||
public static @Nullable UserCapabilityImpl getNullable(@NotNull Player player) {
|
||||
return (UserCapabilityImpl) getCapability(player).orElse(null);
|
||||
}
|
||||
|
||||
private boolean initialised = false;
|
||||
private boolean invalidated = false;
|
||||
|
||||
private User user;
|
||||
private QueryOptionsCache<ServerPlayer> queryOptionsCache;
|
||||
private String language;
|
||||
private Locale locale;
|
||||
|
||||
public UserCapabilityImpl() {
|
||||
|
||||
}
|
||||
|
||||
public void initialise(UserCapabilityImpl previous) {
|
||||
this.user = previous.user;
|
||||
this.queryOptionsCache = previous.queryOptionsCache;
|
||||
this.language = previous.language;
|
||||
this.locale = previous.locale;
|
||||
this.initialised = true;
|
||||
}
|
||||
|
||||
public void initialise(User user, ServerPlayer player, NeoForgeContextManager contextManager) {
|
||||
this.user = user;
|
||||
this.queryOptionsCache = new QueryOptionsCache<>(player, contextManager);
|
||||
this.initialised = true;
|
||||
}
|
||||
|
||||
private void assertInitialised() {
|
||||
if (!this.initialised) {
|
||||
throw new IllegalStateException("Capability has not been initialised");
|
||||
}
|
||||
if (this.invalidated) {
|
||||
throw new IllegalStateException("Capability has been invalidated");
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
this.invalidated = false;
|
||||
this.user = null;
|
||||
this.queryOptionsCache = null;
|
||||
this.language = null;
|
||||
this.locale = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tristate checkPermission(String permission) {
|
||||
assertInitialised();
|
||||
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
return checkPermission(permission, this.queryOptionsCache.getQueryOptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tristate checkPermission(String permission, QueryOptions queryOptions) {
|
||||
assertInitialised();
|
||||
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
if (queryOptions == null) {
|
||||
throw new NullPointerException("queryOptions");
|
||||
}
|
||||
|
||||
PermissionCache cache = this.user.getCachedData().getPermissionData(queryOptions);
|
||||
return cache.checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result();
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
assertInitialised();
|
||||
return this.user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return getQueryOptionsCache().getQueryOptions();
|
||||
}
|
||||
|
||||
public QueryOptionsCache<ServerPlayer> getQueryOptionsCache() {
|
||||
assertInitialised();
|
||||
return this.queryOptionsCache;
|
||||
}
|
||||
|
||||
public Locale getLocale(ServerPlayer player) {
|
||||
if (this.language == null || !this.language.equals(player.getLanguage())) {
|
||||
this.language = player.getLanguage();
|
||||
this.locale = TranslationManager.parseLocale(this.language);
|
||||
}
|
||||
|
||||
return this.locale;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.neoforge.capabilities;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||
|
||||
public class UserCapabilityListener {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRegisterCapabilities(RegisterCapabilitiesEvent event) {
|
||||
event.registerEntity(
|
||||
UserCapability.CAPABILITY,
|
||||
EntityType.PLAYER,
|
||||
(player, ctx) -> {
|
||||
if (!(player instanceof ServerPlayer)) {
|
||||
// Don't attach to LocalPlayer
|
||||
return null;
|
||||
}
|
||||
return new UserCapabilityImpl();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerClone(PlayerEvent.Clone event) {
|
||||
Player previousPlayer = event.getOriginal();
|
||||
Player currentPlayer = event.getEntity();
|
||||
|
||||
try {
|
||||
UserCapabilityImpl previous = UserCapabilityImpl.get(previousPlayer);
|
||||
UserCapabilityImpl current = UserCapabilityImpl.get(currentPlayer);
|
||||
|
||||
current.initialise(previous);
|
||||
previous.invalidate();
|
||||
current.getQueryOptionsCache().invalidate();
|
||||
} catch (IllegalStateException e) {
|
||||
// continue on if we cannot copy original data
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.neoforge.context;
|
||||
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.context.manager.ContextManager;
|
||||
import me.lucko.luckperms.common.context.manager.QueryOptionsCache;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapabilityImpl;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.query.OptionKey;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class NeoForgeContextManager extends ContextManager<ServerPlayer, ServerPlayer> {
|
||||
public static final OptionKey<Boolean> INTEGRATED_SERVER_OWNER = OptionKey.of("integrated_server_owner", Boolean.class);
|
||||
|
||||
public NeoForgeContextManager(LPNeoForgePlugin plugin) {
|
||||
super(plugin, ServerPlayer.class, ServerPlayer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId(ServerPlayer player) {
|
||||
return player.getUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptionsCache<ServerPlayer> getCacheFor(ServerPlayer subject) {
|
||||
if (subject == null) {
|
||||
throw new NullPointerException("subject");
|
||||
}
|
||||
|
||||
return UserCapabilityImpl.get(subject).getQueryOptionsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions formQueryOptions(ServerPlayer subject, ImmutableContextSet contextSet) {
|
||||
QueryOptions.Builder builder = this.plugin.getConfiguration().get(ConfigKeys.GLOBAL_QUERY_OPTIONS).toBuilder();
|
||||
if (subject.getServer() != null && subject.getServer().isSingleplayerOwner(subject.getGameProfile())) {
|
||||
builder.option(INTEGRATED_SERVER_OWNER, true);
|
||||
}
|
||||
|
||||
return builder.context(contextSet).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(ServerPlayer subject) {
|
||||
UserCapabilityImpl capability = UserCapabilityImpl.getNullable(subject);
|
||||
if (capability != null) {
|
||||
capability.getQueryOptionsCache().invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.neoforge.context;
|
||||
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
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.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class NeoForgePlayerCalculator implements ContextCalculator<ServerPlayer> {
|
||||
/**
|
||||
* GameType.NOT_SET(-1, "") was removed in 1.17
|
||||
*/
|
||||
private static final int GAME_MODE_NOT_SET = -1;
|
||||
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
private final boolean gamemode;
|
||||
private final boolean world;
|
||||
private final boolean dimensionType;
|
||||
|
||||
public NeoForgePlayerCalculator(LPNeoForgePlugin plugin, Set<String> disabled) {
|
||||
this.plugin = plugin;
|
||||
this.gamemode = !disabled.contains(DefaultContextKeys.GAMEMODE_KEY);
|
||||
this.world = !disabled.contains(DefaultContextKeys.WORLD_KEY);
|
||||
this.dimensionType = !disabled.contains(DefaultContextKeys.DIMENSION_TYPE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calculate(@NonNull ServerPlayer target, @NonNull ContextConsumer consumer) {
|
||||
ServerLevel level = target.serverLevel();
|
||||
if (this.dimensionType) {
|
||||
consumer.accept(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(level.dimension().location()));
|
||||
}
|
||||
|
||||
ServerLevelData levelData = (ServerLevelData) level.getLevelData();
|
||||
if (this.world) {
|
||||
this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).rewriteAndSubmit(levelData.getLevelName(), consumer);
|
||||
}
|
||||
|
||||
GameType gameMode = target.gameMode.getGameModeForPlayer();
|
||||
if (this.gamemode && gameMode.getId() != GAME_MODE_NOT_SET) {
|
||||
consumer.accept(DefaultContextKeys.GAMEMODE_KEY, gameMode.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ContextSet estimatePotentialContexts() {
|
||||
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
|
||||
|
||||
if (this.gamemode) {
|
||||
for (GameType gameType : GameType.values()) {
|
||||
if (gameType.getId() == GAME_MODE_NOT_SET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.add(DefaultContextKeys.GAMEMODE_KEY, gameType.getName());
|
||||
}
|
||||
}
|
||||
|
||||
MinecraftServer server = this.plugin.getBootstrap().getServer().orElse(null);
|
||||
if (this.dimensionType && server != null) {
|
||||
server.registryAccess().registry(Registries.DIMENSION_TYPE).ifPresent(registry -> {
|
||||
for (ResourceLocation resourceLocation : registry.keySet()) {
|
||||
builder.add(DefaultContextKeys.DIMENSION_TYPE_KEY, getContextKey(resourceLocation));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.world && server != null) {
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
ServerLevelData levelData = (ServerLevelData) level.getLevelData();
|
||||
if (Context.isValidValue(levelData.getLevelName())) {
|
||||
builder.add(DefaultContextKeys.WORLD_KEY, levelData.getLevelName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static String getContextKey(ResourceLocation key) {
|
||||
if (key.getNamespace().equals("minecraft")) {
|
||||
return key.getPath();
|
||||
}
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
|
||||
if (!(this.world || this.dimensionType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.getContextManager().signalContextUpdate((ServerPlayer) event.getEntity());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerChangeGameMode(PlayerEvent.PlayerChangeGameModeEvent event) {
|
||||
if (!this.gamemode || event.getNewGameMode().getId() == GAME_MODE_NOT_SET) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.getContextManager().signalContextUpdate((ServerPlayer) event.getEntity());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.neoforge.listeners;
|
||||
|
||||
import me.lucko.luckperms.common.api.implementation.ApiUser;
|
||||
import me.lucko.luckperms.common.event.LuckPermsEventListener;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import net.luckperms.api.event.EventBus;
|
||||
import net.luckperms.api.event.context.ContextUpdateEvent;
|
||||
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class NeoForgeAutoOpListener implements LuckPermsEventListener {
|
||||
private static final String NODE = "luckperms.autoop";
|
||||
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
public NeoForgeAutoOpListener(LPNeoForgePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(EventBus bus) {
|
||||
bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate);
|
||||
bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate);
|
||||
}
|
||||
|
||||
private void onContextUpdate(ContextUpdateEvent event) {
|
||||
event.getSubject(ServerPlayer.class).ifPresent(player -> refreshAutoOp(player, true));
|
||||
}
|
||||
|
||||
private void onUserDataRecalculate(UserDataRecalculateEvent event) {
|
||||
User user = ApiUser.cast(event.getUser());
|
||||
this.plugin.getBootstrap().getPlayer(user.getUniqueId()).ifPresent(player -> refreshAutoOp(player, false));
|
||||
}
|
||||
|
||||
private void refreshAutoOp(ServerPlayer player, boolean callerIsSync) {
|
||||
if (!callerIsSync && !this.plugin.getBootstrap().getServer().isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
User user = this.plugin.getUserManager().getIfLoaded(player.getUUID());
|
||||
|
||||
boolean value;
|
||||
if (user != null) {
|
||||
QueryOptions queryOptions = this.plugin.getContextManager().getQueryOptions(player);
|
||||
Map<String, Boolean> permData = user.getCachedData().getPermissionData(queryOptions).getPermissionMap();
|
||||
value = permData.getOrDefault(NODE, false);
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
|
||||
if (callerIsSync) {
|
||||
setOp(player, value);
|
||||
} else {
|
||||
this.plugin.getBootstrap().getScheduler().executeSync(() -> setOp(player, value));
|
||||
}
|
||||
}
|
||||
|
||||
private void setOp(ServerPlayer player, boolean value) {
|
||||
this.plugin.getBootstrap().getServer().ifPresent(server -> {
|
||||
if (value) {
|
||||
server.getPlayerList().op(player.getGameProfile());
|
||||
} else {
|
||||
server.getPlayerList().deop(player.getGameProfile());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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.neoforge.listeners;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiGroup;
|
||||
import me.lucko.luckperms.common.cache.BufferedRequest;
|
||||
import me.lucko.luckperms.common.event.LuckPermsEventListener;
|
||||
import me.lucko.luckperms.common.util.CaffeineFactory;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import net.luckperms.api.event.EventBus;
|
||||
import net.luckperms.api.event.context.ContextUpdateEvent;
|
||||
import net.luckperms.api.event.group.GroupDataRecalculateEvent;
|
||||
import net.luckperms.api.event.user.UserDataRecalculateEvent;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Calls {@link net.minecraft.server.players.PlayerList#sendPlayerPermissionLevel(ServerPlayer)} when a players permissions change.
|
||||
*/
|
||||
public class NeoForgeCommandListUpdater implements LuckPermsEventListener {
|
||||
private final LPNeoForgePlugin plugin;
|
||||
private final LoadingCache<UUID, SendBuffer> sendingBuffers = CaffeineFactory.newBuilder()
|
||||
.expireAfterAccess(10, TimeUnit.SECONDS)
|
||||
.build(SendBuffer::new);
|
||||
|
||||
public NeoForgeCommandListUpdater(LPNeoForgePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(EventBus bus) {
|
||||
bus.subscribe(UserDataRecalculateEvent.class, this::onUserDataRecalculate);
|
||||
bus.subscribe(GroupDataRecalculateEvent.class, this::onGroupDataRecalculate);
|
||||
bus.subscribe(ContextUpdateEvent.class, this::onContextUpdate);
|
||||
}
|
||||
|
||||
private void onUserDataRecalculate(UserDataRecalculateEvent e) {
|
||||
requestUpdate(e.getUser().getUniqueId());
|
||||
}
|
||||
|
||||
private void onGroupDataRecalculate(GroupDataRecalculateEvent e) {
|
||||
plugin.getUserManager().getAll().values().forEach(user -> {
|
||||
if (user.resolveInheritanceTree(user.getQueryOptions()).contains(ApiGroup.cast(e.getGroup()))) {
|
||||
requestUpdate(user.getUniqueId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onContextUpdate(ContextUpdateEvent e) {
|
||||
e.getSubject(ServerPlayer.class).ifPresent(p -> requestUpdate(p.getUUID()));
|
||||
}
|
||||
|
||||
private void requestUpdate(UUID uniqueId) {
|
||||
if (!this.plugin.getBootstrap().isPlayerOnline(uniqueId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Buffer the request to send a commands update.
|
||||
SendBuffer sendBuffer = this.sendingBuffers.get(uniqueId);
|
||||
if (sendBuffer != null) {
|
||||
sendBuffer.request();
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the buffer times out.
|
||||
private void sendUpdate(UUID uniqueId) {
|
||||
this.plugin.getBootstrap().getScheduler().sync().execute(() -> {
|
||||
this.plugin.getBootstrap().getPlayer(uniqueId).ifPresent(player -> {
|
||||
MinecraftServer server = player.getServer();
|
||||
if (server != null) {
|
||||
server.getPlayerList().sendPlayerPermissionLevel(player);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private final class SendBuffer extends BufferedRequest<Void> {
|
||||
private final UUID uniqueId;
|
||||
|
||||
SendBuffer(UUID uniqueId) {
|
||||
super(500, TimeUnit.MILLISECONDS, NeoForgeCommandListUpdater.this.plugin.getBootstrap().getScheduler());
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void perform() {
|
||||
sendUpdate(this.uniqueId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.neoforge.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.neoforge.NeoForgeSenderFactory;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapabilityImpl;
|
||||
import me.lucko.luckperms.neoforge.util.AsyncConfigurationTask;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.PacketListener;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ConfigurationTask;
|
||||
import net.minecraft.server.network.ServerConfigurationPacketListenerImpl;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.neoforged.bus.api.EventPriority;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||
import net.neoforged.neoforge.network.event.RegisterConfigurationTasksEvent;
|
||||
|
||||
public class NeoForgeConnectionListener extends AbstractConnectionListener {
|
||||
private static final ConfigurationTask.Type USER_LOGIN_TASK_TYPE = new ConfigurationTask.Type("luckperms:user_login");
|
||||
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
public NeoForgeConnectionListener(LPNeoForgePlugin plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onGatherLoginConfigurationTasks(RegisterConfigurationTasksEvent event) {
|
||||
PacketListener packetListener = event.getListener();
|
||||
if (!(packetListener instanceof ServerConfigurationPacketListenerImpl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameProfile gameProfile = ((ServerConfigurationPacketListenerImpl) packetListener).getOwner();
|
||||
if (gameProfile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String username = gameProfile.getName();
|
||||
UUID uniqueId = gameProfile.getId();
|
||||
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||
this.plugin.getLogger().info("Processing pre-login (sync phase) for " + uniqueId + " - " + username);
|
||||
}
|
||||
|
||||
event.register(new AsyncConfigurationTask(this.plugin, USER_LOGIN_TASK_TYPE, () -> CompletableFuture.runAsync(() -> {
|
||||
onPlayerNegotiationAsync(event.getListener().getConnection(), uniqueId, username);
|
||||
}, this.plugin.getBootstrap().getScheduler().async())));
|
||||
}
|
||||
|
||||
private void onPlayerNegotiationAsync(Connection connection, UUID uniqueId, String username) {
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||
this.plugin.getLogger().info("Processing pre-login (async phase) for " + uniqueId + " - " + username);
|
||||
}
|
||||
|
||||
/* 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(uniqueId, username);
|
||||
recordConnection(uniqueId);
|
||||
this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(uniqueId, username, user);
|
||||
} catch (Exception ex) {
|
||||
this.plugin.getLogger().severe("Exception occurred whilst loading data for " + uniqueId + " - " + username, ex);
|
||||
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) {
|
||||
Component component = TranslationManager.render(Message.LOADING_DATABASE_ERROR.build());
|
||||
connection.send(new ClientboundLoginDisconnectPacket(NeoForgeSenderFactory.toNativeText(component)));
|
||||
connection.disconnect(NeoForgeSenderFactory.toNativeText(component));
|
||||
this.plugin.getEventDispatcher().dispatchPlayerLoginProcess(uniqueId, username, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
ServerPlayer player = (ServerPlayer) event.getEntity();
|
||||
GameProfile profile = player.getGameProfile();
|
||||
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||
this.plugin.getLogger().info("Processing post-login for " + profile.getId() + " - " + profile.getName());
|
||||
}
|
||||
|
||||
User user = this.plugin.getUserManager().getIfLoaded(profile.getId());
|
||||
|
||||
if (user == null) {
|
||||
if (!getUniqueConnections().contains(profile.getId())) {
|
||||
this.plugin.getLogger().warn("User " + profile.getId() + " - " + profile.getName() +
|
||||
" doesn't have data pre-loaded, they have never been processed during pre-login in this session.");
|
||||
} else {
|
||||
this.plugin.getLogger().warn("User " + profile.getId() + " - " + profile.getName() +
|
||||
" doesn't currently have data pre-loaded, but they have been processed before in this session.");
|
||||
}
|
||||
|
||||
Component component = TranslationManager.render(Message.LOADING_STATE_ERROR.build(), player.getLanguage());
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.CANCEL_FAILED_LOGINS)) {
|
||||
player.connection.disconnect(NeoForgeSenderFactory.toNativeText(component));
|
||||
return;
|
||||
} else {
|
||||
player.sendSystemMessage(NeoForgeSenderFactory.toNativeText(component));
|
||||
}
|
||||
}
|
||||
|
||||
// initialise capability
|
||||
UserCapabilityImpl userCapability = UserCapabilityImpl.get(player);
|
||||
userCapability.initialise(user, player, this.plugin.getContextManager());
|
||||
this.plugin.getContextManager().signalContextUpdate(player);
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) {
|
||||
ServerPlayer player = (ServerPlayer) event.getEntity();
|
||||
handleDisconnect(player.getGameProfile().getId());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.neoforge.listeners;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.context.ParsedCommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import me.lucko.luckperms.neoforge.util.BrigadierInjector;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.server.players.ServerOpList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.neoforge.event.AddReloadListenerEvent;
|
||||
import net.neoforged.neoforge.event.CommandEvent;
|
||||
import net.neoforged.neoforge.event.server.ServerStartedEvent;
|
||||
|
||||
public class NeoForgePlatformListener {
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
public NeoForgePlatformListener(LPNeoForgePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onCommand(CommandEvent event) {
|
||||
CommandContextBuilder<CommandSourceStack> context = event.getParseResults().getContext();
|
||||
|
||||
if (!this.plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
|
||||
for (ParsedCommandNode<CommandSourceStack> node : context.getNodes()) {
|
||||
if (!(node.getNode() instanceof LiteralCommandNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = node.getNode().getName().toLowerCase(Locale.ROOT);
|
||||
if (name.equals("op") || name.equals("deop")) {
|
||||
Message.OP_DISABLED.send(this.plugin.getSenderFactory().wrap(context.getSource()));
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onAddReloadListener(AddReloadListenerEvent event) {
|
||||
Commands commands = event.getServerResources().getCommands();
|
||||
BrigadierInjector.inject(this.plugin, commands.getDispatcher());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onServerStarted(ServerStartedEvent event) {
|
||||
if (!this.plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
|
||||
ServerOpList ops = event.getServer().getPlayerList().getOps();
|
||||
ops.getEntries().clear();
|
||||
try {
|
||||
ops.save();
|
||||
} catch (IOException ex) {
|
||||
this.plugin.getLogger().severe("Encountered an error while saving ops", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.neoforge.messaging;
|
||||
|
||||
import me.lucko.luckperms.common.messaging.InternalMessagingService;
|
||||
import me.lucko.luckperms.common.messaging.LuckPermsMessagingService;
|
||||
import me.lucko.luckperms.common.messaging.MessagingFactory;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import net.luckperms.api.messenger.IncomingMessageConsumer;
|
||||
import net.luckperms.api.messenger.Messenger;
|
||||
import net.luckperms.api.messenger.MessengerProvider;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
public class NeoForgeMessagingFactory extends MessagingFactory<LPNeoForgePlugin> {
|
||||
public NeoForgeMessagingFactory(LPNeoForgePlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InternalMessagingService getServiceFor(String messagingType) {
|
||||
if (messagingType.equals("pluginmsg") || messagingType.equals("bungee") || messagingType.equals("velocity")) {
|
||||
try {
|
||||
return new LuckPermsMessagingService(getPlugin(), new PluginMessageMessengerProvider());
|
||||
} catch (Exception e) {
|
||||
getPlugin().getLogger().severe("Exception occurred whilst enabling messaging", e);
|
||||
}
|
||||
}
|
||||
|
||||
return super.getServiceFor(messagingType);
|
||||
}
|
||||
|
||||
private class PluginMessageMessengerProvider implements MessengerProvider {
|
||||
|
||||
@Override
|
||||
public @NonNull String getName() {
|
||||
return "PluginMessage";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Messenger obtain(@NonNull IncomingMessageConsumer incomingMessageConsumer) {
|
||||
PluginMessageMessenger messenger = new PluginMessageMessenger(getPlugin(), incomingMessageConsumer);
|
||||
messenger.init();
|
||||
return messenger;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.neoforge.messaging;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger;
|
||||
import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import net.luckperms.api.messenger.IncomingMessageConsumer;
|
||||
import net.luckperms.api.messenger.Messenger;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.neoforge.network.PacketDistributor;
|
||||
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
|
||||
import net.neoforged.neoforge.network.registration.HandlerThread;
|
||||
|
||||
public class PluginMessageMessenger extends AbstractPluginMessageMessenger implements Messenger {
|
||||
private static final ResourceLocation CHANNEL_ID = ResourceLocation.parse(AbstractPluginMessageMessenger.CHANNEL);
|
||||
private static final CustomPacketPayload.Type<MessageWrapper> PAYLOAD_TYPE = new CustomPacketPayload.Type<>(CHANNEL_ID);
|
||||
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
public PluginMessageMessenger(LPNeoForgePlugin plugin, IncomingMessageConsumer consumer) {
|
||||
super(consumer);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
private void register(final RegisterPayloadHandlersEvent event) {
|
||||
event.registrar("1").executesOn(HandlerThread.NETWORK).commonBidirectional(
|
||||
PAYLOAD_TYPE,
|
||||
StreamCodec.of(
|
||||
(bytebuf, wrapper) -> bytebuf.writeBytes(wrapper.bytes),
|
||||
buf -> {
|
||||
byte[] bytes = new byte[buf.readableBytes()];
|
||||
return new MessageWrapper(bytes);
|
||||
}
|
||||
),
|
||||
(payload, context) -> handleIncomingMessage(payload.bytes())
|
||||
);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
this.plugin.getBootstrap().registerListeners(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendOutgoingMessage(byte[] buf) {
|
||||
AtomicReference<SchedulerTask> taskRef = new AtomicReference<>();
|
||||
SchedulerTask task = this.plugin.getBootstrap().getScheduler().asyncRepeating(() -> {
|
||||
ServerPlayer player = this.plugin.getBootstrap().getServer()
|
||||
.map(MinecraftServer::getPlayerList)
|
||||
.map(PlayerList::getPlayers)
|
||||
.map(players -> Iterables.getFirst(players, null))
|
||||
.orElse(null);
|
||||
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PacketDistributor.sendToPlayer(player, new MessageWrapper(buf));
|
||||
|
||||
SchedulerTask t = taskRef.getAndSet(null);
|
||||
if (t != null) {
|
||||
t.cancel();
|
||||
}
|
||||
}, 10, TimeUnit.SECONDS);
|
||||
taskRef.set(task);
|
||||
}
|
||||
|
||||
public static void registerChannel() {
|
||||
// do nothing - the channels are registered in the static initializer, we just
|
||||
// need to make sure that is called (which it will be if this method runs)
|
||||
}
|
||||
|
||||
public record MessageWrapper(byte[] bytes) implements CustomPacketPayload {
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return PAYLOAD_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.neoforge.service;
|
||||
|
||||
import me.lucko.luckperms.common.cacheddata.type.MetaCache;
|
||||
import me.lucko.luckperms.common.cacheddata.type.PermissionCache;
|
||||
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.verbose.event.CheckOrigin;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgeBootstrap;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapabilityImpl;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import net.luckperms.api.query.QueryMode;
|
||||
import net.luckperms.api.query.QueryOptions;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.neoforged.neoforge.server.permission.handler.IPermissionHandler;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionDynamicContext;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionType;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
|
||||
|
||||
public class NeoForgePermissionHandler implements IPermissionHandler {
|
||||
public static final ResourceLocation IDENTIFIER = ResourceLocation.fromNamespaceAndPath(LPNeoForgeBootstrap.ID, "permission_handler");
|
||||
|
||||
private final LPNeoForgePlugin plugin;
|
||||
private final Set<PermissionNode<?>> permissionNodes;
|
||||
|
||||
public NeoForgePermissionHandler(LPNeoForgePlugin plugin, Collection<PermissionNode<?>> permissionNodes) {
|
||||
this.plugin = plugin;
|
||||
this.permissionNodes = Collections.unmodifiableSet(new HashSet<>(permissionNodes));
|
||||
|
||||
for (PermissionNode<?> node : this.permissionNodes) {
|
||||
this.plugin.getPermissionRegistry().insert(node.getNodeName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getIdentifier() {
|
||||
return IDENTIFIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PermissionNode<?>> getRegisteredNodes() {
|
||||
return this.permissionNodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getPermission(ServerPlayer player, PermissionNode<T> node, PermissionDynamicContext<?>... context) {
|
||||
UserCapabilityImpl capability = UserCapabilityImpl.getNullable(player);
|
||||
|
||||
if (capability != null) {
|
||||
User user = capability.getUser();
|
||||
QueryOptions queryOptions = capability.getQueryOptionsCache().getQueryOptions();
|
||||
|
||||
T value = getPermissionValue(user, queryOptions, node, context);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return node.getDefaultResolver().resolve(player, player.getUUID(), context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getOfflinePermission(UUID player, PermissionNode<T> node, PermissionDynamicContext<?>... context) {
|
||||
User user = this.plugin.getUserManager().getIfLoaded(player);
|
||||
|
||||
if (user != null) {
|
||||
QueryOptions queryOptions = user.getQueryOptions();
|
||||
T value = getPermissionValue(user, queryOptions, node, context);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return node.getDefaultResolver().resolve(null, player, context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getPermissionValue(User user, QueryOptions queryOptions, PermissionNode<T> node, PermissionDynamicContext<?>... context) {
|
||||
queryOptions = appendContextToQueryOptions(queryOptions, context);
|
||||
String key = node.getNodeName();
|
||||
PermissionType<T> type = node.getType();
|
||||
|
||||
// permission check
|
||||
if (type == PermissionTypes.BOOLEAN) {
|
||||
PermissionCache cache = user.getCachedData().getPermissionData(queryOptions);
|
||||
Tristate value = cache.checkPermission(key, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result();
|
||||
if (value != Tristate.UNDEFINED) {
|
||||
return (T) (Boolean) value.asBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
// meta lookup
|
||||
if (node.getType() == PermissionTypes.STRING) {
|
||||
MetaCache cache = user.getCachedData().getMetaData(queryOptions);
|
||||
String value = cache.getMetaOrChatMetaValue(node.getNodeName(), CheckOrigin.PLATFORM_API);
|
||||
if (value != null) {
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
|
||||
// meta lookup (integer)
|
||||
if (node.getType() == PermissionTypes.INTEGER) {
|
||||
MetaCache cache = user.getCachedData().getMetaData(queryOptions);
|
||||
String value = cache.getMetaOrChatMetaValue(node.getNodeName(), CheckOrigin.PLATFORM_API);
|
||||
if (value != null) {
|
||||
try {
|
||||
return (T) Integer.valueOf(Integer.parseInt(value));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static QueryOptions appendContextToQueryOptions(QueryOptions queryOptions, PermissionDynamicContext<?>... context) {
|
||||
if (context.length == 0 || queryOptions.mode() != QueryMode.CONTEXTUAL) {
|
||||
return queryOptions;
|
||||
}
|
||||
|
||||
ImmutableContextSet.Builder contextBuilder = new ImmutableContextSetImpl.BuilderImpl()
|
||||
.addAll(queryOptions.context());
|
||||
|
||||
for (PermissionDynamicContext<?> dynamicContext : context) {
|
||||
contextBuilder.add(dynamicContext.getDynamic().name(), dynamicContext.getSerializedValue());
|
||||
}
|
||||
|
||||
return queryOptions.toBuilder().context(contextBuilder.build()).build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.neoforge.service;
|
||||
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.neoforge.common.ModConfigSpec;
|
||||
import net.neoforged.neoforge.common.NeoForgeConfig;
|
||||
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import net.neoforged.neoforge.server.permission.handler.DefaultPermissionHandler;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
|
||||
|
||||
public class NeoForgePermissionHandlerListener {
|
||||
private final LPNeoForgePlugin plugin;
|
||||
|
||||
public NeoForgePermissionHandlerListener(LPNeoForgePlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPermissionGatherHandler(PermissionGatherEvent.Handler event) {
|
||||
// Override the default permission handler with LuckPerms
|
||||
ModConfigSpec.ConfigValue<String> permissionHandler = NeoForgeConfig.SERVER.permissionHandler;
|
||||
if (permissionHandler.get().equals(DefaultPermissionHandler.IDENTIFIER.toString())) {
|
||||
permissionHandler.set(NeoForgePermissionHandler.IDENTIFIER.toString());
|
||||
}
|
||||
|
||||
event.addPermissionHandler(NeoForgePermissionHandler.IDENTIFIER, permissions -> new NeoForgePermissionHandler(this.plugin, permissions));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPermissionGatherNodes(PermissionGatherEvent.Nodes event) {
|
||||
// register luckperms nodes
|
||||
for (CommandPermission permission : CommandPermission.values()) {
|
||||
event.addNodes(new PermissionNode<>("luckperms", permission.getNode(), PermissionTypes.BOOLEAN, (player, uuid, context) -> false));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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.neoforge.util;
|
||||
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.server.network.ConfigurationTask;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class AsyncConfigurationTask implements ConfigurationTask {
|
||||
private final LPNeoForgePlugin plugin;
|
||||
private final Type type;
|
||||
private final Supplier<CompletableFuture<?>> task;
|
||||
|
||||
public AsyncConfigurationTask(LPNeoForgePlugin plugin, Type type, Supplier<CompletableFuture<?>> task) {
|
||||
this.plugin = plugin;
|
||||
this.type = type;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Consumer<Packet<?>> send) {
|
||||
CompletableFuture<?> future = this.task.get();
|
||||
future.whenCompleteAsync((o, e) -> {
|
||||
if (e != null) {
|
||||
this.plugin.getLogger().warn("Configuration task threw an exception", e);
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type type() {
|
||||
return this.type;
|
||||
}
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.neoforge.util;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import me.lucko.luckperms.common.graph.Graph;
|
||||
import me.lucko.luckperms.common.graph.TraversalAlgorithm;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.neoforge.LPNeoForgePlugin;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapability;
|
||||
import me.lucko.luckperms.neoforge.capabilities.UserCapabilityImpl;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Utility for injecting permission requirements into a Brigadier command tree.
|
||||
*/
|
||||
public final class BrigadierInjector {
|
||||
private BrigadierInjector() {}
|
||||
|
||||
private static final Field REQUIREMENT_FIELD;
|
||||
|
||||
static {
|
||||
Field requirementField;
|
||||
try {
|
||||
requirementField = CommandNode.class.getDeclaredField("requirement");
|
||||
requirementField.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
REQUIREMENT_FIELD = requirementField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject permission requirements into the commands in the given dispatcher.
|
||||
*
|
||||
* @param plugin the plugin
|
||||
* @param dispatcher the command dispatcher
|
||||
*/
|
||||
public static void inject(LPNeoForgePlugin plugin, CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
Iterable<CommandNodeWithParent> tree = CommandNodeGraph.INSTANCE.traverse(
|
||||
TraversalAlgorithm.DEPTH_FIRST_PRE_ORDER,
|
||||
new CommandNodeWithParent(null, dispatcher.getRoot())
|
||||
);
|
||||
|
||||
for (CommandNodeWithParent node : tree) {
|
||||
Predicate<CommandSourceStack> requirement = node.node.getRequirement();
|
||||
|
||||
// already injected - skip
|
||||
if (requirement instanceof InjectedPermissionRequirement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String permission = buildPermissionNode(node);
|
||||
if (permission == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
plugin.getPermissionRegistry().insert(permission);
|
||||
|
||||
InjectedPermissionRequirement newRequirement = new InjectedPermissionRequirement(plugin, permission, requirement);
|
||||
try {
|
||||
REQUIREMENT_FIELD.set(node.node, newRequirement);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildPermissionNode(CommandNodeWithParent node) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
while (node != null) {
|
||||
if (node.node instanceof LiteralCommandNode) {
|
||||
if (builder.length() != 0) {
|
||||
builder.insert(0, '.');
|
||||
}
|
||||
|
||||
String name = node.node.getName().toLowerCase(Locale.ROOT);
|
||||
builder.insert(0, name);
|
||||
}
|
||||
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (builder.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
builder.insert(0, "command.");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injected {@link CommandNode#getRequirement() requirement} that checks for a permission, before
|
||||
* delegating to the existing requirement.
|
||||
*/
|
||||
private static final class InjectedPermissionRequirement implements Predicate<CommandSourceStack> {
|
||||
private final LPNeoForgePlugin plugin;
|
||||
private final String permission;
|
||||
private final Predicate<CommandSourceStack> delegate;
|
||||
|
||||
private InjectedPermissionRequirement(LPNeoForgePlugin plugin, String permission, Predicate<CommandSourceStack> delegate) {
|
||||
this.plugin = plugin;
|
||||
this.permission = permission;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(CommandSourceStack source) {
|
||||
if (source.getEntity() instanceof ServerPlayer) {
|
||||
ServerPlayer player = (ServerPlayer) source.getEntity();
|
||||
Tristate state = Tristate.UNDEFINED;
|
||||
// If player is still connecting and has not been added to world then check LP user directly
|
||||
if (!player.isAddedToLevel()) {
|
||||
User user = this.plugin.getUserManager().getIfLoaded(player.getUUID());
|
||||
if (user == null) {
|
||||
// Should never happen but just in case...
|
||||
return false;
|
||||
}
|
||||
state = user.getCachedData().getPermissionData().checkPermission(permission);
|
||||
} else {
|
||||
UserCapability user = UserCapabilityImpl.get(player);
|
||||
state = user.checkPermission(this.permission);
|
||||
}
|
||||
|
||||
if (state != Tristate.UNDEFINED) {
|
||||
return state.asBoolean() && this.delegate.test(source.withPermission(4));
|
||||
}
|
||||
}
|
||||
|
||||
return this.delegate.test(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Graph} to represent the brigadier command node tree.
|
||||
*/
|
||||
private enum CommandNodeGraph implements Graph<CommandNodeWithParent> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Iterable<? extends CommandNodeWithParent> successors(CommandNodeWithParent ctx) {
|
||||
CommandNode<CommandSourceStack> node = ctx.node;
|
||||
Collection<CommandNodeWithParent> successors = new ArrayList<>();
|
||||
|
||||
for (CommandNode<CommandSourceStack> child : node.getChildren()) {
|
||||
successors.add(new CommandNodeWithParent(ctx, child));
|
||||
}
|
||||
|
||||
return successors;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CommandNodeWithParent {
|
||||
private final CommandNodeWithParent parent;
|
||||
private final CommandNode<CommandSourceStack> node;
|
||||
|
||||
private CommandNodeWithParent(CommandNodeWithParent parent, CommandNode<CommandSourceStack> node) {
|
||||
this.parent = parent;
|
||||
this.node = node;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.neoforge.util;
|
||||
|
||||
import me.lucko.luckperms.common.loader.JarInJarClassLoader;
|
||||
import net.neoforged.bus.api.Event;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.ModLoadingContext;
|
||||
import net.neoforged.fml.event.IModBusEvent;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.LambdaMetafactory;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
|
||||
/**
|
||||
* A utility for registering Forge listeners for methods in a jar-in-jar.
|
||||
*
|
||||
* <p>This differs from {@link IEventBus#register} as reflection is used for invoking the registered listeners
|
||||
* instead of ASM, which is incompatible with {@link JarInJarClassLoader}</p>
|
||||
*/
|
||||
public class NeoForgeEventBusFacade {
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
private final List<ListenerRegistration> listeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Register listeners for all methods annotated with {@link SubscribeEvent} on the target object.
|
||||
*/
|
||||
public void register(Object target) {
|
||||
for (Method method : target.getClass().getMethods()) {
|
||||
// Ignore static methods, Support for these could be added, but they are not used in LuckPerms
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Methods require a SubscribeEvent annotation in order to be registered
|
||||
SubscribeEvent subscribeEvent = method.getAnnotation(SubscribeEvent.class);
|
||||
if (subscribeEvent == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EventType type = determineListenerType(method);
|
||||
Consumer<?> invoker = createInvokerFunction(method, target, type);
|
||||
|
||||
// Determine the 'IEventBus' that this eventType should be registered to.
|
||||
IEventBus eventBus;
|
||||
if (IModBusEvent.class.isAssignableFrom(type.eventType)) {
|
||||
eventBus = ModLoadingContext.get().getActiveContainer().getEventBus();
|
||||
} else {
|
||||
eventBus = NeoForge.EVENT_BUS;
|
||||
}
|
||||
|
||||
addListener(eventBus, subscribeEvent, type.eventType, invoker);
|
||||
|
||||
this.listeners.add(new ListenerRegistration(invoker, eventBus, target));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister previously registered listeners on the target object.
|
||||
*
|
||||
* @param target the target listener
|
||||
*/
|
||||
public void unregister(Object target) {
|
||||
this.listeners.removeIf(listener -> {
|
||||
if (listener.target == target) {
|
||||
listener.close();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all listeners created through this interface.
|
||||
*/
|
||||
public void unregisterAll() {
|
||||
for (ListenerRegistration listener : this.listeners) {
|
||||
listener.close();
|
||||
}
|
||||
this.listeners.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener registration.
|
||||
*/
|
||||
private static final class ListenerRegistration implements AutoCloseable {
|
||||
/** The lambda invoker function */
|
||||
private final Consumer<?> invoker;
|
||||
/** The event bus that the invoker was registered to */
|
||||
private final IEventBus eventBus;
|
||||
/** The target listener class */
|
||||
private final Object target;
|
||||
|
||||
private ListenerRegistration(Consumer<?> invoker, IEventBus eventBus, Object target) {
|
||||
this.invoker = invoker;
|
||||
this.eventBus = eventBus;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.eventBus.unregister(this.invoker);
|
||||
}
|
||||
}
|
||||
|
||||
private static Consumer<?> createInvokerFunction(Method method, Object target, EventType type) {
|
||||
// Use the 'LambdaMetafactory' to generate a consumer which can be passed directly to an 'IEventBus'
|
||||
// when registering a listener, this reduces the overhead involved when reflectively invoking methods.
|
||||
try {
|
||||
MethodHandle methodHandle = LOOKUP.unreflect(method);
|
||||
CallSite callSite = LambdaMetafactory.metafactory(
|
||||
LOOKUP,
|
||||
"accept",
|
||||
MethodType.methodType(Consumer.class, target.getClass()),
|
||||
MethodType.methodType(void.class, Object.class),
|
||||
methodHandle,
|
||||
MethodType.methodType(void.class, type.eventType)
|
||||
);
|
||||
|
||||
return (Consumer<?>) callSite.getTarget().bindTo(target).invokeExact();
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Error whilst registering " + method, t);
|
||||
}
|
||||
}
|
||||
|
||||
public static EventType determineListenerType(Method method) {
|
||||
// Get the parameter types, this includes generic information which is required for GenericEvent
|
||||
Type[] parameterTypes = method.getGenericParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
throw new IllegalArgumentException(""
|
||||
+ "Method " + method + " has @SubscribeEvent annotation. "
|
||||
+ "It has " + parameterTypes.length + " arguments, "
|
||||
+ "but event handler methods require a single argument only."
|
||||
);
|
||||
}
|
||||
|
||||
Type parameterType = parameterTypes[0];
|
||||
Class<?> eventType;
|
||||
Class<?> genericType;
|
||||
|
||||
if (parameterType instanceof Class) { // Non-generic event
|
||||
eventType = (Class<?>) parameterType;
|
||||
genericType = null;
|
||||
} else if (parameterType instanceof ParameterizedType) { // Generic event
|
||||
ParameterizedType parameterizedType = (ParameterizedType) parameterType;
|
||||
|
||||
// Get the event class
|
||||
Type rawType = parameterizedType.getRawType();
|
||||
if (rawType instanceof Class) {
|
||||
eventType = (Class<?>) rawType;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Raw Type " + rawType.getClass() + " is not supported");
|
||||
}
|
||||
|
||||
// Find the type of 'T' in 'GenericEvent<T>'
|
||||
Type[] typeArguments = parameterizedType.getActualTypeArguments();
|
||||
if (typeArguments.length != 1) {
|
||||
throw new IllegalArgumentException(""
|
||||
+ "Method " + method + " has @SubscribeEvent annotation. "
|
||||
+ "It has a " + eventType + " argument, "
|
||||
+ "but generic events require a single type argument only."
|
||||
);
|
||||
}
|
||||
|
||||
// Get the generic class
|
||||
Type typeArgument = typeArguments[0];
|
||||
if (typeArgument instanceof Class<?>) {
|
||||
genericType = (Class<?>) typeArgument;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Type Argument " + typeArgument.getClass() + " is not supported");
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Parameter Type " + parameterType.getClass() + " is not supported");
|
||||
}
|
||||
|
||||
// Ensure 'eventType' is a subclass of event
|
||||
if (!Event.class.isAssignableFrom(eventType)) {
|
||||
throw new IllegalArgumentException(""
|
||||
+ "Method " + method + " has @SubscribeEvent annotation, "
|
||||
+ "but takes an argument that is not an Event subtype: " + eventType
|
||||
);
|
||||
}
|
||||
|
||||
return new EventType(eventType, genericType);
|
||||
}
|
||||
|
||||
private static final class EventType {
|
||||
private final Class<?> eventType;
|
||||
private final Class<?> genericType;
|
||||
|
||||
private EventType(Class<?> eventType, Class<?> genericType) {
|
||||
this.eventType = eventType;
|
||||
this.genericType = genericType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles casting generics for {@link IEventBus#addListener}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Event> void addListener(IEventBus eventBus, SubscribeEvent annotation, Class<?> eventType, Consumer<?> consumer) {
|
||||
eventBus.addListener(annotation.priority(), annotation.receiveCanceled(), (Class<T>) eventType, (Consumer<T>) consumer);
|
||||
}
|
||||
|
||||
}
|
641
neoforge/src/main/resources/luckperms.conf
Normal file
641
neoforge/src/main/resources/luckperms.conf
Normal file
@ -0,0 +1,641 @@
|
||||
####################################################################################################
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | __ __ ___ __ __ | #
|
||||
# | | | | / ` |__/ |__) |__ |__) |\/| /__` | #
|
||||
# | |___ \__/ \__, | \ | |___ | \ | | .__/ | #
|
||||
# | | #
|
||||
# | https://luckperms.net | #
|
||||
# | | #
|
||||
# | WIKI: https://luckperms.net/wiki | #
|
||||
# | DISCORD: https://discord.gg/luckperms | #
|
||||
# | BUG REPORTS: https://github.com/LuckPerms/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 how frequently the pool will 'ping' a connection in order to prevent it
|
||||
# from being timed out by the database or network infrastructure, measured in milliseconds.
|
||||
# - The value should be less than maximum-lifetime and greater than 30000 (30 seconds).
|
||||
# - Setting the value to zero will disable the keepalive functionality.
|
||||
keepalive-time = 0
|
||||
|
||||
# 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.
|
||||
#
|
||||
# - This only applies for remote SQL storage types (MySQL, MariaDB, etc).
|
||||
# - Change this if you want to use different tables for different servers.
|
||||
table-prefix = "luckperms_"
|
||||
|
||||
# The prefix to use for all LuckPerms MongoDB collections.
|
||||
#
|
||||
# - This only applies for the MongoDB storage type.
|
||||
# - Change this if you want to use different collections for different servers. The default is no
|
||||
# prefix.
|
||||
mongodb-collection-prefix = ""
|
||||
|
||||
# The connection string URI to use to connect to the MongoDB instance.
|
||||
#
|
||||
# - When configured, this setting will override anything defined in the address, database,
|
||||
# username or password fields above.
|
||||
# - If you have a connection string that starts with 'mongodb://' or 'mongodb+srv://', enter it
|
||||
# below.
|
||||
# - For more information, please 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.
|
||||
# => rabbitmq Uses RabbitMQ pub-sub to push changes. Your server connection info must be
|
||||
# configured below.
|
||||
# => custom Uses a messaging service provided using the LuckPerms API.
|
||||
# => 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
|
||||
# Multiple Redis nodes can be specified in the same format as a string list under the name "addresses".
|
||||
redis {
|
||||
enabled = false
|
||||
address = "localhost"
|
||||
username = ""
|
||||
password = ""
|
||||
}
|
||||
|
||||
# Settings for RabbitMQ.
|
||||
# Port 5672 is used by default; set address to "host:port" if differs
|
||||
rabbitmq {
|
||||
enabled = false
|
||||
address = "localhost"
|
||||
vhost = "/"
|
||||
username = "guest"
|
||||
password = "guest"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | | #
|
||||
# | 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"
|
||||
|
||||
# LuckPerms has a number of built-in contexts. These can be disabled by adding the context key to
|
||||
# the list below.
|
||||
disabled-contexts = [
|
||||
# "world"
|
||||
]
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | 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 = false
|
||||
|
||||
# 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 | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
||||
# A list of context calculators which will be skipped when calculating contexts.
|
||||
#
|
||||
# - You can disable context calculators by either:
|
||||
# => specifying the Java class name used by the calculator (e.g. com.example.ExampleCalculator)
|
||||
# => specifying a sub-section of the Java package used by the calculator (e.g. com.example)
|
||||
disabled-context-calculators = []
|
||||
|
||||
# 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. | #
|
||||
# | | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | Server Operator (OP) settings | #
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
|
||||
# Controls whether server operators should exist at all.
|
||||
#
|
||||
# - When set to 'false', all players will be de-opped, and the /op and /deop commands will be
|
||||
# disabled. Note that vanilla features like the spawn-protection require an operator on the
|
||||
# server to work.
|
||||
enable-ops = true
|
||||
|
||||
# Enables or disables a special permission based system in LuckPerms for controlling OP status.
|
||||
#
|
||||
# - If set to true, any user with the permission "luckperms.autoop" will automatically be granted
|
||||
# server operator status. This permission can be inherited, or set on specific servers/worlds,
|
||||
# temporarily, etc.
|
||||
# - Additionally, setting this to true will force the "enable-ops" option above to false. All users
|
||||
# will be de-opped unless they have the permission node, and the op/deop commands will be
|
||||
# disabled.
|
||||
# - It is recommended that you use this option instead of assigning a single '*' permission.
|
||||
# - However, on Forge this setting can be used as a "pseudo" root wildcard, as many mods support
|
||||
# the operator system over permissions.
|
||||
auto-op = false
|
||||
|
||||
# +----------------------------------------------------------------------------------------------+ #
|
||||
# | 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 not require users to confirm bulkupdate operations.
|
||||
#
|
||||
# - When set to true, operations will be executed immediately.
|
||||
# - This is not recommended, as bulkupdate has the potential to irreversibly delete large amounts of
|
||||
# data, and is not designed to be executed automatically.
|
||||
# - If automation is needed, users should prefer using the LuckPerms API.
|
||||
skip-bulkupdate-confirmation = false
|
||||
|
||||
# If LuckPerms should prevent bulkupdate operations.
|
||||
#
|
||||
# - When set to true, bulkupdate operations (the /lp bulkupdate command) will not work.
|
||||
# - When set to false, bulkupdate operations will be allowed via the console.
|
||||
disable-bulkupdate = 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 update the list of commands sent to the client when permissions are changed.
|
||||
update-client-command-list = true
|
||||
|
||||
# If LuckPerms should attempt to resolve Vanilla command target selectors for LP commands.
|
||||
# See here for more info: https://minecraft.wiki/w/Target_selectors
|
||||
resolve-command-selectors = false
|
@ -1,5 +1,6 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
@ -8,7 +9,6 @@ pluginManagement {
|
||||
name = 'Forge'
|
||||
url = 'https://maven.minecraftforge.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,9 @@ include (
|
||||
'bungee',
|
||||
'bungee:loader',
|
||||
'fabric',
|
||||
'neoforge',
|
||||
'neoforge:loader',
|
||||
'neoforge:neoforge-api',
|
||||
'forge',
|
||||
'forge:loader',
|
||||
'forge:forge-api',
|
||||
|
Loading…
Reference in New Issue
Block a user