Use bukkit command dispatcher (#132)

This commit is contained in:
IzzelAliz 2021-02-12 17:42:37 +08:00
parent ddbb4265f2
commit 9ede1cf416
10 changed files with 93 additions and 70 deletions

View File

@ -1,5 +1,6 @@
package io.izzel.arclight.common.bridge.server; package io.izzel.arclight.common.bridge.server;
import net.minecraft.command.Commands;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.RemoteConsoleCommandSender; import org.bukkit.command.RemoteConsoleCommandSender;
import org.bukkit.craftbukkit.v.CraftServer; import org.bukkit.craftbukkit.v.CraftServer;
@ -21,4 +22,6 @@ public interface MinecraftServerBridge {
void bridge$drainQueuedTasks(); void bridge$drainQueuedTasks();
boolean bridge$hasStopped(); boolean bridge$hasStopped();
Commands bridge$getVanillaCommands();
} }

View File

@ -3,7 +3,6 @@ package io.izzel.arclight.common.mixin.bukkit;
import io.izzel.arclight.common.bridge.bukkit.CraftServerBridge; import io.izzel.arclight.common.bridge.bukkit.CraftServerBridge;
import io.izzel.arclight.common.bridge.world.WorldBridge; import io.izzel.arclight.common.bridge.world.WorldBridge;
import jline.console.ConsoleReader; import jline.console.ConsoleReader;
import net.minecraft.command.Commands;
import net.minecraft.server.dedicated.DedicatedPlayerList; import net.minecraft.server.dedicated.DedicatedPlayerList;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.management.PlayerList; import net.minecraft.server.management.PlayerList;
@ -11,17 +10,12 @@ import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.world.WorldEvent;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.craftbukkit.v.CraftServer; import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.CraftWorld; import org.bukkit.craftbukkit.v.CraftWorld;
import org.bukkit.craftbukkit.v.command.BukkitCommandWrapper;
import org.bukkit.craftbukkit.v.command.CraftCommandMap; import org.bukkit.craftbukkit.v.command.CraftCommandMap;
import org.bukkit.craftbukkit.v.help.SimpleHelpMap; import org.bukkit.craftbukkit.v.help.SimpleHelpMap;
import org.bukkit.craftbukkit.v.util.permissions.CraftDefaultPermissions;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginLoadOrder;
import org.bukkit.plugin.SimplePluginManager; import org.bukkit.plugin.SimplePluginManager;
import org.bukkit.util.permissions.DefaultPermissions;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Mutable;
@ -81,50 +75,6 @@ public abstract class CraftServerMixin implements CraftServerBridge {
} }
} }
/**
* @author IzzelAliz
* @reason
*/
@Overwrite(remap = false)
public void enablePlugins(PluginLoadOrder type) {
if (type == PluginLoadOrder.STARTUP) {
helpMap.clear();
helpMap.initializeGeneralTopics();
}
Plugin[] plugins = pluginManager.getPlugins();
for (Plugin plugin : plugins) {
if ((!plugin.isEnabled()) && (plugin.getDescription().getLoad() == type)) {
enablePlugin(plugin);
}
}
if (type == PluginLoadOrder.POSTWORLD) {
this.commandMap.setFallbackCommands();
this.commandMap.registerServerAliases();
DefaultPermissions.registerCorePermissions();
CraftDefaultPermissions.registerCorePermissions();
this.loadCustomPermissions();
this.helpMap.initializeCommands();
this.syncCommands();
}
}
/**
* @author IzzelAliz
* @reason
*/
@Overwrite(remap = false)
public void syncCommands() {
Commands dispatcher = this.console.getCommandManager();
for (Map.Entry<String, Command> entry : this.commandMap.getKnownCommands().entrySet()) {
String label = entry.getKey();
Command command = entry.getValue();
new BukkitCommandWrapper((CraftServer) (Object) this, command).register(dispatcher.getDispatcher(), label);
}
}
@Override @Override
public void bridge$setPlayerList(PlayerList playerList) { public void bridge$setPlayerList(PlayerList playerList) {
this.playerList = (DedicatedPlayerList) playerList; this.playerList = (DedicatedPlayerList) playerList;

View File

@ -0,0 +1,24 @@
package io.izzel.arclight.common.mixin.core.advancements;
import com.mojang.brigadier.CommandDispatcher;
import io.izzel.arclight.common.bridge.server.MinecraftServerBridge;
import net.minecraft.advancements.FunctionManager;
import net.minecraft.command.CommandSource;
import net.minecraft.server.MinecraftServer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(FunctionManager.class)
public class FunctionManagerMixin {
@Shadow @Final private MinecraftServer server;
@Inject(method = "getCommandDispatcher", cancellable = true, at = @At("HEAD"))
private void arclight$useVanillaDispatcher(CallbackInfoReturnable<CommandDispatcher<CommandSource>> cir) {
cir.setReturnValue(((MinecraftServerBridge) this.server).bridge$getVanillaCommands().getDispatcher());
}
}

View File

@ -1,29 +1,66 @@
package io.izzel.arclight.common.mixin.core.command; package io.izzel.arclight.common.mixin.core.command;
import com.google.common.collect.Maps;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.RootCommandNode; import com.mojang.brigadier.tree.RootCommandNode;
import io.izzel.arclight.common.bridge.command.CommandNodeBridge; import io.izzel.arclight.common.bridge.command.CommandNodeBridge;
import io.izzel.arclight.common.bridge.entity.player.ServerPlayerEntityBridge; import io.izzel.arclight.common.bridge.entity.player.ServerPlayerEntityBridge;
import io.izzel.arclight.common.bridge.server.MinecraftServerBridge;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands; import net.minecraft.command.Commands;
import net.minecraft.command.ISuggestionProvider; import net.minecraft.command.ISuggestionProvider;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.play.server.SCommandListPacket;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.player.PlayerCommandSendEvent; import org.bukkit.event.player.PlayerCommandSendEvent;
import org.spigotmc.SpigotConfig;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
@Mixin(Commands.class) @Mixin(Commands.class)
public class CommandsMixin { public abstract class CommandsMixin {
// @formatter:off
@Shadow public abstract int handleCommand(CommandSource source, String command);
@Shadow @Final private CommandDispatcher<CommandSource> dispatcher;
// @formatter:on
@Shadow
protected abstract void commandSourceNodesToSuggestionNodes(CommandNode<CommandSource> rootCommandSource, CommandNode<ISuggestionProvider> rootSuggestion, CommandSource source, Map<CommandNode<CommandSource>, CommandNode<ISuggestionProvider>> commandNodeToSuggestionNode);
public void arclight$constructor() {
this.dispatcher = new CommandDispatcher<>();
this.dispatcher.setConsumer((context, b, i) -> context.getSource().onCommandComplete(context, b, i));
}
public int a(CommandSource source, String command, String label) {
return this.handleCommand(source, command);
}
/**
* @author IzzelAliz
* @reason
*/
@Overwrite
public void send(ServerPlayerEntity player) {
if (SpigotConfig.tabComplete < 0) return;
Map<CommandNode<CommandSource>, CommandNode<ISuggestionProvider>> map = Maps.newIdentityHashMap();
RootCommandNode<ISuggestionProvider> vanillaRoot = new RootCommandNode<>();
Commands vanillaCommands = ((MinecraftServerBridge) player.server).bridge$getVanillaCommands();
map.put(vanillaCommands.getDispatcher().getRoot(), vanillaRoot);
this.commandSourceNodesToSuggestionNodes(vanillaCommands.getDispatcher().getRoot(), vanillaRoot, player.getCommandSource(), map);
RootCommandNode<ISuggestionProvider> node = new RootCommandNode<>();
map.put(this.dispatcher.getRoot(), node);
this.commandSourceNodesToSuggestionNodes(this.dispatcher.getRoot(), node, player.getCommandSource(), map);
@Inject(method = "send", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/command/Commands;commandSourceNodesToSuggestionNodes(Lcom/mojang/brigadier/tree/CommandNode;Lcom/mojang/brigadier/tree/CommandNode;Lnet/minecraft/command/CommandSource;Ljava/util/Map;)V"))
private void arclight$playerCommandSend(ServerPlayerEntity player, CallbackInfo ci, Map<CommandNode<CommandSource>, CommandNode<ISuggestionProvider>> map , RootCommandNode<ISuggestionProvider> node) {
LinkedHashSet<String> set = new LinkedHashSet<>(); LinkedHashSet<String> set = new LinkedHashSet<>();
for (CommandNode<ISuggestionProvider> child : node.getChildren()) { for (CommandNode<ISuggestionProvider> child : node.getChildren()) {
set.add(child.getName()); set.add(child.getName());
@ -35,5 +72,6 @@ public class CommandsMixin {
((CommandNodeBridge) node).bridge$removeCommand(s); ((CommandNodeBridge) node).bridge$removeCommand(s);
} }
} }
player.connection.sendPacket(new SCommandListPacket(node));
} }
} }

View File

@ -1057,7 +1057,7 @@ public abstract class ServerPlayNetHandlerMixin implements ServerPlayNetHandlerB
return; return;
} }
try { try {
minecraftServer.getCommandManager().handleCommand(((CraftPlayer) event.getPlayer()).getHandle().getCommandSource(), event.getMessage()); this.server.dispatchCommand(event.getPlayer(), event.getMessage().substring(1));
} catch (CommandException ex) { } catch (CommandException ex) {
player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to perform this command"); player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to perform this command");
java.util.logging.Logger.getLogger(ServerPlayNetHandler.class.getName()).log(Level.SEVERE, null, ex); java.util.logging.Logger.getLogger(ServerPlayNetHandler.class.getName()).log(Level.SEVERE, null, ex);

View File

@ -135,6 +135,7 @@ public abstract class MinecraftServerMixin extends RecursiveEventLoop<TickDelaye
public RemoteConsoleCommandSender remoteConsole; public RemoteConsoleCommandSender remoteConsole;
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<>(); public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<>();
public int autosavePeriod; public int autosavePeriod;
public Commands vanillaCommandDispatcher;
private boolean hasStopped = false; private boolean hasStopped = false;
private final Object stopLock = new Object(); private final Object stopLock = new Object();
@ -165,6 +166,7 @@ public abstract class MinecraftServerMixin extends RecursiveEventLoop<TickDelaye
e.printStackTrace(); e.printStackTrace();
} }
this.datapackconfiguration = ArclightCaptures.getDatapackConfig(); this.datapackconfiguration = ArclightCaptures.getDatapackConfig();
this.vanillaCommandDispatcher = dataRegistries.getCommandManager();
} }
/** /**
@ -508,6 +510,11 @@ public abstract class MinecraftServerMixin extends RecursiveEventLoop<TickDelaye
return getBukkitSender(wrapper); return getBukkitSender(wrapper);
} }
@Override
public Commands bridge$getVanillaCommands() {
return this.vanillaCommandDispatcher;
}
public boolean isDebugging() { public boolean isDebugging() {
return false; return false;
} }

View File

@ -6,6 +6,7 @@ import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands; import net.minecraft.command.Commands;
import net.minecraft.network.rcon.RConConsoleSource; import net.minecraft.network.rcon.RConConsoleSource;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.dedicated.PendingCommand;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v.CraftServer; import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.command.CraftRemoteConsoleCommandSender; import org.bukkit.craftbukkit.v.command.CraftRemoteConsoleCommandSender;
@ -48,10 +49,13 @@ public abstract class DedicatedServerMixin extends MinecraftServerMixin {
@Redirect(method = "executePendingCommands", at = @At(value = "INVOKE", target = "Lnet/minecraft/command/Commands;handleCommand(Lnet/minecraft/command/CommandSource;Ljava/lang/String;)I")) @Redirect(method = "executePendingCommands", at = @At(value = "INVOKE", target = "Lnet/minecraft/command/Commands;handleCommand(Lnet/minecraft/command/CommandSource;Ljava/lang/String;)I"))
private int arclight$serverCommandEvent(Commands commands, CommandSource source, String command) { private int arclight$serverCommandEvent(Commands commands, CommandSource source, String command) {
if (command.isEmpty()) {
return 0;
}
ServerCommandEvent event = new ServerCommandEvent(console, command); ServerCommandEvent event = new ServerCommandEvent(console, command);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) { if (!event.isCancelled()) {
return commands.handleCommand(source, event.getCommand()); server.dispatchCommand(console, event.getCommand());
} }
return 0; return 0;
} }
@ -69,12 +73,7 @@ public abstract class DedicatedServerMixin extends MinecraftServerMixin {
if (event.isCancelled()) { if (event.isCancelled()) {
return; return;
} }
ServerCommandEvent event2 = new ServerCommandEvent(console, event.getCommand()); this.server.dispatchServerCommand(remoteConsole, new PendingCommand(event.getCommand(), this.rconConsoleSource.getCommandSource()));
Bukkit.getPluginManager().callEvent(event2);
if (event2.isCancelled()) {
return;
}
this.getCommandManager().handleCommand(this.rconConsoleSource.getCommandSource(), event2.getCommand());
}); });
return this.rconConsoleSource.getLogContents(); return this.rconConsoleSource.getLogContents();
} }

View File

@ -233,12 +233,12 @@ public abstract class ServerWorldMixin extends WorldMixin implements ServerWorld
@Inject(method = "save", at = @At("RETURN")) @Inject(method = "save", at = @At("RETURN"))
private void arclight$saveLevelDat(IProgressUpdate progress, boolean flush, boolean skipSave, CallbackInfo ci) { private void arclight$saveLevelDat(IProgressUpdate progress, boolean flush, boolean skipSave, CallbackInfo ci) {
if (this.field_241103_E_ instanceof DerivedWorldInfo) { if (this.field_241103_E_ instanceof ServerWorldInfo) {
return; ServerWorldInfo worldInfo = (ServerWorldInfo) this.field_241103_E_;
worldInfo.setWorldBorderSerializer(this.getWorldBorder().getSerializer());
worldInfo.setCustomBossEventData(this.shadow$getServer().getCustomBossEvents().write());
this.convertable.saveLevel(this.shadow$getServer().field_240767_f_, worldInfo, this.shadow$getServer().getPlayerList().getHostPlayerData());
} }
this.$$worldDataServer.setWorldBorderSerializer(this.getWorldBorder().getSerializer());
this.$$worldDataServer.setCustomBossEventData(this.shadow$getServer().getCustomBossEvents().write());
this.convertable.saveLevel(this.shadow$getServer().field_240767_f_, this.$$worldDataServer, this.shadow$getServer().getPlayerList().getHostPlayerData());
} }
@Inject(method = "onChunkUnloading", at = @At("HEAD")) @Inject(method = "onChunkUnloading", at = @At("HEAD"))

View File

@ -132,6 +132,7 @@ public class ArclightMixinPlugin implements IMixinConfigPlugin {
.add("net.minecraft.network.play.client.CCloseWindowPacket") .add("net.minecraft.network.play.client.CCloseWindowPacket")
.add("net.minecraft.world.dimension.DimensionType") .add("net.minecraft.world.dimension.DimensionType")
.add("net.minecraft.util.text.Color") .add("net.minecraft.util.text.Color")
.add("net.minecraft.command.Commands")
.build(); .build();
@Override @Override

View File

@ -16,6 +16,7 @@
"mixinPriority": 500, "mixinPriority": 500,
"mixins": [ "mixins": [
"advancements.AdvancementMixin", "advancements.AdvancementMixin",
"advancements.FunctionManagerMixin",
"advancements.PlayerAdvancementsMixin", "advancements.PlayerAdvancementsMixin",
"block.AbstractBlock_AbstractBlockStateMixin", "block.AbstractBlock_AbstractBlockStateMixin",
"block.AbstractBlockMixin", "block.AbstractBlockMixin",