mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Cloud for commands (#3808)
Co-authored-by: onebeastchris <github@onechris.mozmail.com>
This commit is contained in:
parent
6002c9c7a1
commit
87ab51cb28
95 changed files with 2556 additions and 1879 deletions
|
|
@ -1,5 +1,7 @@
|
|||
dependencies {
|
||||
api(projects.core)
|
||||
|
||||
implementation(libs.cloud.bungee)
|
||||
implementation(libs.adventure.text.serializer.bungeecord)
|
||||
compileOnlyApi(libs.bungeecord.proxy)
|
||||
}
|
||||
|
|
@ -8,13 +10,15 @@ platformRelocate("net.md_5.bungee.jni")
|
|||
platformRelocate("com.fasterxml.jackson")
|
||||
platformRelocate("io.netty.channel.kqueue") // This is not used because relocating breaks natives, but we must include it or else we get ClassDefNotFound
|
||||
platformRelocate("net.kyori")
|
||||
platformRelocate("org.incendo")
|
||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
||||
platformRelocate("org.yaml") // Broken as of 1.20
|
||||
|
||||
// These dependencies are already present on the platform
|
||||
provided(libs.bungeecord.proxy)
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.bungeecord.GeyserBungeeMain"
|
||||
}
|
||||
|
||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.platform.bungeecord;
|
|||
|
||||
import io.netty.channel.Channel;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
|
|
@ -34,17 +35,20 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.GeyserBungeeCommandExecutor;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.bungee.BungeeCommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
|
@ -54,21 +58,22 @@ import java.net.SocketAddress;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserBungeeConfiguration geyserConfig;
|
||||
private GeyserBungeeInjector geyserInjector;
|
||||
private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
|
||||
private IGeyserPingPassthrough geyserBungeePingPassthrough;
|
||||
|
||||
private GeyserImpl geyser;
|
||||
|
||||
// We can't disable the plugin; hence we need to keep track of it manually
|
||||
private boolean disabled;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
onGeyserInitialize();
|
||||
|
|
@ -93,16 +98,23 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
if (!this.loadConfig()) {
|
||||
disabled = true;
|
||||
return;
|
||||
}
|
||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||
|
||||
// Registration of listeners occurs only once
|
||||
this.getProxy().getPluginManager().registerListener(this, new GeyserBungeeUpdateListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (disabled) {
|
||||
return; // Config did not load properly!
|
||||
}
|
||||
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
|
||||
// task that waits for a field to be filled which is set after the plugin enable
|
||||
// process is complete
|
||||
|
|
@ -143,10 +155,18 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
} else {
|
||||
// For consistency with other platforms - create command manager before GeyserImpl#start()
|
||||
// This ensures the command events are called before the item/block ones are
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSender.class,
|
||||
id -> getProxy().getPlayer(id),
|
||||
() -> getProxy().getConsole(),
|
||||
BungeeCommandSource::new
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new BungeeCommandManager<>(
|
||||
this,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults
|
||||
}
|
||||
|
||||
// Force-disable query if enabled, or else Geyser won't enable
|
||||
|
|
@ -181,16 +201,6 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
this.geyserInjector.initializeLocalChannel(this);
|
||||
|
||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
|
||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
Map<String, Command> commands = entry.getValue();
|
||||
if (commands.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -226,8 +236,8 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return this.geyserCommandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return this.commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.Permissions;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ public final class GeyserBungeeUpdateListener implements Listener {
|
|||
public void onPlayerJoin(final PostLoginEvent event) {
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
final ProxiedPlayer player = event.getPlayer();
|
||||
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
|
||||
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,19 +27,22 @@ package org.geysermc.geyser.platform.bungeecord.command;
|
|||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeeCommandSource implements GeyserCommandSource {
|
||||
|
||||
private final net.md_5.bungee.api.CommandSender handle;
|
||||
private final CommandSender handle;
|
||||
|
||||
public BungeeCommandSource(net.md_5.bungee.api.CommandSender handle) {
|
||||
public BungeeCommandSource(CommandSender handle) {
|
||||
this.handle = handle;
|
||||
// Ensure even Java players' languages are loaded
|
||||
GeyserLocale.loadGeyserLocale(this.locale());
|
||||
|
|
@ -72,12 +75,20 @@ public class BungeeCommandSource implements GeyserCommandSource {
|
|||
return !(handle instanceof ProxiedPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID playerUuid() {
|
||||
if (handle instanceof ProxiedPlayer player) {
|
||||
return player.getUniqueId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String locale() {
|
||||
if (handle instanceof ProxiedPlayer player) {
|
||||
Locale locale = player.getLocale();
|
||||
if (locale != null) {
|
||||
// Locale can be null early on in the conneciton
|
||||
// Locale can be null early on in the connection
|
||||
return GeyserLocale.formatLocale(locale.getLanguage() + "_" + locale.getCountry());
|
||||
}
|
||||
}
|
||||
|
|
@ -86,6 +97,12 @@ public class BungeeCommandSource implements GeyserCommandSource {
|
|||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return handle.hasPermission(permission);
|
||||
// Handle blank permissions ourselves, as bungeecord only handles empty ones
|
||||
return permission.isBlank() || handle.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.bungeecord.command;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class GeyserBungeeCommandExecutor extends Command implements TabExecutor {
|
||||
private final GeyserCommandExecutor commandExecutor;
|
||||
|
||||
public GeyserBungeeCommandExecutor(String name, GeyserImpl geyser, Map<String, org.geysermc.geyser.api.command.Command> commands) {
|
||||
super(name);
|
||||
|
||||
this.commandExecutor = new GeyserCommandExecutor(geyser, commands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
BungeeCommandSource commandSender = new BungeeCommandSource(sender);
|
||||
GeyserSession session = this.commandExecutor.getGeyserSession(commandSender);
|
||||
|
||||
if (args.length > 0) {
|
||||
GeyserCommand command = this.commandExecutor.getCommand(args[0]);
|
||||
if (command != null) {
|
||||
if (!sender.hasPermission(command.permission())) {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.locale());
|
||||
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
return;
|
||||
}
|
||||
if (command.isBedrockOnly() && session == null) {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.locale());
|
||||
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
return;
|
||||
}
|
||||
command.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
|
||||
} else {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.not_found", commandSender.locale());
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
}
|
||||
} else {
|
||||
this.commandExecutor.getCommand("help").execute(session, commandSender, new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return commandExecutor.tabComplete(new BungeeCommandSource(sender));
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
plugins {
|
||||
application
|
||||
}
|
||||
|
||||
architectury {
|
||||
platformSetupLoomIde()
|
||||
fabric()
|
||||
|
|
@ -35,13 +31,12 @@ dependencies {
|
|||
shadow(projects.api) { isTransitive = false }
|
||||
shadow(projects.common) { isTransitive = false }
|
||||
|
||||
// Permissions
|
||||
modImplementation(libs.fabric.permissions)
|
||||
include(libs.fabric.permissions)
|
||||
modImplementation(libs.cloud.fabric)
|
||||
include(libs.cloud.fabric)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.fabric.GeyserFabricMain"
|
||||
}
|
||||
|
||||
relocate("org.cloudburstmc.netty")
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
package org.geysermc.geyser.platform.fabric;
|
||||
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||
|
|
@ -34,9 +33,16 @@ import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
|||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.fabric.FabricServerCommandManager;
|
||||
|
||||
public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInitializer {
|
||||
|
||||
|
|
@ -70,20 +76,23 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit
|
|||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserModUpdateListener.onPlayReady(handler.getPlayer()));
|
||||
|
||||
this.onGeyserInitialize();
|
||||
|
||||
var sourceConverter = CommandSourceConverter.layered(
|
||||
CommandSourceStack.class,
|
||||
id -> getServer().getPlayerList().getPlayer(id),
|
||||
Player::createCommandSourceStack,
|
||||
() -> getServer().createCommandSourceStack(), // NPE if method reference is used, since server is not available yet
|
||||
ModCommandSource::new
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new FabricServerCommandManager<>(
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
this.setCommandRegistry(new CommandRegistry(GeyserImpl.getInstance(), cloud, false)); // applying root permission would be a breaking change because we can't register permission defaults
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServer() {
|
||||
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
|
||||
return Permissions.check(source, permissionNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
|
||||
return Permissions.check(source, permissionNode, permissionLevel);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
plugins {
|
||||
application
|
||||
}
|
||||
|
||||
// This is provided by "org.cloudburstmc.math.mutable" too, so yeet.
|
||||
// NeoForge's class loader is *really* annoying.
|
||||
provided("org.cloudburstmc.math", "api")
|
||||
|
|
@ -38,10 +34,13 @@ dependencies {
|
|||
|
||||
// Include all transitive deps of core via JiJ
|
||||
includeTransitive(projects.core)
|
||||
|
||||
modImplementation(libs.cloud.neoforge)
|
||||
include(libs.cloud.neoforge)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.forge.GeyserNeoForgeMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.neoforge.GeyserNeoForgeMain"
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.platform.neoforge;
|
|||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.neoforged.bus.api.EventPriority;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.loading.FMLLoader;
|
||||
|
|
@ -35,15 +36,22 @@ import net.neoforged.neoforge.event.GameShuttingDownEvent;
|
|||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||
import net.neoforged.neoforge.event.server.ServerStartedEvent;
|
||||
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.neoforge.NeoForgeServerCommandManager;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Mod(ModConstants.MOD_ID)
|
||||
public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
||||
|
||||
private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler();
|
||||
|
||||
public GeyserNeoForgeBootstrap(ModContainer container) {
|
||||
super(new GeyserNeoForgePlatform(container));
|
||||
|
||||
|
|
@ -56,9 +64,25 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
|||
|
||||
NeoForge.EVENT_BUS.addListener(this::onServerStopping);
|
||||
NeoForge.EVENT_BUS.addListener(this::onPlayerJoin);
|
||||
NeoForge.EVENT_BUS.addListener(this.permissionHandler::onPermissionGather);
|
||||
|
||||
NeoForge.EVENT_BUS.addListener(EventPriority.HIGHEST, this::onPermissionGather);
|
||||
|
||||
this.onGeyserInitialize();
|
||||
|
||||
var sourceConverter = CommandSourceConverter.layered(
|
||||
CommandSourceStack.class,
|
||||
id -> getServer().getPlayerList().getPlayer(id),
|
||||
Player::createCommandSourceStack,
|
||||
() -> getServer().createCommandSourceStack(),
|
||||
ModCommandSource::new
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new NeoForgeServerCommandManager<>(
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
GeyserNeoForgeCommandRegistry registry = new GeyserNeoForgeCommandRegistry(getGeyser(), cloud);
|
||||
this.setCommandRegistry(registry);
|
||||
NeoForge.EVENT_BUS.addListener(EventPriority.LOWEST, registry::onPermissionGatherForUndefined);
|
||||
}
|
||||
|
||||
private void onServerStarted(ServerStartedEvent event) {
|
||||
|
|
@ -87,13 +111,17 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
|
|||
return FMLLoader.getDist().isDedicatedServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
|
||||
return this.permissionHandler.hasPermission(source, permissionNode);
|
||||
}
|
||||
private void onPermissionGather(PermissionGatherEvent.Nodes event) {
|
||||
getGeyser().eventBus().fire(
|
||||
(GeyserRegisterPermissionsEvent) (permission, defaultValue) -> {
|
||||
Objects.requireNonNull(permission, "permission");
|
||||
Objects.requireNonNull(defaultValue, "permission default for " + permission);
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
|
||||
return this.permissionHandler.hasPermission(source, permissionNode, permissionLevel);
|
||||
if (permission.isBlank()) {
|
||||
return;
|
||||
}
|
||||
PermissionUtils.register(permission, defaultValue, event);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.neoforge;
|
||||
|
||||
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.api.util.TriState;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.neoforge.PermissionNotRegisteredException;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class GeyserNeoForgeCommandRegistry extends CommandRegistry {
|
||||
|
||||
/**
|
||||
* Permissions with an undefined permission default. Use Set to not register the same fallback more than once.
|
||||
* NeoForge requires that all permissions are registered, and cloud-neoforge follows that.
|
||||
* This is unlike most platforms, on which we wouldn't register a permission if no default was provided.
|
||||
*/
|
||||
private final Set<String> undefinedPermissions = new HashSet<>();
|
||||
|
||||
public GeyserNeoForgeCommandRegistry(GeyserImpl geyser, CommandManager<GeyserCommandSource> cloud) {
|
||||
super(geyser, cloud);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void register(GeyserCommand command, Map<String, GeyserCommand> commands) {
|
||||
super.register(command, commands);
|
||||
|
||||
// FIRST STAGE: Collect all permissions that may have undefined defaults.
|
||||
if (!command.permission().isBlank() && command.permissionDefault() == null) {
|
||||
// Permission requirement exists but no default value specified.
|
||||
undefinedPermissions.add(command.permission());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRegisterPermissions(GeyserRegisterPermissionsEvent event) {
|
||||
super.onRegisterPermissions(event);
|
||||
|
||||
// SECOND STAGE
|
||||
// Now that we are aware of all commands, we can eliminate some incorrect assumptions.
|
||||
// Example: two commands may have the same permission, but only of them defines a permission default.
|
||||
undefinedPermissions.removeAll(permissionDefaults.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers permissions with possibly undefined defaults.
|
||||
* Should be subscribed late to allow extensions and mods to register a desired permission default first.
|
||||
*/
|
||||
void onPermissionGatherForUndefined(PermissionGatherEvent.Nodes event) {
|
||||
// THIRD STAGE
|
||||
for (String permission : undefinedPermissions) {
|
||||
if (PermissionUtils.register(permission, TriState.NOT_SET, event)) {
|
||||
// The permission was not already registered
|
||||
geyser.getLogger().debug("Registered permission " + permission + " with fallback default value of NOT_SET");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserCommandSource source, String permission) {
|
||||
// NeoForgeServerCommandManager will throw this exception if the permission is not registered to the server.
|
||||
// We can't realistically ensure that every permission is registered (calls by API users), so we catch this.
|
||||
// This works for our calls, but not for cloud's internal usage. For that case, see above.
|
||||
try {
|
||||
return super.hasPermission(source, permission);
|
||||
} catch (PermissionNotRegisteredException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.neoforge;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.neoforged.neoforge.server.permission.PermissionAPI;
|
||||
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionDynamicContextKey;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionType;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class GeyserNeoForgePermissionHandler {
|
||||
|
||||
private static final Constructor<?> PERMISSION_NODE_CONSTRUCTOR;
|
||||
|
||||
static {
|
||||
try {
|
||||
@SuppressWarnings("rawtypes")
|
||||
Constructor<PermissionNode> constructor = PermissionNode.class.getDeclaredConstructor(
|
||||
String.class,
|
||||
PermissionType.class,
|
||||
PermissionNode.PermissionResolver.class,
|
||||
PermissionDynamicContextKey[].class
|
||||
);
|
||||
constructor.setAccessible(true);
|
||||
PERMISSION_NODE_CONSTRUCTOR = constructor;
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Unable to construct PermissionNode!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, PermissionNode<Boolean>> permissionNodes = new HashMap<>();
|
||||
|
||||
public void onPermissionGather(PermissionGatherEvent.Nodes event) {
|
||||
this.registerNode(Constants.UPDATE_PERMISSION, event);
|
||||
|
||||
GeyserCommandManager commandManager = GeyserImpl.getInstance().commandManager();
|
||||
for (Map.Entry<String, Command> entry : commandManager.commands().entrySet()) {
|
||||
Command command = entry.getValue();
|
||||
|
||||
// Don't register aliases
|
||||
if (!command.name().equals(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.registerNode(command.permission(), event);
|
||||
}
|
||||
|
||||
for (Map<String, Command> commands : commandManager.extensionCommands().values()) {
|
||||
for (Map.Entry<String, Command> entry : commands.entrySet()) {
|
||||
Command command = entry.getValue();
|
||||
|
||||
// Don't register aliases
|
||||
if (!command.name().equals(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.registerNode(command.permission(), event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
|
||||
PermissionNode<Boolean> node = this.permissionNodes.get(permissionNode);
|
||||
if (node == null) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Unable to find permission node " + permissionNode);
|
||||
return false;
|
||||
}
|
||||
|
||||
return PermissionAPI.getPermission((ServerPlayer) source, node);
|
||||
}
|
||||
|
||||
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
|
||||
if (!source.isPlayer()) {
|
||||
return true;
|
||||
}
|
||||
assert source.getPlayer() != null;
|
||||
boolean permission = this.hasPermission(source.getPlayer(), permissionNode);
|
||||
if (!permission) {
|
||||
return source.getPlayer().hasPermissions(permissionLevel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void registerNode(String node, PermissionGatherEvent.Nodes event) {
|
||||
PermissionNode<Boolean> permissionNode = this.createNode(node);
|
||||
|
||||
// NeoForge likes to crash if you try and register a duplicate node
|
||||
if (!event.getNodes().contains(permissionNode)) {
|
||||
event.addNodes(permissionNode);
|
||||
this.permissionNodes.put(node, permissionNode);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private PermissionNode<Boolean> createNode(String node) {
|
||||
// The typical constructors in PermissionNode require a
|
||||
// mod id, which means our permission nodes end up becoming
|
||||
// geyser_neoforge.<node> instead of just <node>. We work around
|
||||
// this by using reflection to access the constructor that
|
||||
// doesn't require a mod id or ResourceLocation.
|
||||
try {
|
||||
return (PermissionNode<Boolean>) PERMISSION_NODE_CONSTRUCTOR.newInstance(
|
||||
node,
|
||||
PermissionTypes.BOOLEAN,
|
||||
(PermissionNode.PermissionResolver<Boolean>) (player, playerUUID, context) -> false,
|
||||
new PermissionDynamicContextKey[0]
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to create permission node " + node, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.neoforge;
|
||||
|
||||
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.api.util.TriState;
|
||||
import org.geysermc.geyser.platform.neoforge.mixin.PermissionNodeMixin;
|
||||
|
||||
/**
|
||||
* Common logic for handling the more complicated way we have to register permission on NeoForge
|
||||
*/
|
||||
public class PermissionUtils {
|
||||
|
||||
private PermissionUtils() {
|
||||
//no
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given permission and its default value to the event. If the permission has the same name as one
|
||||
* that has already been registered to the event, it will not be registered. In other words, it will not override.
|
||||
*
|
||||
* @param permission the permission to register
|
||||
* @param permissionDefault the permission's default value. See {@link GeyserRegisterPermissionsEvent#register(String, TriState)} for TriState meanings.
|
||||
* @param event the registration event
|
||||
* @return true if the permission was registered
|
||||
*/
|
||||
public static boolean register(String permission, TriState permissionDefault, PermissionGatherEvent.Nodes event) {
|
||||
// NeoForge likes to crash if you try and register a duplicate node
|
||||
if (event.getNodes().stream().noneMatch(n -> n.getNodeName().equals(permission))) {
|
||||
PermissionNode<Boolean> node = createNode(permission, permissionDefault);
|
||||
event.addNodes(node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static PermissionNode<Boolean> createNode(String node, TriState permissionDefault) {
|
||||
return PermissionNodeMixin.geyser$construct(
|
||||
node,
|
||||
PermissionTypes.BOOLEAN,
|
||||
(player, playerUUID, context) -> switch (permissionDefault) {
|
||||
case TRUE -> true;
|
||||
case FALSE -> false;
|
||||
case NOT_SET -> {
|
||||
if (player != null) {
|
||||
yield player.createCommandSourceStack().hasPermission(player.server.getOperatorUserPermissionLevel());
|
||||
}
|
||||
yield false; // NeoForge javadocs say player is null in the case of an offline player.
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.neoforge.mixin;
|
||||
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionDynamicContextKey;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
|
||||
import net.neoforged.neoforge.server.permission.nodes.PermissionType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(value = PermissionNode.class, remap = false) // this is API - do not remap
|
||||
public interface PermissionNodeMixin {
|
||||
|
||||
/**
|
||||
* Invokes the matching private constructor in {@link PermissionNode}.
|
||||
* <p>
|
||||
* The typical constructors in PermissionNode require a mod id, which means our permission nodes
|
||||
* would end up becoming {@code geyser_neoforge.<node>} instead of just {@code <node>}.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes") // the varargs
|
||||
@Invoker("<init>")
|
||||
static <T> PermissionNode<T> geyser$construct(String nodeName, PermissionType<T> type, PermissionNode.PermissionResolver<T> defaultResolver, PermissionDynamicContextKey... dynamics) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ authors="GeyserMC"
|
|||
description="${description}"
|
||||
[[mixins]]
|
||||
config = "geyser.mixins.json"
|
||||
[[mixins]]
|
||||
config = "geyser_neoforge.mixins.json"
|
||||
[[dependencies.geyser_neoforge]]
|
||||
modId="neoforge"
|
||||
type="required"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "org.geysermc.geyser.platform.neoforge.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"PermissionNodeMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
|
|
@ -25,30 +25,21 @@
|
|||
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.mod.command.GeyserModCommandExecutor;
|
||||
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
||||
import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
|
@ -59,7 +50,6 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
|
|
@ -70,13 +60,15 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||
|
||||
private final GeyserModPlatform platform;
|
||||
|
||||
@Getter
|
||||
private GeyserImpl geyser;
|
||||
private Path dataFolder;
|
||||
|
||||
@Setter
|
||||
@Setter @Getter
|
||||
private MinecraftServer server;
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
@Setter
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserModConfiguration geyserConfig;
|
||||
private GeyserModInjector geyserInjector;
|
||||
private final GeyserModLogger geyserLogger = new GeyserModLogger();
|
||||
|
|
@ -94,10 +86,6 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
|
||||
|
||||
// Create command manager here, since the permission handler on neo needs it
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
}
|
||||
|
||||
public void onGeyserEnable() {
|
||||
|
|
@ -130,50 +118,6 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||
if (isServer()) {
|
||||
this.geyserInjector.initializeLocalChannel(this);
|
||||
}
|
||||
|
||||
// Start command building
|
||||
// Set just "geyser" as the help command
|
||||
GeyserModCommandExecutor helpExecutor = new GeyserModCommandExecutor(geyser,
|
||||
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
|
||||
|
||||
// Register all subcommands as valid
|
||||
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
|
||||
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
||||
builder.then(Commands.literal(command.getKey())
|
||||
.executes(executor)
|
||||
// Could also test for Bedrock but depending on when this is called it may backfire
|
||||
.requires(executor::testPermission)
|
||||
// Allows parsing of arguments; e.g. for /geyser dump logs or the connectiontest command
|
||||
.then(Commands.argument("args", StringArgumentType.greedyString())
|
||||
.executes(context -> executor.runWithArgs(context, StringArgumentType.getString(context, "args")))
|
||||
.requires(executor::testPermission)));
|
||||
}
|
||||
server.getCommands().getDispatcher().register(builder);
|
||||
|
||||
// Register extension commands
|
||||
for (Map.Entry<Extension, Map<String, Command>> extensionMapEntry : geyser.commandManager().extensionCommands().entrySet()) {
|
||||
Map<String, Command> extensionCommands = extensionMapEntry.getValue();
|
||||
if (extensionCommands.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Register help command for just "/<extensionId>"
|
||||
GeyserModCommandExecutor extensionHelpExecutor = new GeyserModCommandExecutor(geyser,
|
||||
(GeyserCommand) extensionCommands.get("help"));
|
||||
LiteralArgumentBuilder<CommandSourceStack> extCmdBuilder = Commands.literal(extensionMapEntry.getKey().description().id()).executes(extensionHelpExecutor);
|
||||
|
||||
for (Map.Entry<String, Command> command : extensionCommands.entrySet()) {
|
||||
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
|
||||
extCmdBuilder.then(Commands.literal(command.getKey())
|
||||
.executes(executor)
|
||||
.requires(executor::testPermission)
|
||||
.then(Commands.argument("args", StringArgumentType.greedyString())
|
||||
.executes(context -> executor.runWithArgs(context, StringArgumentType.getString(context, "args")))
|
||||
.requires(executor::testPermission)));
|
||||
}
|
||||
server.getCommands().getDispatcher().register(extCmdBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -206,8 +150,8 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return geyserCommandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -235,6 +179,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||
return this.server.getServerVersion();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions") // Certain IDEA installations think that ip cannot be null
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
|
|
@ -270,10 +215,6 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||
return this.platform.resolveResource(resource);
|
||||
}
|
||||
|
||||
public abstract boolean hasPermission(@NonNull Player source, @NonNull String permissionNode);
|
||||
|
||||
public abstract boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel);
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean loadConfig() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -25,17 +25,18 @@
|
|||
|
||||
package org.geysermc.geyser.platform.mod;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSender;
|
||||
import org.geysermc.geyser.Permissions;
|
||||
import org.geysermc.geyser.platform.mod.command.ModCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
public final class GeyserModUpdateListener {
|
||||
public static void onPlayReady(Player player) {
|
||||
CommandSourceStack stack = player.createCommandSourceStack();
|
||||
if (GeyserModBootstrap.getInstance().hasPermission(stack, Constants.UPDATE_PERMISSION, 2)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new ModCommandSender(stack));
|
||||
// Should be creating this in the supplier, but we need it for the permission check.
|
||||
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
|
||||
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());
|
||||
if (source.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> source);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.mod.command;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class GeyserModCommandExecutor extends GeyserCommandExecutor implements Command<CommandSourceStack> {
|
||||
private final GeyserCommand command;
|
||||
|
||||
public GeyserModCommandExecutor(GeyserImpl geyser, GeyserCommand command) {
|
||||
super(geyser, Collections.singletonMap(command.name(), command));
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public boolean testPermission(CommandSourceStack source) {
|
||||
return GeyserModBootstrap.getInstance().hasPermission(source, command.permission(), command.isSuggestedOpOnly() ? 2 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int run(CommandContext<CommandSourceStack> context) {
|
||||
return runWithArgs(context, "");
|
||||
}
|
||||
|
||||
public int runWithArgs(CommandContext<CommandSourceStack> context, String args) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
ModCommandSender sender = new ModCommandSender(source);
|
||||
GeyserSession session = getGeyserSession(sender);
|
||||
if (!testPermission(source)) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command.isBedrockOnly() && session == null) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
command.execute(session, sender, args.split(" "));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -31,19 +31,21 @@ import net.minecraft.core.RegistryAccess;
|
|||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ModCommandSender implements GeyserCommandSource {
|
||||
public class ModCommandSource implements GeyserCommandSource {
|
||||
|
||||
private final CommandSourceStack source;
|
||||
|
||||
public ModCommandSender(CommandSourceStack source) {
|
||||
public ModCommandSource(CommandSourceStack source) {
|
||||
this.source = source;
|
||||
// todo find locale?
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -75,8 +77,24 @@ public class ModCommandSender implements GeyserCommandSource {
|
|||
return !(source.getEntity() instanceof ServerPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID playerUuid() {
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
return player.getUUID();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return GeyserModBootstrap.getInstance().hasPermission(source, permission, source.getServer().getOperatorUserPermissionLevel());
|
||||
// Unlike other bootstraps; we delegate to cloud here too:
|
||||
// On NeoForge; we'd have to keep track of all PermissionNodes - cloud already does that
|
||||
// For Fabric, we won't need to include the Fabric Permissions API anymore - cloud already does that too :p
|
||||
return GeyserImpl.getInstance().commandRegistry().hasPermission(this, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
|
|
@ -111,12 +110,6 @@ public class GeyserModWorldManager extends GeyserWorldManager {
|
|||
return SharedConstants.getCurrentVersion().getProtocolVersion() == GameProtocol.getJavaProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
ServerPlayer player = getPlayer(session);
|
||||
return GeyserModBootstrap.getInstance().hasPermission(player, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameMode getDefaultGameMode(GeyserSession session) {
|
||||
return GameMode.byId(server.getDefaultGameType().getId());
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ dependencies {
|
|||
classifier("all") // otherwise the unshaded jar is used without the shaded NMS implementations
|
||||
})
|
||||
|
||||
implementation(libs.cloud.paper)
|
||||
implementation(libs.commodore)
|
||||
|
||||
implementation(libs.adventure.text.serializer.bungeecord)
|
||||
|
||||
compileOnly(libs.folia.api)
|
||||
compileOnly(libs.paper.mojangapi)
|
||||
|
||||
compileOnlyApi(libs.viaversion)
|
||||
}
|
||||
|
|
@ -33,13 +33,15 @@ platformRelocate("com.fasterxml.jackson")
|
|||
platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger")
|
||||
platformRelocate("org.objectweb.asm")
|
||||
platformRelocate("me.lucko.commodore")
|
||||
platformRelocate("org.incendo")
|
||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
||||
platformRelocate("org.yaml") // Broken as of 1.20
|
||||
|
||||
// These dependencies are already present on the platform
|
||||
provided(libs.viaversion)
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.spigot.GeyserSpigotMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.spigot.GeyserSpigotMain"
|
||||
}
|
||||
|
||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
|
|
|
|||
|
|
@ -30,37 +30,34 @@ import com.viaversion.viaversion.api.data.MappingData;
|
|||
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import me.lucko.commodore.CommodoreProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.server.ServerLoadEvent;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.adapters.paper.PaperAdapters;
|
||||
import org.geysermc.geyser.adapters.spigot.SpigotAdapters;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserRegisterPermissionsEvent;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.spigot.command.GeyserBrigadierSupport;
|
||||
import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor;
|
||||
import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager;
|
||||
import org.geysermc.geyser.platform.spigot.command.SpigotCommandRegistry;
|
||||
import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource;
|
||||
import org.geysermc.geyser.platform.spigot.world.GeyserPistonListener;
|
||||
import org.geysermc.geyser.platform.spigot.world.GeyserSpigotBlockPlaceListener;
|
||||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotLegacyNativeWorldManager;
|
||||
|
|
@ -68,21 +65,21 @@ import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorld
|
|||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.incendo.cloud.bukkit.BukkitCommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
|
||||
private GeyserSpigotCommandManager geyserCommandManager;
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserSpigotConfiguration geyserConfig;
|
||||
private GeyserSpigotInjector geyserInjector;
|
||||
private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
|
||||
|
|
@ -165,31 +162,37 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
|
||||
// Because Bukkit locks its command map upon startup, we need to
|
||||
// add our plugin commands in onEnable, but populating the executor
|
||||
// can happen at any time (later in #onGeyserEnable())
|
||||
CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap();
|
||||
for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) {
|
||||
// Thanks again, Bukkit
|
||||
try {
|
||||
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||
constructor.setAccessible(true);
|
||||
|
||||
PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this);
|
||||
pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!");
|
||||
|
||||
commandMap.register(extension.description().id(), "geyserext", pluginCommand);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
||||
this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.name(), ex);
|
||||
}
|
||||
// Create command manager early so we can add Geyser extension commands
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSender.class,
|
||||
Bukkit::getPlayer,
|
||||
Bukkit::getConsoleSender,
|
||||
SpigotCommandSource::new
|
||||
);
|
||||
LegacyPaperCommandManager<GeyserCommandSource> cloud;
|
||||
try {
|
||||
// LegacyPaperCommandManager works for spigot too, see https://cloud.incendo.org/minecraft/paper
|
||||
cloud = new LegacyPaperCommandManager<>(
|
||||
this,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
try {
|
||||
// Commodore brigadier on Spigot/Paper 1.13 - 1.18.2
|
||||
// Paper-only brigadier on 1.19+
|
||||
cloud.registerBrigadier();
|
||||
} catch (BukkitCommandManager.BrigadierInitializationException e) {
|
||||
geyserLogger.debug("Failed to initialize Brigadier support: " + e.getMessage());
|
||||
}
|
||||
|
||||
this.commandRegistry = new SpigotCommandRegistry(geyser, cloud);
|
||||
|
||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
|
||||
@EventHandler
|
||||
public void onServerLoaded(ServerLoadEvent event) {
|
||||
if (event.getType() == ServerLoadEvent.LoadType.RELOAD) {
|
||||
|
|
@ -227,7 +230,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
|
||||
|
||||
// Don't need to re-create the world manager/re-register commands/reinject when reloading
|
||||
// Don't need to re-create the world manager/reinject when reloading
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -282,79 +285,40 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
geyserLogger.debug("Using default world manager.");
|
||||
}
|
||||
|
||||
PluginCommand geyserCommand = this.getCommand("geyser");
|
||||
Objects.requireNonNull(geyserCommand, "base command cannot be null");
|
||||
geyserCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser, geyserCommandManager.getCommands()));
|
||||
|
||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
Map<String, Command> commands = entry.getValue();
|
||||
if (commands.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PluginCommand command = this.getCommand(entry.getKey().description().id());
|
||||
if (command == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands));
|
||||
}
|
||||
|
||||
// Register permissions so they appear in, for example, LuckPerms' UI
|
||||
// Re-registering permissions throws an error
|
||||
for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
|
||||
Command command = entry.getValue();
|
||||
if (command.aliases().contains(entry.getKey())) {
|
||||
// Don't register aliases
|
||||
continue;
|
||||
// Re-registering permissions without removing it throws an error
|
||||
PluginManager pluginManager = Bukkit.getPluginManager();
|
||||
geyser.eventBus().fire((GeyserRegisterPermissionsEvent) (permission, def) -> {
|
||||
Objects.requireNonNull(permission, "permission");
|
||||
Objects.requireNonNull(def, "permission default for " + permission);
|
||||
|
||||
if (permission.isBlank()) {
|
||||
return;
|
||||
}
|
||||
PermissionDefault permissionDefault = switch (def) {
|
||||
case TRUE -> PermissionDefault.TRUE;
|
||||
case FALSE -> PermissionDefault.FALSE;
|
||||
case NOT_SET -> PermissionDefault.OP;
|
||||
};
|
||||
|
||||
Permission existingPermission = pluginManager.getPermission(permission);
|
||||
if (existingPermission != null) {
|
||||
geyserLogger.debug("permission " + permission + " with default " +
|
||||
existingPermission.getDefault() + " is being overridden by " + permissionDefault);
|
||||
|
||||
pluginManager.removePermission(permission);
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||
GeyserLocale.getLocaleStringLog(command.description()),
|
||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||
}
|
||||
|
||||
// Register permissions for extension commands
|
||||
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
|
||||
Command command = entry.getValue();
|
||||
if (command.aliases().contains(entry.getKey())) {
|
||||
// Don't register aliases
|
||||
continue;
|
||||
}
|
||||
|
||||
if (command.permission().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Avoid registering the same permission twice, e.g. for the extension help commands
|
||||
if (Bukkit.getPluginManager().getPermission(command.permission()) != null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("Skipping permission " + command.permission() + " as it is already registered");
|
||||
continue;
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||
GeyserLocale.getLocaleStringLog(command.description()),
|
||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||
}
|
||||
}
|
||||
|
||||
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
||||
"Whether update notifications can be seen", PermissionDefault.OP));
|
||||
pluginManager.addPermission(new Permission(permission, permissionDefault));
|
||||
});
|
||||
|
||||
// Events cannot be unregistered - re-registering results in duplicate firings
|
||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||
pluginManager.registerEvents(blockPlaceListener, this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||
pluginManager.registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||
|
||||
boolean brigadierSupported = CommodoreProvider.isSupported();
|
||||
geyserLogger.debug("Brigadier supported? " + brigadierSupported);
|
||||
if (brigadierSupported) {
|
||||
GeyserBrigadierSupport.loadBrigadier(this, geyserCommand);
|
||||
}
|
||||
pluginManager.registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -390,8 +354,8 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return this.geyserCommandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return this.commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ import org.bukkit.entity.Player;
|
|||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.Permissions;
|
||||
import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ public final class GeyserSpigotUpdateListener implements Listener {
|
|||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
final Player player = event.getPlayer();
|
||||
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
|
||||
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.spigot.command;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import me.lucko.commodore.Commodore;
|
||||
import me.lucko.commodore.CommodoreProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin;
|
||||
|
||||
/**
|
||||
* Needs to be a separate class so pre-1.13 loads correctly.
|
||||
*/
|
||||
public final class GeyserBrigadierSupport {
|
||||
|
||||
public static void loadBrigadier(GeyserSpigotPlugin plugin, PluginCommand pluginCommand) {
|
||||
// Enable command completions if supported
|
||||
// This is beneficial because this is sent over the network and Bedrock can see it
|
||||
Commodore commodore = CommodoreProvider.getCommodore(plugin);
|
||||
LiteralArgumentBuilder<?> builder = LiteralArgumentBuilder.literal("geyser");
|
||||
for (String command : plugin.getGeyserCommandManager().getCommands().keySet()) {
|
||||
builder.then(LiteralArgumentBuilder.literal(command));
|
||||
}
|
||||
commodore.register(pluginCommand, builder);
|
||||
|
||||
try {
|
||||
Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent");
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPaperCommandListener(), plugin);
|
||||
plugin.getGeyserLogger().debug("Successfully registered AsyncPlayerSendCommandsEvent listener.");
|
||||
} catch (ClassNotFoundException e) {
|
||||
plugin.getGeyserLogger().debug("Not registering AsyncPlayerSendCommandsEvent listener.");
|
||||
}
|
||||
}
|
||||
|
||||
private GeyserBrigadierSupport() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.spigot.command;
|
||||
|
||||
import com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public final class GeyserPaperCommandListener implements Listener {
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EventHandler
|
||||
public void onCommandSend(AsyncPlayerSendCommandsEvent<?> event) {
|
||||
// Documentation says to check (event.isAsynchronous() || !event.hasFiredAsync()), but as of Paper 1.18.2
|
||||
// event.hasFiredAsync is never true
|
||||
if (event.isAsynchronous()) {
|
||||
CommandNode<?> geyserBrigadier = event.getCommandNode().getChild("geyser");
|
||||
if (geyserBrigadier != null) {
|
||||
Player player = event.getPlayer();
|
||||
boolean isJavaPlayer = isProbablyJavaPlayer(player);
|
||||
Map<String, Command> commands = GeyserImpl.getInstance().commandManager().getCommands();
|
||||
Iterator<? extends CommandNode<?>> it = geyserBrigadier.getChildren().iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
CommandNode<?> subnode = it.next();
|
||||
Command command = commands.get(subnode.getName());
|
||||
if (command != null) {
|
||||
if ((command.isBedrockOnly() && isJavaPlayer) || !player.hasPermission(command.permission())) {
|
||||
// Remove this from the node as we don't have permission to use it
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This early on, there is a rare chance that Geyser has yet to process the connection. We'll try to minimize that
|
||||
* chance, though.
|
||||
*/
|
||||
private boolean isProbablyJavaPlayer(Player player) {
|
||||
if (GeyserImpl.getInstance().connectionByUuid(player.getUniqueId()) != null) {
|
||||
// For sure this is a Bedrock player
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GeyserImpl.getInstance().getConfig().isUseDirectConnection()) {
|
||||
InetSocketAddress address = player.getAddress();
|
||||
if (address != null) {
|
||||
return address.getPort() != 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.spigot.command;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GeyserSpigotCommandExecutor extends GeyserCommandExecutor implements TabExecutor {
|
||||
|
||||
public GeyserSpigotCommandExecutor(GeyserImpl geyser, Map<String, org.geysermc.geyser.api.command.Command> commands) {
|
||||
super(geyser, commands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NonNull CommandSender sender, @NonNull Command command, @NonNull String label, String[] args) {
|
||||
SpigotCommandSource commandSender = new SpigotCommandSource(sender);
|
||||
GeyserSession session = getGeyserSession(commandSender);
|
||||
|
||||
if (args.length > 0) {
|
||||
GeyserCommand geyserCommand = getCommand(args[0]);
|
||||
if (geyserCommand != null) {
|
||||
if (!sender.hasPermission(geyserCommand.permission())) {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", commandSender.locale());
|
||||
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
return true;
|
||||
}
|
||||
if (geyserCommand.isBedrockOnly() && session == null) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", commandSender.locale()));
|
||||
return true;
|
||||
}
|
||||
geyserCommand.execute(session, commandSender, args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]);
|
||||
return true;
|
||||
} else {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.not_found", commandSender.locale());
|
||||
commandSender.sendMessage(ChatColor.RED + message);
|
||||
}
|
||||
} else {
|
||||
getCommand("help").execute(session, commandSender, new String[0]);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(@NonNull CommandSender sender, @NonNull Command command, @NonNull String label, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return tabComplete(new SpigotCommandSource(sender));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -29,16 +29,21 @@ import org.bukkit.Bukkit;
|
|||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class GeyserSpigotCommandManager extends GeyserCommandManager {
|
||||
public class SpigotCommandRegistry extends CommandRegistry {
|
||||
|
||||
private static final CommandMap COMMAND_MAP;
|
||||
private final CommandMap commandMap;
|
||||
|
||||
public SpigotCommandRegistry(GeyserImpl geyser, CommandManager<GeyserCommandSource> cloud) {
|
||||
super(geyser, cloud);
|
||||
|
||||
static {
|
||||
CommandMap commandMap = null;
|
||||
try {
|
||||
// Paper-only
|
||||
|
|
@ -49,24 +54,28 @@ public class GeyserSpigotCommandManager extends GeyserCommandManager {
|
|||
Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
|
||||
cmdMapField.setAccessible(true);
|
||||
commandMap = (CommandMap) cmdMapField.get(Bukkit.getServer());
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (Exception ex) {
|
||||
geyser.getLogger().error("Failed to get Spigot's CommandMap", ex);
|
||||
}
|
||||
}
|
||||
COMMAND_MAP = commandMap;
|
||||
}
|
||||
|
||||
public GeyserSpigotCommandManager(GeyserImpl geyser) {
|
||||
super(geyser);
|
||||
this.commandMap = commandMap;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String description(String command) {
|
||||
Command cmd = COMMAND_MAP.getCommand(command.replace("/", ""));
|
||||
return cmd != null ? cmd.getDescription() : "";
|
||||
}
|
||||
public String description(@NonNull String command, @NonNull String locale) {
|
||||
// check if the command is /geyser or an extension command so that we can localize the description
|
||||
String description = super.description(command, locale);
|
||||
if (!description.isBlank()) {
|
||||
return description;
|
||||
}
|
||||
|
||||
public static CommandMap getCommandMap() {
|
||||
return COMMAND_MAP;
|
||||
if (commandMap != null) {
|
||||
Command cmd = commandMap.getCommand(command);
|
||||
if (cmd != null) {
|
||||
return cmd.getDescription();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -27,17 +27,21 @@ package org.geysermc.geyser.platform.spigot.command;
|
|||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.platform.spigot.PaperAdventure;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
public class SpigotCommandSource implements GeyserCommandSource {
|
||||
private final org.bukkit.command.CommandSender handle;
|
||||
import java.util.UUID;
|
||||
|
||||
public SpigotCommandSource(org.bukkit.command.CommandSender handle) {
|
||||
public class SpigotCommandSource implements GeyserCommandSource {
|
||||
private final CommandSender handle;
|
||||
|
||||
public SpigotCommandSource(CommandSender handle) {
|
||||
this.handle = handle;
|
||||
// Ensure even Java players' languages are loaded
|
||||
GeyserLocale.loadGeyserLocale(locale());
|
||||
|
|
@ -65,11 +69,24 @@ public class SpigotCommandSource implements GeyserCommandSource {
|
|||
handle.spigot().sendMessage(BungeeComponentSerializer.get().serialize(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return handle instanceof ConsoleCommandSender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID playerUuid() {
|
||||
if (handle instanceof Player player) {
|
||||
return player.getUniqueId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public String locale() {
|
||||
|
|
@ -83,6 +100,7 @@ public class SpigotCommandSource implements GeyserCommandSource {
|
|||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return handle.hasPermission(permission);
|
||||
// Don't trust Spigot to handle blank permissions
|
||||
return permission.isBlank() || handle.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,15 +128,6 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
|||
return GameMode.byId(Bukkit.getDefaultGameMode().ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(GeyserSession session, String permission) {
|
||||
Player player = Bukkit.getPlayer(session.javaUuid());
|
||||
if (player != null) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
Player bukkitPlayer;
|
||||
|
|
|
|||
|
|
@ -6,11 +6,3 @@ version: ${version}
|
|||
softdepend: ["ViaVersion", "floodgate"]
|
||||
api-version: 1.13
|
||||
folia-supported: true
|
||||
commands:
|
||||
geyser:
|
||||
description: The main command for Geyser.
|
||||
usage: /geyser <subcommand>
|
||||
permission: geyser.command
|
||||
permissions:
|
||||
geyser.command:
|
||||
default: true
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
|
||||
|
||||
plugins {
|
||||
application
|
||||
}
|
||||
|
||||
val terminalConsoleVersion = "1.2.0"
|
||||
val jlineVersion = "3.21.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
|
|
@ -69,7 +70,8 @@ import java.util.stream.Collectors;
|
|||
|
||||
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private StandaloneCloudCommandManager cloud;
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserStandaloneConfiguration geyserConfig;
|
||||
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
|
|
@ -222,13 +224,24 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
|
||||
geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
|
||||
|
||||
geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
geyserCommandManager.init();
|
||||
boolean reloading = geyser.isReloading();
|
||||
if (!reloading) {
|
||||
// Currently there would be no significant benefit of re-initializing commands. Also, we would have to unsubscribe CommandRegistry.
|
||||
// Fire GeyserDefineCommandsEvent after PreInitEvent, before PostInitEvent, for consistency with other bootstraps.
|
||||
cloud = new StandaloneCloudCommandManager(geyser);
|
||||
commandRegistry = new CommandRegistry(geyser, cloud);
|
||||
}
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
if (!reloading) {
|
||||
// Event must be fired after CommandRegistry has subscribed its listener.
|
||||
// Also, the subscription for the Permissions class is created when Geyser is initialized.
|
||||
cloud.fireRegisterPermissionsEvent();
|
||||
}
|
||||
|
||||
if (gui != null) {
|
||||
gui.enableCommands(geyser.getScheduledThread(), geyserCommandManager);
|
||||
gui.enableCommands(geyser.getScheduledThread(), commandRegistry);
|
||||
}
|
||||
|
||||
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||
|
|
@ -255,8 +268,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
|
||||
@Override
|
||||
public void onGeyserDisable() {
|
||||
// We can re-register commands on standalone, so why not
|
||||
GeyserImpl.getInstance().commandManager().getCommands().clear();
|
||||
geyser.disable();
|
||||
}
|
||||
|
||||
|
|
@ -277,8 +288,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return geyserCommandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -44,7 +44,9 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
|||
|
||||
@Override
|
||||
protected void runCommand(String line) {
|
||||
GeyserImpl.getInstance().commandManager().runCommand(this, line);
|
||||
// don't block the terminal!
|
||||
GeyserImpl geyser = GeyserImpl.getInstance();
|
||||
geyser.getScheduledThread().execute(() -> geyser.commandRegistry().runCommand(this, line));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ package org.geysermc.geyser.platform.standalone.gui;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
|
|
@ -271,15 +271,14 @@ public class GeyserStandaloneGUI {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enable the command input box.
|
||||
* Enables the command input box.
|
||||
*
|
||||
* @param executor the executor for running commands off the GUI thread
|
||||
* @param commandManager the command manager to delegate commands to
|
||||
* @param executor the executor that commands will be run on
|
||||
* @param registry the command registry containing all current commands
|
||||
*/
|
||||
public void enableCommands(ScheduledExecutorService executor, GeyserCommandManager commandManager) {
|
||||
public void enableCommands(ScheduledExecutorService executor, CommandRegistry registry) {
|
||||
// we don't want to block the GUI thread with the command execution
|
||||
// todo: once cloud is used, an AsynchronousCommandExecutionCoordinator can be used to avoid this scheduler
|
||||
commandListener.handler = cmd -> executor.schedule(() -> commandManager.runCommand(logger, cmd), 0, TimeUnit.SECONDS);
|
||||
commandListener.dispatcher = cmd -> executor.execute(() -> registry.runCommand(logger, cmd));
|
||||
commandInput.setEnabled(true);
|
||||
commandInput.requestFocusInWindow();
|
||||
}
|
||||
|
|
@ -344,13 +343,14 @@ public class GeyserStandaloneGUI {
|
|||
|
||||
private class CommandListener implements ActionListener {
|
||||
|
||||
private Consumer<String> handler;
|
||||
private Consumer<String> dispatcher;
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String command = commandInput.getText();
|
||||
// the headless variant of Standalone strips trailing whitespace for us - we need to manually
|
||||
String command = commandInput.getText().stripTrailing();
|
||||
appendConsole(command + "\n"); // show what was run in the console
|
||||
handler.accept(command); // run the command
|
||||
dispatcher.accept(command); // run the command
|
||||
commandInput.setText(""); // clear the input
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@ dependencies {
|
|||
api(projects.core)
|
||||
|
||||
compileOnlyApi(libs.velocity.api)
|
||||
api(libs.cloud.velocity)
|
||||
}
|
||||
|
||||
platformRelocate("com.fasterxml.jackson")
|
||||
platformRelocate("it.unimi.dsi.fastutil")
|
||||
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
|
||||
platformRelocate("org.yaml")
|
||||
platformRelocate("org.incendo")
|
||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
||||
|
||||
exclude("com.google.*:*")
|
||||
|
||||
|
|
@ -38,8 +41,8 @@ exclude("net.kyori:adventure-nbt:*")
|
|||
// These dependencies are already present on the platform
|
||||
provided(libs.velocity.api)
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.velocity.GeyserVelocityMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.velocity.GeyserVelocityMain"
|
||||
}
|
||||
|
||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
|
|
@ -74,4 +77,4 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
|||
modrinth {
|
||||
uploadFile.set(tasks.getByPath("shadowJar"))
|
||||
loaders.addAll("velocity")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
package org.geysermc.geyser.platform.velocity;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.command.CommandManager;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
|
|
@ -34,24 +34,28 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
|||
import com.velocitypowered.api.network.ListenerType;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.PluginContainer;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor;
|
||||
import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
import org.incendo.cloud.velocity.VelocityCommandManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -59,29 +63,28 @@ import java.io.IOException;
|
|||
import java.net.SocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||
public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||
|
||||
private final ProxyServer proxyServer;
|
||||
private final CommandManager commandManager;
|
||||
private final PluginContainer container;
|
||||
private final GeyserVelocityLogger geyserLogger;
|
||||
private GeyserCommandManager geyserCommandManager;
|
||||
private GeyserVelocityConfiguration geyserConfig;
|
||||
private GeyserVelocityInjector geyserInjector;
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
private CommandRegistry commandRegistry;
|
||||
private GeyserImpl geyser;
|
||||
|
||||
@Getter
|
||||
private final Path configFolder = Paths.get("plugins/" + GeyserImpl.NAME + "-Velocity/");
|
||||
|
||||
@Inject
|
||||
public GeyserVelocityPlugin(ProxyServer server, Logger logger, CommandManager manager) {
|
||||
this.geyserLogger = new GeyserVelocityLogger(logger);
|
||||
public GeyserVelocityPlugin(ProxyServer server, PluginContainer container, Logger logger) {
|
||||
this.proxyServer = server;
|
||||
this.commandManager = manager;
|
||||
this.container = container;
|
||||
this.geyserLogger = new GeyserVelocityLogger(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -117,8 +120,19 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
} else {
|
||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
var sourceConverter = new CommandSourceConverter<>(
|
||||
CommandSource.class,
|
||||
id -> proxyServer.getPlayer(id).orElse(null),
|
||||
proxyServer::getConsoleCommandSource,
|
||||
VelocityCommandSource::new
|
||||
);
|
||||
CommandManager<GeyserCommandSource> cloud = new VelocityCommandManager<>(
|
||||
container,
|
||||
proxyServer,
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
sourceConverter
|
||||
);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults
|
||||
}
|
||||
|
||||
GeyserImpl.start();
|
||||
|
|
@ -129,22 +143,10 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
||||
}
|
||||
|
||||
// No need to re-register commands when reloading
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
return;
|
||||
// No need to re-register events
|
||||
if (!GeyserImpl.getInstance().isReloading()) {
|
||||
proxyServer.getEventManager().register(this, new GeyserVelocityUpdateListener());
|
||||
}
|
||||
|
||||
this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands()));
|
||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||
Map<String, Command> commands = entry.getValue();
|
||||
if (commands.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands));
|
||||
}
|
||||
|
||||
proxyServer.getEventManager().register(this, new GeyserVelocityUpdateListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -175,8 +177,8 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return this.geyserCommandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return this.commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ package org.geysermc.geyser.platform.velocity;
|
|||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PostLoginEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.Permissions;
|
||||
import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ public final class GeyserVelocityUpdateListener {
|
|||
public void onPlayerJoin(PostLoginEvent event) {
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
final Player player = event.getPlayer();
|
||||
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
|
||||
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.velocity.command;
|
||||
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.command.GeyserCommand;
|
||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GeyserVelocityCommandExecutor extends GeyserCommandExecutor implements SimpleCommand {
|
||||
|
||||
public GeyserVelocityCommandExecutor(GeyserImpl geyser, Map<String, Command> commands) {
|
||||
super(geyser, commands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Invocation invocation) {
|
||||
GeyserCommandSource sender = new VelocityCommandSource(invocation.source());
|
||||
GeyserSession session = getGeyserSession(sender);
|
||||
|
||||
if (invocation.arguments().length > 0) {
|
||||
GeyserCommand command = getCommand(invocation.arguments()[0]);
|
||||
if (command != null) {
|
||||
if (!invocation.source().hasPermission(getCommand(invocation.arguments()[0]).permission())) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
||||
return;
|
||||
}
|
||||
if (command.isBedrockOnly() && session == null) {
|
||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale()));
|
||||
return;
|
||||
}
|
||||
command.execute(session, sender, invocation.arguments().length > 1 ? Arrays.copyOfRange(invocation.arguments(), 1, invocation.arguments().length) : new String[0]);
|
||||
} else {
|
||||
String message = GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.not_found", sender.locale());
|
||||
sender.sendMessage(ChatColor.RED + message);
|
||||
}
|
||||
} else {
|
||||
getCommand("help").execute(session, sender, new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(Invocation invocation) {
|
||||
// Velocity seems to do the splitting a bit differently. This results in the same behaviour in bungeecord/spigot.
|
||||
if (invocation.arguments().length == 0 || invocation.arguments().length == 1) {
|
||||
return tabComplete(new VelocityCommandSource(invocation.source()));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
|
@ -31,10 +31,12 @@ import com.velocitypowered.api.proxy.Player;
|
|||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public class VelocityCommandSource implements GeyserCommandSource {
|
||||
|
||||
|
|
@ -72,6 +74,14 @@ public class VelocityCommandSource implements GeyserCommandSource {
|
|||
return handle instanceof ConsoleCommandSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID playerUuid() {
|
||||
if (handle instanceof Player player) {
|
||||
return player.getUniqueId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String locale() {
|
||||
if (handle instanceof Player) {
|
||||
|
|
@ -83,6 +93,12 @@ public class VelocityCommandSource implements GeyserCommandSource {
|
|||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return handle.hasPermission(permission);
|
||||
// Handle blank permissions ourselves, as velocity only handles empty ones
|
||||
return permission.isBlank() || handle.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ platformRelocate("net.kyori")
|
|||
platformRelocate("org.yaml")
|
||||
platformRelocate("it.unimi.dsi.fastutil")
|
||||
platformRelocate("org.cloudburstmc.netty")
|
||||
platformRelocate("org.incendo")
|
||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
||||
|
||||
// These dependencies are already present on the platform
|
||||
provided(libs.viaproxy)
|
||||
|
||||
application {
|
||||
mainClass.set("org.geysermc.geyser.platform.viaproxy.GeyserViaProxyMain")
|
||||
tasks.withType<Jar> {
|
||||
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.viaproxy.GeyserViaProxyMain"
|
||||
}
|
||||
|
||||
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
|
||||
|
|
|
|||
|
|
@ -34,13 +34,15 @@ import net.raphimc.viaproxy.plugins.events.ProxyStartEvent;
|
|||
import net.raphimc.viaproxy.plugins.events.ProxyStopEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.ShouldVerifyOnlineModeEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.command.CommandRegistry;
|
||||
import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||
|
|
@ -50,7 +52,6 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
import org.geysermc.geyser.util.LoopbackUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
|
@ -66,7 +67,8 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||
private final GeyserViaProxyLogger logger = new GeyserViaProxyLogger(LogManager.getLogger("Geyser"));
|
||||
private GeyserViaProxyConfiguration config;
|
||||
private GeyserImpl geyser;
|
||||
private GeyserCommandManager commandManager;
|
||||
private StandaloneCloudCommandManager cloud;
|
||||
private CommandRegistry commandRegistry;
|
||||
private IGeyserPingPassthrough pingPassthrough;
|
||||
|
||||
@Override
|
||||
|
|
@ -87,7 +89,9 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||
@EventHandler
|
||||
private void onConsoleCommand(final ConsoleCommandEvent event) {
|
||||
final String command = event.getCommand().startsWith("/") ? event.getCommand().substring(1) : event.getCommand();
|
||||
if (this.getGeyserCommandManager().runCommand(this.getGeyserLogger(), command + " " + String.join(" ", event.getArgs()))) {
|
||||
CommandRegistry registry = this.getCommandRegistry();
|
||||
if (registry.rootCommands().contains(command)) {
|
||||
registry.runCommand(this.getGeyserLogger(), command + " " + String.join(" ", event.getArgs()));
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -128,17 +132,25 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||
|
||||
@Override
|
||||
public void onGeyserEnable() {
|
||||
if (GeyserImpl.getInstance().isReloading()) {
|
||||
boolean reloading = geyser.isReloading();
|
||||
if (reloading) {
|
||||
if (!this.loadConfig()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Only initialized once - documented in the Geyser-Standalone bootstrap
|
||||
this.cloud = new StandaloneCloudCommandManager(geyser);
|
||||
this.commandRegistry = new CommandRegistry(geyser, cloud);
|
||||
}
|
||||
|
||||
this.commandManager = new GeyserCommandManager(this.geyser);
|
||||
this.commandManager.init();
|
||||
|
||||
GeyserImpl.start();
|
||||
|
||||
if (!reloading) {
|
||||
// Event must be fired after CommandRegistry has subscribed its listener.
|
||||
// Also, the subscription for the Permissions class is created when Geyser is initialized (by GeyserImpl#start)
|
||||
this.cloud.fireRegisterPermissionsEvent();
|
||||
}
|
||||
|
||||
if (ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().newerThanOrEqualTo(LegacyProtocolVersion.b1_8tob1_8_1)) {
|
||||
// Only initialize the ping passthrough if the protocol version is above beta 1.7.3, as that's when the status protocol was added
|
||||
this.pingPassthrough = GeyserLegacyPingPassthrough.init(this.geyser);
|
||||
|
|
@ -166,8 +178,8 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeyserCommandManager getGeyserCommandManager() {
|
||||
return this.commandManager;
|
||||
public CommandRegistry getCommandRegistry() {
|
||||
return this.commandRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -185,7 +197,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||
return new GeyserViaProxyDumpInfo();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@NonNull
|
||||
@Override
|
||||
public String getServerBindAddress() {
|
||||
if (ViaProxy.getConfig().getBindAddress() instanceof InetSocketAddress socketAddress) {
|
||||
|
|
@ -209,6 +221,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean loadConfig() {
|
||||
try {
|
||||
final File configFile = FileUtils.fileOrCopiedFromResource(new File(ROOT_FOLDER, "config.yml"), "config.yml", s -> s.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue