Fix CraftServer#syncCommands with commands unloaded by plugins

This commit is contained in:
Frank van der Heijden 2022-06-15 17:06:40 +02:00
parent 4054a3eca9
commit f9e2379e88
No known key found for this signature in database
GPG key ID: 4BC514A52A496F84
2 changed files with 44 additions and 25 deletions

View file

@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -109,11 +110,14 @@ public class BukkitPluginManager extends AbstractPluginManager<Plugin, BukkitPlu
public PluginResults<Plugin> disableOrderedPlugins(List<Plugin> plugins) { public PluginResults<Plugin> disableOrderedPlugins(List<Plugin> plugins) {
PluginResults<Plugin> disableResults = new PluginResults<>(); PluginResults<Plugin> disableResults = new PluginResults<>();
Set<String> removedCommands = new HashSet<>();
for (Plugin plugin : plugins) { for (Plugin plugin : plugins) {
String pluginId = getPluginId(plugin); String pluginId = getPluginId(plugin);
if (!isPluginEnabled(pluginId)) return disableResults.addResult(pluginId, Result.ALREADY_DISABLED); if (!isPluginEnabled(pluginId)) return disableResults.addResult(pluginId, Result.ALREADY_DISABLED);
Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.PRE)); Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.PRE));
Map<String, ? extends Command> pluginCommands = getPluginCommands(plugin);
try { try {
Bukkit.getPluginManager().disablePlugin(plugin); Bukkit.getPluginManager().disablePlugin(plugin);
RCraftingManager.removeRecipesFor(plugin); RCraftingManager.removeRecipesFor(plugin);
@ -122,12 +126,14 @@ public class BukkitPluginManager extends AbstractPluginManager<Plugin, BukkitPlu
return disableResults.addResult(pluginId, Result.ERROR); return disableResults.addResult(pluginId, Result.ERROR);
} }
unregisterCommands(plugin); unregisterCommands(pluginCommands);
removedCommands.addAll(pluginCommands.keySet());
Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.POST)); Bukkit.getPluginManager().callEvent(new BukkitPluginDisableEvent(plugin, PluginEvent.Stage.POST));
disableResults.addResult(pluginId, plugin); disableResults.addResult(pluginId, plugin);
} }
RCraftServer.syncCommands(removedCommands);
return disableResults; return disableResults;
} }
@ -192,7 +198,7 @@ public class BukkitPluginManager extends AbstractPluginManager<Plugin, BukkitPlu
enableResults.addResult(pluginId, plugin); enableResults.addResult(pluginId, plugin);
} }
RCraftServer.syncCommands(); RCraftServer.syncCommands(Collections.emptySet());
return enableResults; return enableResults;
} }
@ -214,31 +220,40 @@ public class BukkitPluginManager extends AbstractPluginManager<Plugin, BukkitPlu
} }
} }
/**
* Retrieves the commands associated to a plugin.
*/
public static <C extends Command & PluginIdentifiableCommand> Map<String, C> getPluginCommands(Plugin plugin) {
Map<String, C> commands = new HashMap<>();
Map<String, Command> knownCommands = getKnownCommands();
if (knownCommands == null) return commands;
for (Map.Entry<String, Command> entry : knownCommands.entrySet()) {
Command c = entry.getValue();
if (c instanceof PluginIdentifiableCommand) {
@SuppressWarnings("unchecked")
C pc = (C) c;
if (pc.getPlugin().getName().equals(plugin.getName())) {
commands.put(entry.getKey(), pc);
}
}
}
return commands;
}
/** /**
* Unregisters all commands from the specified plugin. * Unregisters all commands from the specified plugin.
* @param plugin The plugin. * @param plugin The plugin.
*/ */
public static void unregisterCommands(Plugin plugin) { public static void unregisterCommands(Plugin plugin) {
Map<String, Command> knownCommands = getKnownCommands(); unregisterCommands(getPluginCommands(plugin));
if (knownCommands == null) return; }
List<String> unregisteredCommands = new ArrayList<>(); private static void unregisterCommands(Map<String, ? extends Command> pluginCommands) {
knownCommands.entrySet().removeIf(e -> { pluginCommands.values().forEach(c -> c.unregister(RCraftServer.getCommandMap()));
Command c = e.getValue(); RCommandDispatcher.removeCommands(pluginCommands.keySet());
if (c instanceof PluginIdentifiableCommand) {
PluginIdentifiableCommand pc = (PluginIdentifiableCommand) c;
if (pc.getPlugin().getName().equals(plugin.getName())) {
c.unregister(RCraftServer.getCommandMap());
unregisteredCommands.add(e.getKey());
return true;
}
return false;
}
return false;
});
RCommandDispatcher.removeCommands(unregisteredCommands);
RCraftServer.updateCommands();
} }
/** /**

View file

@ -48,7 +48,7 @@ public class RCraftServer {
* Syncs and registers all commands, but keeping the old values that haven't been added. * Syncs and registers all commands, but keeping the old values that haven't been added.
*/ */
@SuppressWarnings({"rawtypes"}) @SuppressWarnings({"rawtypes"})
public static void syncCommands() { public static void syncCommands(Set<String> removedCommands) {
if (MinecraftReflectionVersion.MINOR < 13) return; if (MinecraftReflectionVersion.MINOR < 13) return;
Collection children = RCommandDispatcher.getDispatcher().getRoot().getChildren(); Collection children = RCommandDispatcher.getDispatcher().getRoot().getChildren();
@ -56,8 +56,12 @@ public class RCraftServer {
Object root = RCommandDispatcher.getDispatcher().getRoot(); Object root = RCommandDispatcher.getDispatcher().getRoot();
for (Object child : children) { for (Object child : children) {
RCommandNode.removeCommand(root, RCommandNode.getName(child)); String name = RCommandNode.getName(child);
RCommandNode.addChild(root, child); RCommandNode.removeCommand(root, name);
if (!removedCommands.contains(name)) {
RCommandNode.addChild(root, child);
}
} }
updateCommands(); updateCommands();
} }
@ -144,7 +148,7 @@ public class RCraftServer {
); );
commandMap.registerServerAliases(); commandMap.registerServerAliases();
RCraftServer.syncCommands(); RCraftServer.syncCommands(commandNames);
} }
/** /**