java.lang.Throwable: null
at org.spigotmc.AsyncCatcher.catchOp(AsyncCatcher.java:15)
at org.bukkit.craftbukkit.v1_19_R1.CraftServer.dispatchCommand(CraftServer.java:916)
at net.numra.scripter.TimedCommand.run(TimedCommand.java:28)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.lang.Thread.run(Thread.java:833)```
#Thread Help
1 messages · Page 1 of 1 (latest)
Started working with threads to do timing and got this stacktrace. Any help demystifying it?
Are you calling Bukkit#dispatchCommand() on an async thread? If so, you can't because it's not a thread safe operation. Only a handful of things are thread safe in bukkit.
Oh yeah, didn't think about that when I started doing threads...
How might I be able to do this then?
Just run your code synchronously. Either that or try to delay it by a tick or two so that things don't try to execute all at once on one tick.
It's async so I can use a schedular thread, and It's only doing 1 or 2 things so I dont see how delaying it would help
Well I think you can use the bukkit scheduler to make it synchronous again.
Bukkit#getScheduler()#runTaskLater()
Tried that but I'd rather not be dependent on TPS
I can go back to that if there isn't a better option
Well that's the tricky part. If your relying on in game commands, then ticks may be a better bet. If you are working on an independent system, then milliseconds are the way to go. It just depends on what you are trying to accomplish.
In game command is the result, I'd rather not have it dependent on TPS for consistency, especially since these are typically going to be longer waits in which small inaccuracies may add up. If there is a way to communicate the command back to the main thread, that'd probably be better
Well commands are indirectly linked to server TPS. If TPS is low, commands may be delayed until the server can catch up.
Could you elaborate on this a little more?
If there is a way to communicate the command back to the main thread
Well I don't know much how threads work, so basically just run the command from the other thread or have the other thread let the main thread know when to.
Oh and what you said about tps, thats fine I understand that. I just don't want it to be severely effected if there is low TPS in the middle or something like that, (plus its really only dependent on a few ticks instead of a large number of them)
I believe that commands are run on the main thread by default. I'm not sure if they can be altered to run on another thread. The output of the command can call asynchronous methods, but the command itself is run synchronously.
Realistically, it shouldn't be a big issue as server commands are pretty light in terms of computation. Unless your command is trying to access a bunch of files all at once or made to compute large datasets on the same thread, then you won't run into a performance issue.
My problem isn't with me causing TPS, its something else causing low TPS that interrupts my timing
Is there a way to do the latter of what I said earlier?
Well what is it that you are trying to do? I'm not getting the full picture.
Here I'll send the class
package net.numra.scripter;
import org.bukkit.Server;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class TimedCommand {
private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private Scripter plugin;
private String command;
private long time;
private ScheduledFuture<?> future;
public void start() {
future = executor.scheduleAtFixedRate(this::run, time, time, TimeUnit.SECONDS);
}
public void cancel() {
future.cancel(false);
}
private void run() {
Server server = plugin.getServer();
server.dispatchCommand(server.getConsoleSender(), command);
plugin.getLogger().info("Ran scheduled command: " + command);
}
public TimedCommand(String commandStr, int time, Scripter plugin) {
this.plugin = plugin;
this.command = commandStr;
this.time = time;
}
}```
Oh, so like a configurable daily command execution or something similar?
ye
Hmm, if you are able to make the #run method be called synchronously then you should be fine. I think that can be achieved with the synchronized keyword. I guess I'm still a little confused on what you meant by other things causing low TPS.
Since you are using an ExecutorService instead of the Bukkit system, you should have much more accurate timings. However, since the server is based off of ticks, it could be behind before it executes the commands you send it. Meaning that if your commands are scheduled to execute at midnight, but the server is behind on its tick, then your command may not execute exactly at midnight. It may execute a minute later or at the worst 5 minutes later if the server is really lagging behind.
Well that would have to be terrible tps for even a minute delay, no?
Yea, the TPS would have to be pretty low for it to be that far behind.
yeah thats fine then, as instead of the tps being really low making it like an hour off, it makes it a minute off
I'll let you know if this works
No still didn't work
I'll send the new stacktrace
[01:06:26 ERROR]: Thread pool-11-thread-1 failed main thread check: command dispatch
java.lang.Throwable: null
at org.spigotmc.AsyncCatcher.catchOp(AsyncCatcher.java:15)
at org.bukkit.craftbukkit.v1_19_R1.CraftServer.dispatchCommand(CraftServer.java:916)
at net.numra.scripter.TimedCommand.run(TimedCommand.java:29)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.lang.Thread.run(Thread.java:833)```
You still here?
Yea, I'm trying to see what the best approach is for making that method run synchronously.
mk thanks
There is this but I don't know how efficient it would be for something like this https://stackoverflow.com/a/16268778
Same sorta thing here: https://stackoverflow.com/a/27955299
But they both seem like I'd need loops which I don't know how good of an Idea that is
Dealing with multiple threads isn't exactly my strong suit, but I'm not sure if that would be an ideal approach. That may work for a normal java program, but not for a spigot plugin. I'm sure there's a simple way to do it too.
Are you sure the Bukkit scheduler isn't the right approach here?
You think I might be able to do some witchcraft like make a bukkitrunnable that all it does is run the command?
so I just execute the BukkitRunnable from the secondary thread and maybe bukkit runs it on the main?
Well the bukkit scheduler has a method called #runTaskTimer which will do the same thing as your scheduled executor service.
I know, I was using that previously
I'd rather use this if I can get it to work
Thread Help
Can't do this, it would need the scheduler on the main thread it would seem
I'm going to try it anyway however
wait that actually worked
@wheat fable thanks for helping me, I guess. DDidn't expect that to work lol
Changed the run function to this: java private void run() { Bukkit.getScheduler().runTask(plugin, l -> { Server server = plugin.getServer(); server.dispatchCommand(server.getConsoleSender(), command); plugin.getLogger().info("Ran scheduled command: " + command); }); }
Yea, I think the bukkit scheduler is able to take things to the main thread. Don't know how it does it, but it's what it does. (Unless you use the async methods ofc)