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.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
So I was digging around in some old java projects that I never finished and I pulled out this little number that is of my best projects ever built.
It's a desktop clock widget coded in java and it works perfectly fine except for one thing. The way I have it check the current time to stay updated is in a loop and the loop "crashes" in a matter of seconds so the widget no longer gets the current time.
This is how the loop is constructed (reduced in size):
public class getCurrentTime {
public static void getTime() throws InterruptedException {
int hour = global.calendar.get(Calendar.HOUR);
int minute = global.calendar2.get(Calendar.MINUTE);
if(hour == 0) {
global.hour.setIcon(new ImageIcon("resources/hours/12.png"));
}else if(hour == 23) {
global.hour.setIcon(new ImageIcon("resources/hours/11.png"));
}else {
global.hour.setText("?");
}
if(minute == 0) {
global.minute.setIcon(new ImageIcon("resources/minutes/00.png"));
}else if(minute == 59) {
global.minute.setIcon(new ImageIcon("resources/minutes/59.png"));
}else {
global.minute.setText("?");
}
Thread.sleep(1000);
getTime();
}
}
global is a class where I keep most of my variables (I know it's weird, this was like 3 years ago, it's how I used to write my programs).
So my main question is, is there a way that I can prevent the loop from "crashing"?
Thanks in advance!
This is not a loop, really. It is a recursive call. In each recursion, some more memory will be allocated, so it will after some time go out of memory. I wonder why this is a matter of seconds here, but anyway.
Try using a Timer to schedule the gui update.
Edit : you are creating a new ImageIcon in each recursion. They can be rather large in memory. Maybe they are the reason for the rapid "crash".
Apart from that I suggest sticking to java naming conventions. Class names should start with a capital letter.
Thread.sleep(1000); is not going to be a good option. You can use Timer. By using a Timer you schedule a task at regular intervals.
Timer timer = new Timer();
long interval = (1000) ; // 1 sec
timer.schedule( new TimerTask() {
public void run() {
//do your work;
}
}, 0, interval);
If you want to stop the scheduling, you can use timer.cancel();
EDIT: as Fildor said I think memory is your problem. Use timer this way, it should solve your problem.
This is first solution
class clockUpdater implements Runnable {
#Override
public void run() {
Thread.sleep(WAIT_TILL_NEW_MINUTE);
while(true){ //a infinite loop
// the Swing call below must be queued onto the Swing event thread
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
//your clock updating code here
}
});
try {
Thread.sleep(1000*60);//you only need to update once after a minute
} catch (InterruptedException e) {
// ....
}
}
}
}
This is second
class clockUpdater extends Thread{
#Override
public void run(){
Thread.sleep(WAIT_TILL_NEW_MINUTE);
while(true){
try{
//your update code here
Thread.sleep(60*1000)//
}
catch(Exception e){
//..
}
}
}
}
Note: You need to start this as soon as the minute changes in system time. Or clock time will lag behind. Calculate WAIT_TILL_NEW_MINUTE as soon as your program starts. Better to update only when it is needed.
The answer for your question is
use set timeout function for the function
which you want to avoid loop crashing.
Like:
setTimeout(function() {
// write the logic
},1000);
So I am making a game where there are waves of enemies. The Wave class contains an update method that updates all the enemies in an arraylist of enemies contained in the Wave class. The Wave class also has a boolean called beat that decides whether or not the player has beaten the current wave. I am now have been trying however to start the next wave after the player beats the first. All waves in the arraylist start out with their beat variable as true except for the first. There are currently only two waves. I do not know why this is not working. Thank You for any help.
for(int i = 0; i < 1;i++)
{
if(!w.get(i).beat)
w.get(i).update(g2d);
else if(w.get(i).beat)
{
if(i-1 != -1)
{
if(w.get(i-1).beat && w.get(i).beat)
{
w.get(i).beat = false;
}
}
}
}
Your loop will increment i to the next wave after setting the current wave's beat setting to false, and miss calling the update method for that case. It looks like you should either call its update method immediately after setting beat = false, or perform the if test in the opposite order like this:
for(int i = 0; i < numWaves;i++) // upper range should be the number of waves
{
if(w.get(i).beat)
{
if(i>0) // this can be simplified to "if (i>0)"
{
if(w.get(i-1).beat) // no need to check w.get(i).beat here
{
w.get(i).beat = false;
}
}
}
else
w.get(i).update(g2d);
}
I don't know why you'd initialize a wave's beat state to true then set it to false when its turn comes. Why not just initialize all to false since they really haven't been beat yet?
I'm not sure that I understand your code but I can tell you 2 things. First of all, your loop never loops because as soon as the index is 1, it ends without executing the code a second time. Secondly
if(i-1 != -1)
{
if(w.get(i-1).beat && w.get(i).beat)
{
w.get(i).beat = false;
}
}
is always false due to what I said.
So some background information, I'm new to programming and am still learning, so I apologize for my trivial error making. I am making my own text based game just to further practice etc.
Here is the link to everything on dropbox for more context:
https://www.dropbox.com/sh/uxy7vafzt3fwikf/B-FQ3VXfsR
I am currently trying to implement the combat system for my game, and I am running into the issue of the combat sequence not ending when required. The 'combat sequence' is a while loop as follows:
public void startCombat()
{
inCombat = true;
while(inCombat != false)// && herohealth > 0 && monsterhealth > 0)
{
checkAlive();
heroHitMonster();
checkAlive();
monsterHitHero();
}
attackinghero.setHeroHealth(herohealth);
attackedmonster.setMonsterHealth(monsterhealth);
}
where the checkAlive() method is as follows:
public void checkAlive()
{
if(herohealth <= 0)
{
System.out.println("You have died.");
attackinghero.clearInventory();
inCombat = false;
}
else if(monsterhealth <= 0)
{
System.out.println("You have killed the "+attackedmonster.getmonsterName()+"!");
inCombat = false;
combatlocation.removeMonster(attackedmonster.getmonsterName());
}
else
{
//
}
}
I am trying to get it to end the combat sequence when either the 'hero' or 'monster' health become <= 0,
however it is currently finishing the while loop and therefore producing the result of the hero being hit even if he killed the monster in his first hit.
This is what is currently being 'printed to screen'
rat loses 5 health!
You have killed the rat!
Hero loses 1 health!
Any help is much appreciated, thanks in advance.
checkAlive shouldn't be void it should be Boolean and should return inCombat, and in your function startCombat you should do inCombat=checkAlive();
The while loop will only evaluate after both actions. You need a way to break the loop after the hero hits the monster. I would personally change the checkAlive method to return a boolean, and put the hit methods in if statements in the while loop:
if(checkAlive())
{
heroHitMonster();
}
if(checkAlive())
{
monsterHitHero();
}
You should end the loop at the end of the checkAlive instead of changing the boolean value.
If you killed the monster at first hit, you still execute the monsterHitHero() even, if the monster is killed. The function to hit should be conditioned to the life of heroes/monster.
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?
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.