mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-12-04 16:44:24 +01:00
fixes issue with bbox reload command
https://github.com/BentoBoxWorld/BentoBox/issues/731 Issue was that classes were not being fully removed from class loaders and commands needed to be unregistered from Bukkit. For the latter, reflection was required to obtain the knownCommand map and change it because there is no Bukkit API to unregister commands.
This commit is contained in:
parent
9da79f3bc2
commit
9b8c8f6bc8
@ -21,6 +21,7 @@ import java.net.URLClassLoader;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads addons and sets up permissions
|
* Loads addons and sets up permissions
|
||||||
@ -155,4 +156,11 @@ public class AddonClassLoader extends URLClassLoader {
|
|||||||
return addon;
|
return addon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return class list
|
||||||
|
*/
|
||||||
|
public Set<String> getClasses() {
|
||||||
|
return classes.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package world.bentobox.bentobox.commands;
|
package world.bentobox.bentobox.commands;
|
||||||
|
|
||||||
import world.bentobox.bentobox.api.addons.Addon;
|
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
|
||||||
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
|
||||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
|
||||||
import world.bentobox.bentobox.api.user.User;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
|
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
|
||||||
|
import world.bentobox.bentobox.api.user.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads settings, addons and localization.
|
* Reloads settings, addons and localization.
|
||||||
*
|
*
|
||||||
@ -36,7 +34,7 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
|
|||||||
@Override
|
@Override
|
||||||
public boolean execute(User user, String label, List<String> args) {
|
public boolean execute(User user, String label, List<String> args) {
|
||||||
if (args.isEmpty()) {
|
if (args.isEmpty()) {
|
||||||
this.askConfirmation(user, () -> {
|
this.askConfirmation(user, user.getTranslation("commands.bentobox.reload.warning"), () -> {
|
||||||
// Reload settings
|
// Reload settings
|
||||||
getPlugin().loadSettings();
|
getPlugin().loadSettings();
|
||||||
user.sendMessage("commands.bentobox.reload.settings-reloaded");
|
user.sendMessage("commands.bentobox.reload.settings-reloaded");
|
||||||
@ -49,18 +47,6 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
|
|||||||
getPlugin().getLocalesManager().reloadLanguages();
|
getPlugin().getLocalesManager().reloadLanguages();
|
||||||
user.sendMessage("commands.bentobox.reload.locales-reloaded");
|
user.sendMessage("commands.bentobox.reload.locales-reloaded");
|
||||||
});
|
});
|
||||||
} else if (args.size() == 1) {
|
|
||||||
Optional<Addon> addon = getPlugin().getAddonsManager().getAddonByName(args.get(0));
|
|
||||||
if (!addon.isPresent()) {
|
|
||||||
user.sendMessage("commands.bentobox.reload.unknown-addon");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.askConfirmation(user, () -> {
|
|
||||||
user.sendMessage("commands.bentobox.reload.addon", TextVariables.NAME, addon.get().getDescription().getName());
|
|
||||||
addon.ifPresent(getPlugin().getAddonsManager()::reloadAddon);
|
|
||||||
user.sendMessage("commands.bentobox.reload.addon-reloaded", TextVariables.NAME, addon.get().getDescription().getName());
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
showHelp(this, user);
|
showHelp(this, user);
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,9 @@
|
|||||||
package world.bentobox.bentobox.managers;
|
package world.bentobox.bentobox.managers;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
|
||||||
import world.bentobox.bentobox.api.addons.Addon;
|
|
||||||
import world.bentobox.bentobox.api.addons.AddonClassLoader;
|
|
||||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
|
||||||
import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonFormatException;
|
|
||||||
import world.bentobox.bentobox.api.configuration.ConfigObject;
|
|
||||||
import world.bentobox.bentobox.api.events.addon.AddonEvent;
|
|
||||||
import world.bentobox.bentobox.database.objects.DataObject;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -35,6 +17,26 @@ import java.util.jar.JarEntry;
|
|||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
|
import world.bentobox.bentobox.api.addons.Addon;
|
||||||
|
import world.bentobox.bentobox.api.addons.Addon.State;
|
||||||
|
import world.bentobox.bentobox.api.addons.AddonClassLoader;
|
||||||
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||||
|
import world.bentobox.bentobox.api.addons.exceptions.InvalidAddonFormatException;
|
||||||
|
import world.bentobox.bentobox.api.configuration.ConfigObject;
|
||||||
|
import world.bentobox.bentobox.api.events.addon.AddonEvent;
|
||||||
|
import world.bentobox.bentobox.commands.BentoBoxCommand;
|
||||||
|
import world.bentobox.bentobox.database.objects.DataObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tastybento, ComminQ
|
* @author tastybento, ComminQ
|
||||||
*/
|
*/
|
||||||
@ -213,19 +215,29 @@ public class AddonsManager {
|
|||||||
*/
|
*/
|
||||||
public void reloadAddons() {
|
public void reloadAddons() {
|
||||||
disableAddons();
|
disableAddons();
|
||||||
|
// Reload BentoBox commands
|
||||||
|
new BentoBoxCommand();
|
||||||
loadAddons();
|
loadAddons();
|
||||||
enableAddons();
|
enableAddons();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads one addon
|
* Disable all the enabled addons
|
||||||
* @param addon - addon
|
|
||||||
*/
|
*/
|
||||||
public void reloadAddon(Addon addon) {
|
public void disableAddons() {
|
||||||
Path p = addon.getFile().toPath();
|
if (!getEnabledAddons().isEmpty()) {
|
||||||
disable(addon);
|
plugin.log("Disabling addons...");
|
||||||
loadAddon(p.toFile());
|
// Disable addons
|
||||||
enableAddon(addon);
|
getEnabledAddons().forEach(this::disable);
|
||||||
|
plugin.log("Addons successfully disabled.");
|
||||||
|
}
|
||||||
|
// Unregister all commands
|
||||||
|
plugin.getCommandsManager().unregisterCommands();
|
||||||
|
// Clear all maps
|
||||||
|
listeners.clear();
|
||||||
|
addons.clear();
|
||||||
|
loaders.clear();
|
||||||
|
classes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,23 +265,6 @@ public class AddonsManager {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable all the enabled addons
|
|
||||||
*/
|
|
||||||
public void disableAddons() {
|
|
||||||
if (!getEnabledAddons().isEmpty()) {
|
|
||||||
plugin.log("Disabling addons...");
|
|
||||||
// Disable addons
|
|
||||||
getEnabledAddons().forEach(this::disable);
|
|
||||||
plugin.log("Addons successfully disabled.");
|
|
||||||
}
|
|
||||||
// Clear all maps
|
|
||||||
listeners.clear();
|
|
||||||
addons.clear();
|
|
||||||
loaders.clear();
|
|
||||||
classes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public List<Addon> getAddons() {
|
public List<Addon> getAddons() {
|
||||||
return addons;
|
return addons;
|
||||||
@ -422,11 +417,9 @@ public class AddonsManager {
|
|||||||
}
|
}
|
||||||
// Clear loaders
|
// Clear loaders
|
||||||
if (loaders.containsKey(addon)) {
|
if (loaders.containsKey(addon)) {
|
||||||
try {
|
loaders.get(addon).getClasses().forEach(classes::remove);
|
||||||
loaders.get(addon).close();
|
addon.setState(State.DISABLED);
|
||||||
} catch (IOException ignore) {
|
loaders.remove(addon);
|
||||||
// Nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove it from the addons list
|
// Remove it from the addons list
|
||||||
|
@ -7,16 +7,19 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandMap;
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.SimpleCommandMap;
|
||||||
import org.eclipse.jdt.annotation.NonNull;
|
import org.eclipse.jdt.annotation.NonNull;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||||
|
|
||||||
public class CommandsManager {
|
public class CommandsManager {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private Map<@NonNull String, @NonNull CompositeCommand> commands = new HashMap<>();
|
private Map<@NonNull String, @NonNull CompositeCommand> commands = new HashMap<>();
|
||||||
|
private SimpleCommandMap commandMap;
|
||||||
|
|
||||||
public void registerCommand(@NonNull CompositeCommand command) {
|
public void registerCommand(@NonNull CompositeCommand command) {
|
||||||
commands.put(command.getLabel(), command);
|
commands.put(command.getLabel(), command);
|
||||||
@ -24,20 +27,39 @@ public class CommandsManager {
|
|||||||
try{
|
try{
|
||||||
Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
|
Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
|
||||||
commandMapField.setAccessible(true);
|
commandMapField.setAccessible(true);
|
||||||
CommandMap commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
|
commandMap = (SimpleCommandMap) commandMapField.get(Bukkit.getServer());
|
||||||
|
|
||||||
String commandPrefix = "bentobox";
|
String commandPrefix = "bentobox";
|
||||||
if (command.getAddon() != null) {
|
if (command.getAddon() != null) {
|
||||||
commandPrefix = command.getAddon().getDescription().getName().toLowerCase(Locale.ENGLISH);
|
commandPrefix = command.getAddon().getDescription().getName().toLowerCase(Locale.ENGLISH);
|
||||||
}
|
}
|
||||||
|
if (!commandMap.register(commandPrefix, command)) {
|
||||||
commandMap.register(commandPrefix, command);
|
BentoBox.getInstance().logError("Failed to register command " + commandPrefix + " " + command.getLabel());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(Exception exception){
|
catch(Exception exception){
|
||||||
Bukkit.getLogger().severe("Bukkit server commandMap method is not there! This means no commands can be registered!");
|
Bukkit.getLogger().severe("Bukkit server commandMap method is not there! This means no commands can be registered!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters all BentoBox registered commands with Bukkit
|
||||||
|
*/
|
||||||
|
public void unregisterCommands() {
|
||||||
|
// Use reflection to obtain the knownCommands in the commandMap
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Command> knownCommands = (Map<String, Command>) commandMap.getClass().getMethod("getKnownCommands").invoke(commandMap);
|
||||||
|
knownCommands.values().removeIf(commands.values()::contains);
|
||||||
|
// Not sure if this is needed, but it clears out all references
|
||||||
|
commands.values().forEach(c -> c.unregister(commandMap));
|
||||||
|
// Zap everything
|
||||||
|
commands.clear();
|
||||||
|
} catch(Exception e){
|
||||||
|
Bukkit.getLogger().severe("Known commands reflection was not possible, BentoBox is now unstable, so restart server!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to get a registered command.
|
* Try to get a registered command.
|
||||||
* @param command - command string
|
* @param command - command string
|
||||||
|
@ -305,13 +305,13 @@ commands:
|
|||||||
about:
|
about:
|
||||||
description: "display copyright and license info"
|
description: "display copyright and license info"
|
||||||
reload:
|
reload:
|
||||||
parameters: "[addon]"
|
description: "reloads BentoBox and all addons, settings and locales"
|
||||||
description: "reloads all addons, settings and locales or reloads an individual addon"
|
|
||||||
locales-reloaded: "&2Languages reloaded."
|
locales-reloaded: "&2Languages reloaded."
|
||||||
addons-reloaded: "&2Addons reloaded."
|
addons-reloaded: "&2Addons reloaded."
|
||||||
settings-reloaded: "&2Settings reloaded."
|
settings-reloaded: "&2Settings reloaded."
|
||||||
addon: "&6Reloading &b[name]&2."
|
addon: "&6Reloading &b[name]&2."
|
||||||
addon-reloaded: "&b[name] &2reloaded."
|
addon-reloaded: "&b[name] &2reloaded."
|
||||||
|
warning: "&cWarning: Reloading may cause instability, so if you see errors afterwards, restart the server"
|
||||||
unknown-addon: "&2Unknown addon!"
|
unknown-addon: "&2Unknown addon!"
|
||||||
version:
|
version:
|
||||||
plugin-version: "&2BentoBox version: &3[version]"
|
plugin-version: "&2BentoBox version: &3[version]"
|
||||||
|
@ -300,8 +300,7 @@ commands:
|
|||||||
about:
|
about:
|
||||||
description: "mostra le info riguardo copyright e licenza"
|
description: "mostra le info riguardo copyright e licenza"
|
||||||
reload:
|
reload:
|
||||||
parameters: "[addon]"
|
description: "ricarica gli addons, le impostazioni, i locali"
|
||||||
description: "ricarica gli addons, le impostazioni, i locali o ricarica un singolo addon"
|
|
||||||
locales-reloaded: "&2Linguaggi ricaricati."
|
locales-reloaded: "&2Linguaggi ricaricati."
|
||||||
addons-reloaded: "&2Addons ricaricati."
|
addons-reloaded: "&2Addons ricaricati."
|
||||||
settings-reloaded: "&2Impostazioni ricaricate."
|
settings-reloaded: "&2Impostazioni ricaricate."
|
||||||
|
@ -313,7 +313,6 @@ commands:
|
|||||||
locales-reloaded: "&2Valodas faili pārlādēti."
|
locales-reloaded: "&2Valodas faili pārlādēti."
|
||||||
addons-reloaded: "&2Papildinājumu pārlādēti."
|
addons-reloaded: "&2Papildinājumu pārlādēti."
|
||||||
settings-reloaded: "&2Iestatījumi pārlādēti."
|
settings-reloaded: "&2Iestatījumi pārlādēti."
|
||||||
parameters: '[addon]'
|
|
||||||
addon: '&6Pārlādē &b[name]&2.'
|
addon: '&6Pārlādē &b[name]&2.'
|
||||||
addon-reloaded: '&b[name] &2pārlādēts.'
|
addon-reloaded: '&b[name] &2pārlādēts.'
|
||||||
unknown-addon: '&2Nezināms papildinājums!'
|
unknown-addon: '&2Nezināms papildinājums!'
|
||||||
|
@ -29,7 +29,6 @@ import org.powermock.reflect.Whitebox;
|
|||||||
|
|
||||||
import world.bentobox.bentobox.BentoBox;
|
import world.bentobox.bentobox.BentoBox;
|
||||||
import world.bentobox.bentobox.api.addons.Addon;
|
import world.bentobox.bentobox.api.addons.Addon;
|
||||||
import world.bentobox.bentobox.api.addons.AddonDescription;
|
|
||||||
import world.bentobox.bentobox.database.objects.DataObject;
|
import world.bentobox.bentobox.database.objects.DataObject;
|
||||||
|
|
||||||
@RunWith(PowerMockRunner.class)
|
@RunWith(PowerMockRunner.class)
|
||||||
@ -105,23 +104,6 @@ public class AddonsManagerTest {
|
|||||||
verify(plugin, never()).log("Disabling addons...");
|
verify(plugin, never()).log("Disabling addons...");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test method for {@link world.bentobox.bentobox.managers.AddonsManager#reloadAddon(world.bentobox.bentobox.api.addons.Addon)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testReloadAddon() {
|
|
||||||
Addon addon = mock(Addon.class);
|
|
||||||
when(addon.isEnabled()).thenReturn(true);
|
|
||||||
File f = new File(plugin.getDataFolder(), "addons");
|
|
||||||
File file = new File(f, "addon.jar");
|
|
||||||
when(addon.getFile()).thenReturn(file);
|
|
||||||
AddonDescription desc = new AddonDescription.Builder("main", "addon-name", "1.0").build();
|
|
||||||
when(addon.getDescription()).thenReturn(desc);
|
|
||||||
am.reloadAddon(addon);
|
|
||||||
verify(plugin).log("Disabling addon-name...");
|
|
||||||
verify(addon).onDisable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method for {@link world.bentobox.bentobox.managers.AddonsManager#getAddonByName(java.lang.String)}.
|
* Test method for {@link world.bentobox.bentobox.managers.AddonsManager#getAddonByName(java.lang.String)}.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user