Skip to content

Commit b624ba8

Browse files
committed
[+] PluginLoader: unload plugin
1 parent 12b067e commit b624ba8

File tree

1 file changed

+54
-13
lines changed

1 file changed

+54
-13
lines changed

src/main/java/org/hydev/mcpm/client/injector/PluginLoader.java

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,22 +70,63 @@ public void loadPlugin(File jar)
7070
}
7171
}
7272

73-
/**
74-
* Dynamically unload a local plugin through JVM reflections and classloader hacks
75-
*
76-
* @param name Loaded plugin name
77-
*/
78-
public void unloadPlugin(String name)
73+
@Override
74+
public void unloadPlugin(String name) throws PluginNotFoundException
7975
{
80-
// TODO: Implement this
81-
throw new UnsupportedOperationException("TODO");
76+
var pm = Bukkit.getPluginManager();
77+
78+
// 1. Find plugin by name
79+
var plugin = Arrays.stream(pm.getPlugins()).filter(p -> p.getName().equalsIgnoreCase(name)).findFirst()
80+
.orElseThrow(() -> new PluginNotFoundException(name));
81+
82+
// 2. Unload plugin
83+
pm.disablePlugin(plugin);
84+
85+
// 3. Unregister listeners
86+
getPrivateField(pm, "listeners", new TypeToken<Map<Event, SortedSet<RegisteredListener>>>(){})
87+
.ifPresent(listeners -> listeners.values().forEach(set -> set.removeIf(l -> l.getPlugin() == plugin)));
88+
89+
// 4. Remove command from command map
90+
getPrivateField(pm, "commandMap", new TypeToken<SimpleCommandMap>(){}).ifPresent(cmdMap ->
91+
getPrivateField(pm, "knownCommands", new TypeToken<Map<String, Command>>(){}).ifPresent(cmds ->
92+
cmds.values().stream()
93+
.filter(cmd -> cmd instanceof PluginCommand).map(cmd -> (PluginCommand) cmd)
94+
.filter(cmd -> cmd.getPlugin() == plugin).toList().forEach(cmd -> {
95+
cmd.unregister(cmdMap);
96+
cmds.values().removeIf(cmd::equals);
97+
})
98+
)
99+
);
100+
101+
// 5. Remove plugin from list
102+
getPrivateField(pm, "plugins", new TypeToken<List<Plugin>>(){})
103+
.ifPresent(plugins -> plugins.remove(plugin));
104+
105+
// 6. Remove lookup name
106+
getPrivateField(pm, "lookupNames", new TypeToken<Map<String, Plugin>>(){})
107+
.ifPresent(names -> names.remove(plugin.getName()));
108+
109+
// 7. Unload Java classes so that the jar can be deleted on Windows
110+
if (plugin.getClass().getClassLoader() instanceof URLClassLoader cl)
111+
{
112+
if (!setPrivateField(cl, "plugin", null) || !setPrivateField(cl, "pluginInit", null))
113+
System.err.println("Error in setPrivateField, skipping unload");
114+
115+
// Close classloader
116+
try
117+
{
118+
cl.close();
119+
System.gc();
120+
}
121+
catch (IOException e)
122+
{
123+
// File IO error on the hard drive, there are nothing the program can do.
124+
e.printStackTrace();
125+
}
126+
}
82127
}
83128

84-
/**
85-
* Unload then load a plugin
86-
*
87-
* @param name Loaded plugin name
88-
*/
129+
@Override
89130
public void reloadPlugin(String name)
90131
{
91132
// TODO: Implement this

0 commit comments

Comments
 (0)