@@ -70,22 +70,63 @@ public void loadPlugin(File jar)
70
70
}
71
71
}
72
72
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
79
75
{
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
+ }
82
127
}
83
128
84
- /**
85
- * Unload then load a plugin
86
- *
87
- * @param name Loaded plugin name
88
- */
129
+ @ Override
89
130
public void reloadPlugin (String name )
90
131
{
91
132
// TODO: Implement this
0 commit comments