Thread.sleep() in while loop doesn't work properly? - java

The object is supposed to change modes (movement algorithm) every 5 seconds. I first tried with a while loop but the loop was iterating way too fast. Then I added Thread.sleep(5000) but still my object moves only in one algorithm (scatterMode). Here is the algorithm:
//LEVEL 1
//scatter for 7s
//chase for 20s
//scatter for 7s
//chase for 20s
//scatter for 5s
//chase for 20s
//scatter for 5s
//chase indefinite
And here is the code. The constructor and variable declarations are at the bottom if you need to see them.
public void updateMode() throws InterruptedException {
while(ghostalive){
if(seconds<7){
Thread.sleep(100);
mode = scatterMode;
}
if(7<seconds && seconds<27){
Thread.sleep(5000);
mode = chaseMode;
}
if(27<seconds && seconds<34){
Thread.sleep(5000);
mode = scatterMode;
}
if(34<seconds && seconds<54) {
Thread.sleep(5000);
mode = chaseMode;
}
if(54<seconds && seconds>59) {
mode = scatterMode;
}
if(59< seconds && seconds<79){
mode = chaseMode;
}
if(seconds>84){
mode = scatterMode;
ghostalive=false;
}
seconds++;
ghostalive=false;
}
}
private int seconds=0;
private boolean ghostalive=true;
protected static final int chaseMode = 0;
protected static final int scatterMode = 1;
static int mode = scatterMode; //initially ghost start in scatterMode
public Ghost(int x, int y, Maze maze) throws InterruptedException{
super(x, y, maze);
futureDirection = 0;
timer = 0;
updateMode();
//chaseMode = false;
//frightenedMode = false;
}
public static int getMode(){
return mode;
}

Your sleep pattern is mixture of milliseconds and several seconds, but you are expecting to count seconds.
Try something like this:
while(ghostalive){
if(seconds<7){
mode = scatterMode;
}
if(7<seconds && seconds<27){
mode = chaseMode;
}
if(27<seconds && seconds<34){
mode = scatterMode;
}
if(34<seconds && seconds<54) {
mode = chaseMode;
}
if(54<seconds && seconds>59) {
mode = scatterMode;
}
if(59< seconds && seconds<79){
mode = chaseMode;
}
if(seconds>84){
mode = scatterMode;
ghostalive=false;
}
seconds++;
Thread.Sleep(1000);//Sleep for one second only
//ghostalive=false; // Should this be here? Ghost is set to not alive after each loop?
}
I have moved the sleep after the if statements so that it is consistent in each loop.

I think you should not rely on Sleep for measuring time because it can behave differently everytime you run it. Thread can goto sleep for more than the mentioned time. Sleep only pauses current thread for specific time. It does not guarantees that this thread will start executing again after same time.

Do NOT invoke updateMode from the constructor.
Instead, start a new thread.
As of now, probably the following happens: your Ghost is creates, goes through all of his stages before the constructor is complete. Then when your main program starts, your ghost is ghostalive=false and in scatterMode already.
For debugging, put in a lot of Loggin statements. It's best to use the logging APIs, but many beginners prefer System.out.println. It is a good practise to just print what you are doing - i.e. which mode you set the ghost to.
When you then also add game timer things, you should easily see that the ghost first goes through all of his states, before your actual game even started (i.e. a "game has started" logging is also a must.
Logging is not at all more difficult than printing.
// for each class, add such a line:
private static final LOG = java.util.logging.Logger.getLogger("packagename.classname");
static {
// Configure the active logging level manually
// For larger projects, use a .properties file!
LOG.setLevel(java.util.logging.Level.ALL);
}
// inside of appropriate methods, use
if (LOG.isLoggable(Level.DEBUG)) {
LOG.log(Level.DEBUG, "My ghost is now frightened.");
}
The if statement is important. It can be optimized well by hotspot, so that if logging is disabled, the logging statements come at next to no cost.
The good thing is that you can turn on and off these statements easily. While System.out.println you have to manually remove and readd to your code.

When seconds is exactly 7 or 34 or 54,..., there is no condition to handle these cases. It just doesn't enter in any of the if statements.

Related

Stop the running threads from an array

I have an array of threads and I want to start a few of them. The point is that I want to stop the threads with in a for loop.
In the for loop I want to check all threads if they are running or not, and if they are, I want to be asked if I want stop them(dialog box yes/no).
The problem is that the loop doesn't display all the times all three dialog boxes for all those three started thread. Sometime appear 1 dialog box, sometime 3 dialog boxes etc.
So, I do not have the chance to stop all three threads...
public class Main {
public static void main( String[] args )
{
Counter[] arrayOfThreads = new Counter[10];
for( int i = 0; i < arrayOfThreads.length; i++ )
{
arrayOfThreads[i] = new Counter( );
}
arrayOfThreads[3].start( );
arrayOfThreads[5].start( );
arrayOfThreads[2].start( );
for( int i = 0; i < arrayOfThreads.length; i++ )
{
if( arrayOfThreads[i].getState( ) == State.RUNNABLE )
{
int dialogButton = JOptionPane.YES_NO_OPTION;
int dialogResult = JOptionPane.showConfirmDialog( null, "Do you want to stop the theread: " + i, "Warning", dialogButton );
if( dialogResult == JOptionPane.YES_OPTION )
{
arrayOfThreads[i].stopProcessing( );
}
}
}
}
}
class Counter extends Thread
{
volatile boolean processing;
public void run( )
{
int i = 0;
processing = true;
while( processing )
{
System.out.println( " Number: " + i );
i++;
}
System.out.println( "finish" );
}
public void stopProcessing( )
{
processing = false;
}
}
EDIT:
So all what I want is when I press the EXIT button to close the threads and to dispose the frame if all the threads are stoped. I modified the first class to more more clear.
public class Program extends Frame {
public static void main(String[] args) {
Counter[] arrayOfThreads = new Counter[10];
for (int i = 0; i < arrayOfThreads.length; i++) {
arrayOfThreads[i] = new Counter();
}
Program program = new Program(arrayOfThreads);
program.startThreeThreads(1, 4, 5);
}
private Counter[] arrayOfThreads;
private JButton stopThreads;
public Program(Counter[] arrayOfThreads) {
this.arrayOfThreads = arrayOfThreads;
stopThreads = new JButton("STOP THREADS");
closeThreadsWhenExitIsPressed();
setSize(300, 200);
setLayout(new FlowLayout());
add(stopThreads);
setVisible(true);
}
public void closeThreadsWhenExitIsPressed() {
stopThreads.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
stopRunningThreadsMethod();
dispose();
}
});
}
private void startThreeThreads(int first, int second, int third) {
for (int i = 0; i < arrayOfThreads.length; i++) {
if (i == first || i == second || i == third) {
arrayOfThreads[i].start();
continue;
}
}
}
public void stopRunningThreadsMethod() {
for (int i = 0; i < arrayOfThreads.length; i++) {
if (arrayOfThreads[i].isAlive()) {
int dialogButton = JOptionPane.YES_NO_OPTION;
int dialogResult = JOptionPane.showConfirmDialog(null, "Do you want to stop the theread: " + i,
"Warning", dialogButton);
if (dialogResult == JOptionPane.YES_OPTION) {
arrayOfThreads[i].stopProcessing();
}
}
}
}
}
The documentation for getState() is (my emphasis):
Returns the state of this thread. This method is designed for use in
monitoring of the system state, not for synchronization control.
You're trying to use it for synchronization so you're already outside recommendation.
If you look at Thread.State you'll see it isn't always RUNNABLE and I suspect, as is common, System.out is synchronized so although not obvious from your code the thread could be WAITING (on another competing thread to use System.out).
Given all your thread does is hammer output it's probably quite common one or more is waiting. You could even find none show the dialog because as you go round the loop you happen to coincide with that thread waiting!
Check this by reading the state and outputting it!
So first, don't use getState() for synchronization and be aware you don't always know what synchronization is going on 'behind the scenes' in libraries you're using.
The documentation gives leave for the implementer to maybe cut corners in low-level synchronization of getState() and the value may not be 'first class' reliable (synchronized), but regardless don't do things you're told not to even if you don't know why!
The right method is isAlive(). The thread is alive if it has had its start() method called and not yet terminated. Waiting or not, it's alive...
Next problem, is because you set processing=true; in the run() method you could call stopProcessing() before processing has been set true.
There is no guarantee how far (if anywhere) down run() the thread has got when you reach stopProcessing() in the main thread.
I know there's a user interaction (e.g. big delay) but on an overloaded (or single threaded!) machine or a future use case it is possible for processing=true; to be executed after stopProcessing() sets it false. That may lead to 'runaway' processing.
So use volatile boolean processing=true; in the class declaration or set it in the constructor. That guarantees it will be set by the end of the constructor (takes place in the controlling thread) and must be before stopProcessing() is called.
Your application is (of course) a toy but think about when you would stop the threads the user didn't stop.
It's bad practice to just end the JVM without bringing all threads to a safe conclusion.
That doesn't matter in your toy but in real applications you may want to release external resources and (say) flush file buffers rather than let the JVM pull the run out.
That is, finally call stopProcessing() on all the threads in one loop and then join() in a second loop before ending the application.
It's important to use two loops because it makes sure the threads are all stopping concurrently and not one after the other.
I can't emphasise enough why you should end threads properly. People often ignore me and then long into to development have weird glitches that difficult to localise and hard to drive out.
Other considerations:
Consider using interrupt(). It's designed to help terminate threads and does nice things for you like jump them out of sleep and wait conditions (with an Interrupted exception). That will mean they may terminate faster (never slower) than your approach.
Again, not relevant in a toy but valuable in serious application.
Consider sub-classing Runnable instead of Thread. Again your toy is fine and valid but again 'real' applications end up preferring Runnable and using a thread pool of some kind (e.g. ExecutorService). That's clever because on many platforms the overhead of creating and destroying Threads is far larger than a lighter-weight Runnable.
That's the standard advice but I don't think its wisdom is always explained.
The threads probably haven't started by the time you enter the loop in main. Their states are Thread.State.NEW when you check arrayOfThreads[i].getState().
A simple solution would be either to wait some time before executing the loop to make sure the threads are running or to run a while loop over your loop to check the condition more than once.
Both are spotty and inefficient because you don't know exactly when the thread will be up and running. Instead, I would advise implementing a wait-notify mechanism to show a dialogue when the thread is certainly running.

Text based game with a separate timer loop?

I have started a Java coding short course at a university about 5 months ago. I have learnt quite the amount of things with regards to Java coding, but am still lacking in other things such as threads, handling exceptions, or even making JFrame games. I decided to embark on a text based game to just learn and figure out how a game loop should work (kind of), and how the logic should work (still, very "kind of"). The game I wrote runs with if-else commands, thus you get displayed a screen, type in the command of the option you want to pick, and it bumps you to the next menu, very standard of course. I run these if-else statements within nested for loops.
My nested for loops looks like the following:
// This is just an example, they're actually a lot more cluttered
// in my actual source code.
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
for (int i = 0; i <= 10; i--)
{
for (int ii = 0; i <= 10; i--)
{
if (reply.equalsIgnoreCase("/help")
{
System.out.println("Here I have a separate call to a class
file (excuse me, forgot the exact wording), thus it
call something like help.writeOutput(); to display
the help menu");
reply = keyboardInput.nextLine();
if (reply.equalsIgnoreCase("/makegameeasy")
{
// Just an example.
gamedifficultyEasy.writeOutput();
reply = keyboardInput.nextLine();
if (reply.equalsIgnoreCase("/back")
{
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
break;
}
}
else if (reply.equalsIgnoreCase("/makegamedifficult")
{
// Another example.
gamedifficultHard.writeOutput();
reply = keyboardInput.nextLine();
if (reply.equalsIgnoreCase("/back")
{
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
break;
}
}
else if (reply.equalsIgnoreCase("/back")
{
mainMenu.writeOutput();
reply = keyboardInput.nextLine();
break;
}
}
else
{
System.out.println("Here I print out an error for incorrect
input received, standard fare.");
mainMenu.writeOutput();
reply = keyboard.nextLine();
break;
}
}
}
As mentioned, the above is just an example, it's not very elegant, and I can probably use Exceptions for any incorrect info submitted by the user, however I do not know too much of Exceptions to comfortably add them, so I'll do that at a later time, however my main issue at the moment is a part of my game where "resource mining" has to be done on regular intervals. I have been all over Google, but still can't quite catch how to set a Thread or Timer up for my game so it does the mining automatically, and the player can go on with their game.
The game is essentially one of those games where you build up a base, upgrade your mining tools, and generate more "stuff". I have pasted a few blocks of code from my "mining" class file below that will basically run how much of one thing should be mined. In the game you will be able to buy upgrades of course, so it will get factored into your mining speed.
// I initiate my variables a lot earlier, but just for some
// clarity, I have initiated the variables in the below methods,
// they will not work correctly anyway, I am aware of that, however
// I didn't want to add the other "get, set and initiate"
// variables and methods everywhere, as to not spam this block of code.
// upgradeOS, upgradeHF, and upgradeLM all have their own respective
// set and get methods. They are also all int variables.
public void handleOS()
{
// OS = short for Oxygen Silo
int mineOS = os.getStoredO2() + (1 * upgradeOS);
os.setStoredO2(mineOS);
}
public void handleHF()
{
// HF = short for Hydrogen Fuel
int mineHF = hf.getStoredO2() + (1 * upgradeHF);
hf.setStoredO2(mineHF);
}
public void handleLM()
{
// LM = short for Liquid Minerals
int mineLM = lm.getStoredMinerals() + (1 * upgradeLM);
lm.setStoredMinerals(mineLM);
}
// This is what's going to run the whole time on the set intervals.
public void mine()
{
mineOS = os.getStoredO2() + (1 * upgradeOS);
mineHF = hf.getStoredO2() + (1 * upgradeHF);
mineLM = lm.getStoredMinerals() + (1 * upgradeLM);
os.setStoredO2(mineOS);
hf.setStoredO2(mineHF);
lm.setStoredMinerals(mineLM);
}
// Using 10 seconds here to have it update as quickly as possible so I can
// see any changes. This is just here to write an output.
public void getUpgradeInfo()
{
System.out.println("Oxygen: " + (1 * upgradeOS) + " / per 10 seconds.");
System.out.println("Hydrogen: " + (1 * upgradeHF) + " / per 10 seconds.");
System.out.println("Liquid Mineral: " + (1 * upgradeLM) + " / per 10 seconds.");
}
I'm not the best naming schemes for my materials...
TL;DR: I can't figure out how to implement a thread or timer just for the above mentioned mine() method since I do not have the appropriate amount of knowledge. My if-else rules aren't too elegant, but I'll work on those of course. Basically the if-else rules should run separately from the mine() method, and you can do some AFKing without the game updating the System.out output, thus you can be floating in, for example, the Oxygen Silo upgrade menu, and you won't be bounced back to a different menu due to a thread "waking up", such as being bounced to the main menu, but the mine() method will still generate resources in the background as it should.
Any help on this, or just even a nudge in the right direction will be greatly appreciated.
To answer the question you asked, you can do something like this:
import java.util.*;
TimerTask tt = new TimerTask() {
public void run() {
mine();
}
}
Timer t = new Timer();
t.scheduleAtFixedRate(tt, 0, 1000);
Alternatively, you can use an ActionListener and the swing timer in a similar way. This has the advantage of being Thread-safe in case you build a swing gui on top
Lastly, you should check out the usage of synchronized and volatile to make sure that the variable(s) that are updated in mine() are done so in a thread-safe way
Thanks to #ControlAltDel, definite shove in the right direction. I have taken a bit of code and set it up like this:
import java.util.*;
// extends TimerTask needed
public class TimerTestOne extends TimerTask
{
// Needed
#Override
public void run()
{
TimerTestTwo ttt = new TimerTestTwo();
mine();
}
// Needed, method doesn't need the same name though.
private void completeTask()
{
try
{
//assuming it takes 10 secs to complete the task
Thread.sleep(10000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
// You will need to define the following line of code:
TimerTask tt = new TimerTestOne();
Scanner keyboard = new Scanner(System.in);
String reply;
// Following 2 lines of code, yup, need them.
Timer timer = new Timer(true);
timer.scheduleAtFixedRate(tt, 0, 10*1000);
previousMenu();
for (int i = 0; i <= 10000; i++)
{
for (int ii = 0; ii <= 10000; i++)
{
System.out.println("Go to another menu?");
reply = keyboard.nextLine();
if (reply.equalsIgnoreCase("/yes"))
{
yes();
reply = keyboard.nextLine();
}
}
}
}
// I added the following methods, just so I didn't have to work
// off 2 class files.
public void mine()
{
System.out.println("Mined");
}
public static void yes()
{
System.out.println("Next menu");
}
public static void previousMenu()
{
System.out.println("Previous menu");
}
}
So there, if anyone ever needs to have a look at setting a timer up that won't break your text based game.

For and While Loop

I have a program that runs simultaneously and I have this problem where I want to stop the thread but the for loop/while loop doesn't get cancelled once I once I click enter
If I take the for loop out of the while loop, the program actually responds to the enter and shuts down.
class MyNumber extends Thread {
private volatile boolean processing = true;
public void run() {
while (processing) {
// Once I take this for loop out(put // beside it like right now), the enter key to stop the program then does work.
//for(int i = 1; i<27; i++){
System.out.println("Letter " + "i");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}
}
public void permSleep() {
processing = false;
}
}
public class LetterNumber {
public static void main(String[] args) {
MyNumber num1 = new MyNumber();
num1.start();
System.out.println("Hit enter to stop the Numbers!");
Scanner shutter1 = new Scanner(System.in);
shutter1.nextLine();
num1.permSleep();
}
}
Why does the for loop cause the program to not shutdown?
I'm not really clear on what you're asking. However, if you're expecting that the while and for loops will both terminate as soon as processing is set to true, that isn't what happens. A while will execute the statement in the body (i.e. the for loop), then it will test the condition (processing == true), then if it's true, it executes the statement again, and then tests the condition again. It doesn't test it while the for loop is executing. It doesn't "notice" when the processing variable is set. So when processing is set to true, the for loop will keep going until it's done, which could be another 26 seconds.
To fix this simply, add
if (!processing)
break;
inside the for loop. Now the processing flag will be tested each time through the for loop. (If it were me, I'd put a "label" on the while loop and use that to break out of both loops.) Another way to fix it:
for(int i = 1; i<27 && processing; i++){
which means the for loop will continue only as long as processing is true.
Note: These solutions will still not test processing while the sleep(1000) is going on. So the program could still pause up to 1 second before it terminates. If you really want a solution that will terminate the sleep, you'll have use interrupt or some other concurrency feature.
It should work. Your for loop takes about 27 seconds to finish. It should come out of that after the for loop has finished.

How do I check how many events are happening within 2 seconds? (timer)

I need to check how many events are detected within 2 seconds. I have the timer working and I have everything else working...but I ran into a problem: the loop only checks one time, per second and I can't seem to figure out how to fix that. I need it to check constantly during these two seconds to see how many events there were in total!
Here is what I have:
int seconds = 0;
System.out.println("Seconds: " + seconds);
while(seconds < 2)
{
//Wait 1 second
try {
Thread.sleep(1000);
}
catch(Exception e) {}
seconds++;
System.out.println("Seconds: " + seconds);
//This needs to be looping the whole time.
//But right now, it's being blocked and only checked once
if(eventDetected() && seconds <= 2){
events++;
}
}
So you can see my problem. I can't split them up because then the second timer would run, and THEN eventDetected() would be checked. I need it to check constantly DURING the two second timer...so I basically need both things to happen at once. Is there any way I can do this?
Thanks for any help ahead of time!
I think your design pattern needs work -- I don't know what type event you're looking to detect, but no matter how short your sleep time is, there's a chance you could miss an event using the current pattern. Here's what I suggest:
Have eventDetected() increment your events counter. That way, you won't miss an event.
Then, you just need a way to turn on and off listening (and perhaps resetting the event counter). If you're sure that in you're current pattern you are really in a different thread that won't block your eventDetected() method, you could set a flag to check. For example:
When you want to start listening:
listenForEvents = true;
In eventDetected():
if (listenForEvents) { events++; }
When you want to stop listening (for example, after your Thread.sleep() call):
listenForEvents = false;
With multithreading, make sure to watch out for concurrency issues checking and setting the variables, of course.
I would tell you what kind of event I have to keep track of but then I'd have to kill you :D
Answered my own question. Hopefully this will help anyone else out who has a similar problem at some point! I looked up multithreading a bit...
I created a new class EventTimer which implements Runnable, with a public field for seconds:
public class EventTimer implements Runnable{
int seconds;
static int timerThreadCount = 0;
Thread t;
public EventTimer() {
timerThreadCount++;
this.seconds = 0;
t = new Thread(this, "Event Timer");
t.start(); // Start the thread
}
#Override
public void run() {
// TODO Auto-generated method stub
while(seconds < 2)
{
//Wait 1 second
try {
Thread.sleep(1000);
}
catch(Exception e) {
System.out.println("Waiting interupted.");
}
seconds++;
System.out.println("Seconds: " + seconds);
}
}
}
Then I used an instance of the EventTimer, and used a while loop & if statement to solve my problem.
EventTimer t = new EventTimer();
while(t.seconds < 2){
if(eventDetected()) events++;
}
It was actually quite simple! I realize that each iteration of my loop of operation (since the entire code piece above is inside an infinite loop) will create a new EventTimer thread and I will eventually run into memory problems however. How would I close/end a thread after the timer has reached 2 seconds?

Why isn't "<= 1" working as expected?

I have the following code
#EventHandler
public void onPlayerQuit(PlayerQuitEvent event){
Player player = event.getPlayer();
final Player[] playerlist = getServer().getOnlinePlayers();
if (playerlist.length <=1) { // if no players are online
getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable(){
// this is a scheduler.
public void run(){
if(playerlist.length <=1){
getServer().shutdown();
}
}
}, 6000L); // runs every 6000 ticks, which is every 300 seconds, which is every 5 minutes.
}
}
Which when a player leaves, it checks to see if he was the last one on, if he was, then it after 5 minutes, it checks again, and if still no one is on its supposed to stop the server.
In this line here:
if (playerlist.length <=1) { // if no players are online
I HAVE to have it as <=1 or it doesnt work at all, but it will also stop the server if I leave, and join back and im the only one on. When I had it at =0 and just <1 it didnt work.
Any ideas?
Here is my update code (Still doesnt work):
#EventHandler
public void onPlayerQuit(PlayerQuitEvent event){
Player player = event.getPlayer();
final Player[] playerlist = getServer().getOnlinePlayers();
if (playerlist.length <=1) { // if no players are online
getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable(){
// this is a scheduler.
public void run(){
final Player[] playerlist = getServer().getOnlinePlayers();
if(playerlist.length <=1){
getServer().shutdown();
}
}
}, 500L); // runs every 6000 ticks, which is every 300 seconds, which is every 5 minutes.
}
}
The reason why what you have written does not work is your use of <=. If someone logs off and no one is left, the task is scheduled. If someone logs back on within 5 minuets or less and remains online, when the scheduled task checks to see if the server should be shutdown, 1 <= 1 is true so the server shuts down.
You mentioned that just using = did not work, this is because in boolean statements, == must be used to check for equality.
Try using this:
if (playerlist.length == 0) { // if no players are online
// Do stuff
}
Update (Discussed in comments):
I do not know the Bukkit API very well, but this is what I assume is happening then: The online player list is updated after onPlayerQuit() is executed. Try this: Inside of your onPlayerQuit() method, try checking playerlist.length == 1 and inside of your task, check playerlist.length == 0
I do not know if you have already solved this problem, but I think part of the problem is that you are trying to re-initialize an already initialized final variable...Again, like some of the others in the replies, I do not know Bukkit API as I am trying to learn it, but you cannot re-initialize a final variable...So I would either recommend taking away the final part, or if it must remain, I would make a new array for the run() method...as you are checking for a second time to see if anyone is online...it will not matter if you change arrays, because you are changing amount of players online presumably anyways...because it is final, it will always be 1 when you re-run the length of the final array of playerlist...
You code doesn't refresh the playerlist variable when the delayed task in run, never detecting if someone has joined when the task is actually run.
A better implementation of the code would be:
#EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
boolean playersOnServer = false;
for(Player p : getServer().getOnlinePlayers()) {
if(p==player) continue;
playersOnServer = true;
break;
}
if (!playersOnServer) {
getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable(){
public void run(){
//Redo players check
boolean playersOnServer = false;
for(Player p : getServer().getOnlinePlayers()) {
playersOnServer = true;
break;
}
if(!playersOnServer){
getServer().shutdown();
}
}
}, 6000L);
}
}
Inside the above code, I used a for loop instead of a simple check to see if there are any players online, to make it work with the old player array from the old bukkit, and the new collections method from the new bukkit.
This kind of detection still has its bugs, for example if the last person quits, then directly joins, and then waits 4 minutes and 59 seconds before leaving again, it will shut down the server directly when he has left.
You never cancel the task when someone logs back on. Since you never cancel the task it will execute even if someone logs back on within the 5m time frame.

Categories

Resources