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:
parent
feee6a0ebe
commit
a17923602f
5 changed files with 109 additions and 84 deletions
|
@ -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" />
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*================================================================================================*/
|
/*================================================================================================*/
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -126,91 +126,106 @@ 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;
|
||||||
if (sd.method == null || sd.command == null) { //Main command not registered, but we have subcommands
|
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> {
|
||||||
sender.sendMessage(sd.helpText);
|
try {
|
||||||
return true;
|
handleCommandAsync(sender, commandline, sd, subcommand);
|
||||||
}
|
} catch (Exception e) {
|
||||||
if (!hasPermission(sender, sd.command, sd.method)) {
|
TBMCCoreAPI.SendException("Command execution failed for sender " + sender + " and message " + commandline, e);
|
||||||
sender.sendMessage("§cYou don't have permission to use this command");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
val params = new ArrayList<Object>(sd.method.getParameterCount());
|
|
||||||
int j = subcommand.length(), pj;
|
|
||||||
Class<?>[] parameterTypes = sd.method.getParameterTypes();
|
|
||||||
if (parameterTypes.length == 0)
|
|
||||||
throw new Exception("No sender parameter for method '" + sd.method + "'");
|
|
||||||
val sendertype = parameterTypes[0];
|
|
||||||
final ChromaGamerBase cg;
|
|
||||||
if (sendertype.isAssignableFrom(sender.getClass()))
|
|
||||||
params.add(sender); //The command either expects a CommandSender or it is a Player, or some other expected type
|
|
||||||
else if (sender instanceof Command2MCSender
|
|
||||||
&& sendertype.isAssignableFrom(((Command2MCSender) sender).getSender().getClass()))
|
|
||||||
params.add(((Command2MCSender) sender).getSender());
|
|
||||||
else if (ChromaGamerBase.class.isAssignableFrom(sendertype)
|
|
||||||
&& sender instanceof Command2MCSender
|
|
||||||
&& (cg = ChromaGamerBase.getFromSender(((Command2MCSender) sender).getSender())) != null
|
|
||||||
&& cg.getClass() == sendertype) //The command expects a user of our system
|
|
||||||
params.add(cg);
|
|
||||||
else {
|
|
||||||
sender.sendMessage("§cYou need to be a " + sendertype.getSimpleName() + " to use this command.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
val paramArr = sd.method.getParameters();
|
|
||||||
for (int i1 = 1; i1 < parameterTypes.length; i1++) {
|
|
||||||
Class<?> cl = parameterTypes[i1];
|
|
||||||
pj = j + 1; //Start index
|
|
||||||
if (pj == commandline.length() + 1) { //No param given
|
|
||||||
if (paramArr[i1].isAnnotationPresent(OptionalArg.class)) {
|
|
||||||
if (cl.isPrimitive())
|
|
||||||
params.add(Defaults.defaultValue(cl));
|
|
||||||
else if (Number.class.isAssignableFrom(cl)
|
|
||||||
|| Number.class.isAssignableFrom(cl))
|
|
||||||
params.add(Defaults.defaultValue(Primitives.unwrap(cl)));
|
|
||||||
else
|
|
||||||
params.add(null);
|
|
||||||
continue; //Fill the remaining params with nulls
|
|
||||||
} else {
|
|
||||||
sender.sendMessage(sd.helpText); //Required param missing
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (paramArr[i1].isVarArgs()) {
|
});
|
||||||
params.add(commandline.substring(j + 1).split(" +"));
|
return true; //We found a method
|
||||||
continue;
|
}
|
||||||
|
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
|
||||||
|
sender.sendMessage(sd.helpText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hasPermission(sender, sd.command, sd.method)) {
|
||||||
|
sender.sendMessage("§cYou don't have permission to use this command");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val params = new ArrayList<Object>(sd.method.getParameterCount());
|
||||||
|
int j = subcommand.length(), pj;
|
||||||
|
Class<?>[] parameterTypes = sd.method.getParameterTypes();
|
||||||
|
if (parameterTypes.length == 0)
|
||||||
|
throw new Exception("No sender parameter for method '" + sd.method + "'");
|
||||||
|
val sendertype = parameterTypes[0];
|
||||||
|
final ChromaGamerBase cg;
|
||||||
|
if (sendertype.isAssignableFrom(sender.getClass()))
|
||||||
|
params.add(sender); //The command either expects a CommandSender or it is a Player, or some other expected type
|
||||||
|
else if (sender instanceof Command2MCSender
|
||||||
|
&& sendertype.isAssignableFrom(((Command2MCSender) sender).getSender().getClass()))
|
||||||
|
params.add(((Command2MCSender) sender).getSender());
|
||||||
|
else if (ChromaGamerBase.class.isAssignableFrom(sendertype)
|
||||||
|
&& sender instanceof Command2MCSender
|
||||||
|
&& (cg = ChromaGamerBase.getFromSender(((Command2MCSender) sender).getSender())) != null
|
||||||
|
&& cg.getClass() == sendertype) //The command expects a user of our system
|
||||||
|
params.add(cg);
|
||||||
|
else {
|
||||||
|
sender.sendMessage("§cYou need to be a " + sendertype.getSimpleName() + " to use this command.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val paramArr = sd.method.getParameters();
|
||||||
|
for (int i1 = 1; i1 < parameterTypes.length; i1++) {
|
||||||
|
Class<?> cl = parameterTypes[i1];
|
||||||
|
pj = j + 1; //Start index
|
||||||
|
if (pj == commandline.length() + 1) { //No param given
|
||||||
|
if (paramArr[i1].isAnnotationPresent(OptionalArg.class)) {
|
||||||
|
if (cl.isPrimitive())
|
||||||
|
params.add(Defaults.defaultValue(cl));
|
||||||
|
else if (Number.class.isAssignableFrom(cl)
|
||||||
|
|| Number.class.isAssignableFrom(cl))
|
||||||
|
params.add(Defaults.defaultValue(Primitives.unwrap(cl)));
|
||||||
|
else
|
||||||
|
params.add(null);
|
||||||
|
continue; //Fill the remaining params with nulls
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(sd.helpText); //Required param missing
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
j = commandline.indexOf(' ', j + 1); //End index
|
|
||||||
if (j == -1 || paramArr[i1].isAnnotationPresent(TextArg.class)) //Last parameter
|
|
||||||
j = commandline.length();
|
|
||||||
String param = commandline.substring(pj, j);
|
|
||||||
if (cl == String.class) {
|
|
||||||
params.add(param);
|
|
||||||
continue;
|
|
||||||
} else if (Number.class.isAssignableFrom(cl) || cl.isPrimitive()) {
|
|
||||||
try {
|
|
||||||
//noinspection unchecked
|
|
||||||
Number n = ThorpeUtils.convertNumber(NumberFormat.getInstance().parse(param), (Class<? extends Number>) cl);
|
|
||||||
params.add(n);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
sender.sendMessage("§c'" + param + "' is not a number.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
val conv = paramConverters.get(cl);
|
|
||||||
if (conv == null)
|
|
||||||
throw new Exception("No suitable converter found for parameter type '" + cl.getCanonicalName() + "' for command '" + sd.method.toString() + "'");
|
|
||||||
val cparam = conv.converter.apply(param);
|
|
||||||
if (cparam == null) {
|
|
||||||
sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
params.add(cparam);
|
|
||||||
}
|
}
|
||||||
|
if (paramArr[i1].isVarArgs()) {
|
||||||
|
params.add(commandline.substring(j + 1).split(" +"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
j = commandline.indexOf(' ', j + 1); //End index
|
||||||
|
if (j == -1 || paramArr[i1].isAnnotationPresent(TextArg.class)) //Last parameter
|
||||||
|
j = commandline.length();
|
||||||
|
String param = commandline.substring(pj, j);
|
||||||
|
if (cl == String.class) {
|
||||||
|
params.add(param);
|
||||||
|
continue;
|
||||||
|
} else if (Number.class.isAssignableFrom(cl) || cl.isPrimitive()) {
|
||||||
|
try {
|
||||||
|
//noinspection unchecked
|
||||||
|
Number n = ThorpeUtils.convertNumber(NumberFormat.getInstance().parse(param), (Class<? extends Number>) cl);
|
||||||
|
params.add(n);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
sender.sendMessage("§c'" + param + "' is not a number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
val conv = paramConverters.get(cl);
|
||||||
|
if (conv == null)
|
||||||
|
throw new Exception("No suitable converter found for parameter type '" + cl.getCanonicalName() + "' for command '" + sd.method.toString() + "'");
|
||||||
|
val cparam = conv.converter.apply(param);
|
||||||
|
if (cparam == null) {
|
||||||
|
sender.sendMessage(conv.errormsg); //Param conversion failed - ex. plugin not found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
Loading…
Reference in a new issue