#Thread Help

1 messages · Page 1 of 1 (latest)

winged trench
#
        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)```
#

Started working with threads to do timing and got this stacktrace. Any help demystifying it?

wheat fable
#

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.

winged trench
#

Oh yeah, didn't think about that when I started doing threads...

#

How might I be able to do this then?

wheat fable
#

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.

winged trench
wheat fable
#

Well I think you can use the bukkit scheduler to make it synchronous again.
Bukkit#getScheduler()#runTaskLater()

winged trench
#

I can go back to that if there isn't a better option

wheat fable
#

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.

winged trench
#

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

wheat fable
#

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

winged trench
winged trench
wheat fable
#

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.

winged trench
winged trench
wheat fable
#

Well what is it that you are trying to do? I'm not getting the full picture.

winged trench
#

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;
    }
}```
wheat fable
#

Oh, so like a configurable daily command execution or something similar?

winged trench
#

ye

wheat fable
#

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.

winged trench
wheat fable
#

Yea, the TPS would have to be pretty low for it to be that far behind.

winged trench
#

yeah thats fine then, as instead of the tps being really low making it like an hour off, it makes it a minute off

winged trench
winged trench
#

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)```
wheat fable
#

Yea, I'm trying to see what the best approach is for making that method run synchronously.

winged trench
#

mk thanks

winged trench
wheat fable
#

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?

winged trench
#

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?

wheat fable
#

Well the bukkit scheduler has a method called #runTaskTimer which will do the same thing as your scheduled executor service.

winged trench
#

I know, I was using that previously

#

I'd rather use this if I can get it to work

#

Thread Help

winged trench
#

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); }); }

wheat fable
#

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)

winged trench
#

I think because when .getScheduler() is called it returns a Scheduler on main threadd or something

#

Doesn't really matter though, since it works