Slow startup debug, async command handling

The command argument processing and permission checking is done asynchronously, mainly because LuckPerms (rightfully) complains that loading player data (for unconnected DC users) should not be done on the main thread.
The actual command execution is still done on the main thread.
This commit is contained in:
Norbi Peti 2019-08-23 00:50:36 +02:00
parent feee6a0ebe
commit a17923602f
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
5 changed files with 109 additions and 84 deletions

View file

@ -12,6 +12,8 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" /> <orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="module" module-name="ButtonCore (1) (com.github.TBMCPlugins.ButtonCore)" />
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" /> <orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" /> <orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" /> <orderEntry type="library" name="Maven: org.javassist:javassist:3.20.0-GA" level="project" />

View file

@ -53,11 +53,15 @@ public class RandomTP extends TBMCCommandBase
public void onEnable(Component component) public void onEnable(Component component)
{ {
System.out.println("Adding command");
TBMCChatAPI.AddCommand(component, this); TBMCChatAPI.AddCommand(component, this);
System.out.println("Getting world");
world = Bukkit.getWorld("World"); world = Bukkit.getWorld("World");
System.out.println("Getting border");
border = world.getWorldBorder(); border = world.getWorldBorder();
newLocation(); System.out.println("Getting new location");
System.out.println("Success: "+newLocation()); //TODO: It takes 10-30 seconds to find a location (newLocation() was there)
} }
/*================================================================================================*/ /*================================================================================================*/

View file

@ -19,7 +19,6 @@ import org.bukkit.event.player.PlayerQuitEvent;
public class RestartComponent extends Component<MainPlugin> implements Listener { public class RestartComponent extends Component<MainPlugin> implements Listener {
@Override @Override
public void enable() { public void enable() {
//TODO: Permissions for the commands
registerCommand(new ScheduledRestartCommand(this)); registerCommand(new ScheduledRestartCommand(this));
TBMCChatAPI.AddCommand(this, new PrimeRestartCommand(this)); TBMCChatAPI.AddCommand(this, new PrimeRestartCommand(this));
registerListener(this); registerListener(this);

View file

@ -131,10 +131,15 @@ public abstract class Component<TP extends JavaPlugin> {
throw new UnregisteredComponentException(component); throw new UnregisteredComponentException(component);
if (component.enabled == enabled) return; //Don't do anything if (component.enabled == enabled) return; //Don't do anything
if (component.enabled = enabled) { if (component.enabled = enabled) {
//System.out.println("Updating config for "+component.getClassName());
updateConfig(component.getPlugin(), component); updateConfig(component.getPlugin(), component);
//System.out.println("Enabling "+component.getClassName());
component.enable(); component.enable();
if (ButtonPlugin.configGenAllowed(component)) if (ButtonPlugin.configGenAllowed(component)) {
//System.out.println("Pregenning config for "+component.getClassName());
IHaveConfig.pregenConfig(component, null); IHaveConfig.pregenConfig(component, null);
}
//System.out.println("Done enabling "+component.getClassName());
} else { } else {
component.disable(); component.disable();
component.plugin.saveConfig(); component.plugin.saveConfig();

View file

@ -126,18 +126,32 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
paramConverters.put(cl, new ParamConverter<>(converter, errormsg)); paramConverters.put(cl, new ParamConverter<>(converter, errormsg));
} }
public boolean handleCommand(TP sender, String commandline) throws Exception { public boolean handleCommand(TP sender, String commandline) {
for (int i = commandline.length(); i != -1; i = commandline.lastIndexOf(' ', i - 1)) { for (int i = commandline.length(); i != -1; i = commandline.lastIndexOf(' ', i - 1)) {
String subcommand = commandline.substring(0, i).toLowerCase(); String subcommand = commandline.substring(0, i).toLowerCase();
SubcommandData<TC> sd = subcommands.get(subcommand); //O(1) SubcommandData<TC> sd = subcommands.get(subcommand); //O(1)
if (sd == null) continue; if (sd == null) continue;
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> {
try {
handleCommandAsync(sender, commandline, sd, subcommand);
} catch (Exception e) {
TBMCCoreAPI.SendException("Command execution failed for sender " + sender + " and message " + commandline, e);
}
});
return true; //We found a method
}
return false;
}
//Needed because permission checking may load the (perhaps offline) sender's file which is disallowed on the main thread
public void handleCommandAsync(TP sender, String commandline, SubcommandData<TC> sd, String subcommand) throws Exception {
if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands
sender.sendMessage(sd.helpText); sender.sendMessage(sd.helpText);
return true; return;
} }
if (!hasPermission(sender, sd.command, sd.method)) { if (!hasPermission(sender, sd.command, sd.method)) {
sender.sendMessage("§cYou don't have permission to use this command"); sender.sendMessage("§cYou don't have permission to use this command");
return true; return;
} }
val params = new ArrayList<Object>(sd.method.getParameterCount()); val params = new ArrayList<Object>(sd.method.getParameterCount());
int j = subcommand.length(), pj; int j = subcommand.length(), pj;
@ -158,7 +172,7 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
params.add(cg); params.add(cg);
else { else {
sender.sendMessage("§cYou need to be a " + sendertype.getSimpleName() + " to use this command."); sender.sendMessage("§cYou need to be a " + sendertype.getSimpleName() + " to use this command.");
return true; return;
} }
val paramArr = sd.method.getParameters(); val paramArr = sd.method.getParameters();
for (int i1 = 1; i1 < parameterTypes.length; i1++) { for (int i1 = 1; i1 < parameterTypes.length; i1++) {
@ -176,7 +190,7 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
continue; //Fill the remaining params with nulls continue; //Fill the remaining params with nulls
} else { } else {
sender.sendMessage(sd.helpText); //Required param missing sender.sendMessage(sd.helpText); //Required param missing
return true; return;
} }
} }
if (paramArr[i1].isVarArgs()) { if (paramArr[i1].isVarArgs()) {
@ -197,7 +211,7 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
params.add(n); params.add(n);
} catch (ParseException e) { } catch (ParseException e) {
sender.sendMessage("§c'" + param + "' is not a number."); sender.sendMessage("§c'" + param + "' is not a number.");
return true; return;
} }
continue; continue;
} }
@ -207,10 +221,11 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
val cparam = conv.converter.apply(param); val cparam = conv.converter.apply(param);
if (cparam == null) { if (cparam == null) {
sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found
return true; return;
} }
params.add(cparam); params.add(cparam);
} }
Bukkit.getScheduler().runTask(MainPlugin.Instance, () -> {
try { try {
val ret = sd.method.invoke(sd.command, params.toArray()); //I FORGOT TO TURN IT INTO AN ARRAY (for a long time) val ret = sd.method.invoke(sd.command, params.toArray()); //I FORGOT TO TURN IT INTO AN ARRAY (for a long time)
if (ret instanceof Boolean) { if (ret instanceof Boolean) {
@ -218,12 +233,12 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
sender.sendMessage(sd.helpText); sender.sendMessage(sd.helpText);
} else if (ret != null) } else if (ret != null)
throw new Exception("Wrong return type! Must return a boolean or void. Return value: " + ret); throw new Exception("Wrong return type! Must return a boolean or void. Return value: " + ret);
return true; //We found a method
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
TBMCCoreAPI.SendException("An error occurred in a command handler!", e.getCause()); TBMCCoreAPI.SendException("An error occurred in a command handler!", e.getCause());
} catch (Exception e) {
TBMCCoreAPI.SendException("Command handling failed for sender " + sender + " and subcommand " + subcommand, e);
} }
} });
return false; //Didn't handle
} //TODO: Add to the help } //TODO: Add to the help
public abstract void registerCommand(TC command); public abstract void registerCommand(TC command);