Sponge Build: 1000
Forge Build: 1577
Java Version: 1.8.0_65
Plugin Source: BitBucket
Hi!
I’m developing a Hunger Games plugin and I’m currently struggling with the scheduler to run a countdown task but I don’t want it to be call after the countdown finished.
LobbyCountdownTask taskToExecute = new LobbyCountdownTask(plugin, 10); Task t = taskBuilder.execute(taskToExecute) .interval(1, TimeUnit.SECONDS) .name("HGPlugin - LobbyCountdownTask").submit(plugin);
As you can see, the LobbyCountDownTask should be call only for ten times + one to start a new scheduler task. I was previously using Thread but it wasn’t synchronous with the game, so I couldn’t use the API.
1 Like
After the last execution of your task, put this line:
Task t = myTask;
t.cancel();
Should work
Oh yes, this is obviously the answer. I’ll try this in a minute, thanks a lot.
I should be very tired yesterday…
Make TaskToExecute a task consumer instead of a runnable, then you have access to the task, and can cancel it after it’s counter reaches a value.
Do you have a simple example please? I tried to find what you’re meaning on Google but I didn’t. I don’t know where the timer is share with the Task… By passing arguments? If it is, how can I instantiate a static plugin object ?
EDIT: I can cancel only one task because it’s call in an other task which is repeated. So the task called by the command can’t be stop by task.cancel();
… Here’s the command code:
plugin.setGameStarted(false);
LobbyCountdownTask taskToExecute = new LobbyCountdownTask(plugin, 10);
Task t = taskBuilder.execute(taskToExecute)
.interval(1, TimeUnit.SECONDS)
.name("HGPlugin - LobbyCountdownTask").submit(plugin);
plugin.getLogger().info(t.getName(), " has been called");
if(taskToExecute.isFinish()){
t.cancel();
}
And the code of this called task:
if(seconds > 0) {
game.getServer().getBroadcastChannel().send(Text.of(
TextColors.GOLD, seconds/60, ":", seconds,
TextColors.GREEN, " left to prepare yourself."));
seconds--;
return;
} else if(!gameStartCountdownStarted){
Task t = taskBuilder.execute(taskToExecute)
.interval(1, TimeUnit.SECONDS)
.name("HGPlugin - GameStartCountdownTask").submit(plugin);
if(taskToExecute.isFinish()) {
t.cancel();
this.isFinish = Boolean.TRUE;
} else {
plugin.setPreparationEnded(true);
game.getServer().getBroadcastChannel().send(Text.of(
TextColors.GREEN, "Preparation time ended!"));
plugin.getLogger().info(t.getName() + " has been called");
gameStartCountdownStarted = Boolean.TRUE;
}
}
It makes it hard to provide an example that matches your code, if you arn’t providing where the task is defined, but I’ll try to show an example of what I mean.
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameStartingServerEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.text.Text;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@Plugin(id="au.id.rleach.forumcountdown", name="Forum Countdown", version = "0.0.1")
public class ForumCountdown {
@Listener
public void onStart(GameStartingServerEvent init){
Sponge.getCommandManager()
.register(
this,
CommandSpec.builder()
.description(Text.of("Broadcasts a 60 second timer"))
.executor(new TimerCommandExecutor())
.build(),
"timer"
);
}
private class TimerCommandExecutor implements CommandExecutor {
@Override
public CommandResult execute(CommandSource commandSource, CommandContext commandContext) throws CommandException {
Sponge .getScheduler()
.createTaskBuilder()
.interval(1, TimeUnit.SECONDS)
.delay(1, TimeUnit.SECONDS)
.execute(new ForumTimerTask())
.submit(ForumCountdown.this);
return CommandResult.success();
}
}
private static class ForumTimerTask implements Consumer<Task> {
private int seconds = 60;
@Override
public void accept(Task task) {
seconds--;
Sponge .getGame()
.getServer()
.getBroadcastChannel()
.send(Text.of("Remaining Time: "+seconds+"s"));
if(seconds < 1) {
task.cancel();
}
}
}
}
2 Likes
I understand your example ans my code is working well now but I’m just wondering how the Task task
is given to the accept
method.
Does the Sponge Scheduler API do it for me when it calls the accept
method?
So “.submit();” doesn’t seem to exist in 4.2.0. Has it been replaced with something else? I’ve tried running this code without it, but it’s not functional.
Not sure what you mean, it’s right here.
Perhaps you’re forgetting to provide the plugin instance?
@pie_flavor Oh. I thought I was implementing it correctly, so I assumed it was no longer implemented in 4.2.0 when my code wouldn’t compile. The name of my main class is “Main”, but when I entered that in the “.submit(Main.this)” I got the error “The method submit(Main) is undefined for the type Main.ForumTimerTask”
Could you post full code?
Also, if you click ‘reply’ on someone’s message, the message will auto-notify them, no @-ing required.
1 Like
My code is very identical to the example provided by @ryantheleach. I’ve also changed the name of my class from “Main” to “MainTest”.
package Main;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.logging.Logger;
import org.spongepowered.api.Game;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.args.GenericArguments;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.config.DefaultConfig;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameInitializationEvent;
import org.spongepowered.api.event.game.state.GamePostInitializationEvent;
import org.spongepowered.api.event.game.state.GameStoppedServerEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.text.Text;
import com.google.inject.Inject;
import Executors.JoinExecuter;
import Executors.MeteoriteExecuter;
import Executors.NodeCreatorExecuter;
import Listeners.NodeListener;
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
import ninja.leaping.configurate.loader.ConfigurationLoader;
@Plugin(id = "dz", name = "DZ", version = "0.0.1")
public class MainTest {
@Inject
Game game;
@Listener
public void onInit (GameInitializationEvent event){
game.getCommandManager().register(this, CommandSpec.builder().description(Text.of("Broadcasts a 60 second node capture timer")).executor(new TimerCommandExecutor()).build(),"timer");
}
public class TimerCommandExecutor implements CommandExecutor {
@Override
public CommandResult execute(CommandSource commandSource, CommandContext commandContext) throws CommandException {
game.getScheduler().createTaskBuilder().interval(1, TimeUnit.SECONDS).delay(1, TimeUnit.SECONDS).execute(new ForumTimerTask().submit(MainTest.this));
return CommandResult.success();
}
}
public class ForumTimerTask implements Consumer<Task> {
private int seconds = 60;
@Override
public void accept(Task task) {
seconds--;
game.getServer().getBroadcastChannel().send(Text.of("Node Capture Remaining Time: "+seconds));
if(seconds < 1) {
task.cancel();
}
}
}
}
Misplaced )
game.getScheduler().createTaskBuilder().interval(1, TimeUnit.SECONDS).delay(1, TimeUnit.SECONDS).execute(new ForumTimerTask()).submit(MainTest.this);
1 Like
Ah! Thank you. I should have payed attention to when I was organizing my code.
This is why we linebreak method calls on Builders.
e.g.
Task.builder()
.interval(1, TimeUnit.SECONDS)
.delay(1, TimeUnit.SECONDS)
.execute(new ForumTimerTask())
.submit(MainTest.this);
1 Like
Is it possible to separate the TimerCommandExecutor
nested class, into a separate class file? I’ve been trying to split up this class from ForumCountdown
, but I can not solve the No enclosing instance of the type ForumCountdown is accessible in scope
error, once I move the class over to a new package. I’m trying to keep the class files in their own files, to keep my Main class clean. Thanks.
The important thing is to not copy and paste code, but to understand what it does. submit()
is asking for an Object
which represents the main plugin class. When it’s a member class of the main class to begin with, you can just use <main class>.this
. Once you’ve moved the CommandExecutor
to another class, you’d provide it the standard way - put a MainTest
argument to the constructor of the executor, and pass that into submit()
.
1 Like
For the record the main reason everything was in a single class to begin with was so I could make a forum example that didn’t need to be split into multiple files.
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameStartingServerEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.text.Text;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@Plugin(id="au.id.rleach.forumcountdown", name="Forum Countdown", version = "0.0.1")
public class ForumCountdown {
@Listener
public void onStart(GameStartingServerEvent init){
Sponge.getCommandManager()
.register(
this,
CommandSpec.builder()
.description(Text.of("Broadcasts a 60 second timer"))
.executor(new TimerCommandExecutor(this))
.build(),
"timer"
);
}
private static class TimerCommandExecutor implements CommandExecutor {
private final ForumCountdown plugin;
public TimerCommandExecutor(ForumCountdown plugin) {
this.plugin = plugin;
}
@Override
public CommandResult execute(CommandSource commandSource, CommandContext commandContext) throws CommandException {
Sponge .getScheduler()
.createTaskBuilder()
.interval(1, TimeUnit.SECONDS)
.delay(1, TimeUnit.SECONDS)
.execute(new ForumTimerTask())
.submit(plugin);
return CommandResult.success();
}
}
private static class ForumTimerTask implements Consumer<Task> {
private int seconds = 60;
@Override
public void accept(Task task) {
seconds--;
Sponge .getGame()
.getServer()
.getBroadcastChannel()
.send(Text.of("Remaining Time: "+seconds+"s"));
if(seconds < 1) {
task.cancel();
}
}
}
}
That said, it could also be rewritten to just be inline using java 8 lambda.
package au.id.rleach.testdata;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameStartingServerEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.text.Text;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@Plugin(id="au.id.rleach.forumcountdown", name="Forum Countdown", version = "0.0.1")
public class ForumCountdown {
@Listener
public void onStart(GameStartingServerEvent init){
Sponge.getCommandManager()
.register(
this,
CommandSpec.builder()
.description(Text.of("Broadcasts a 60 second timer"))
.executor(
(source, commandContext) -> {
Sponge.getScheduler()
.createTaskBuilder()
.interval(1, TimeUnit.SECONDS)
.delay(1, TimeUnit.SECONDS)
.execute(new ForumTimerTask())
.submit(this);
return CommandResult.success();
}
)
.build(),
"timer"
);
}
private static class ForumTimerTask implements Consumer<Task> {
private int seconds = 60;
@Override
public void accept(Task task) {
seconds--;
Sponge .getGame()
.getServer()
.getBroadcastChannel()
.send(Text.of("Remaining Time: "+seconds+"s"));
if(seconds < 1) {
task.cancel();
}
}
}
}
I can’t remember why I didn’t to begin with, maybe the API has changed or It wasn’t yet supporting Java8 which is now mandatory.
The ForumTimerTask however can’t be factored out to be a lambda, as it affects state that isn’t effectively final, it could probably adjust a field though I’m not 100% sure.
1 Like
I myself would use an AtomicInteger
in that case.