Get next player in for loop

Discussion in 'Plugin Development' started by elementalgodz11, Apr 27, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    elementalgodz11

    Okay, so I want to make a syncrepeatingtask that iterates through all online players.

    It sends the first player in the loop and moves to the next, and so on, when it gets to the last player in the loop, then it goes back to the first?

    I do not know how I would do this?

    Any help would be appreciated, thanks.
     
  2. Offline

    tommyhoogstra

    For all online players you can just do

    for(Player p : Bukkit.getOnlinePlayers()){
    Stuff
    }
     
  3. Offline

    elementalgodz11

    tommyhoogstra

    I don't want to send a message to the online players at once, I want to do it one by one in the order of the loop.
     
  4. Offline

    garbagemule

    To what end? If you wrap around when you get to the end, the loop will never stop...

    Are you perhaps asking how to make it so that in every repetition of the task, a new player is considered?
     
  5. Offline

    tommyhoogstra

    You dont have to send a message, you could potentially do p.setKicked(true);
     
  6. tommyhoogstra setKicked()? That's a new one! Does setting it to false automatically pull them back on the server or something?
     
    tommyhoogstra likes this.
  7. Offline

    elementalgodz11

    garbagemule

    I am trying to show an entity spawned using ProtocolLib to a player using a SyncRepeatingTask, on the next repetition of the task the entity is removed to the previous player and shown to the next player in the loop.
     
  8. Offline

    BillyGalbreath

    Clarification on what OP is trying to ask. He has a repeating task (lets just say every 10 seconds) and he wants to do something to a player each time its ran. The task mustn't re-use players until all players have been used first. Then it starts over (the loop part).
     
  9. elementalgodz11 In your task, have a counter. Use the value of the counter to get a player from the list, and increment it by 1 each time. Reset the counter when it's too high.
     
  10. Offline

    tommyhoogstra

    AdamQpzm I couldn't be bothered checking what it actually is :p kickPlayer*
     
    AdamQpzm likes this.
  11. Offline

    elementalgodz11

    AdamQpzm

    Okay, thank you.

    Would something like this work?

    Code:java
    1. @Override
    2. public void run() {
    3.  
    4. Player[] online = Bukkit.getOnlinePlayers();
    5.  
    6. for (@SuppressWarnings("unused") Player players : online) {
    7.  
    8. if (online.length > count) {
    9.  
    10. count++;
    11.  
    12. online[count].sendMessage("Test");
    13.  
    14. } else {
    15.  
    16. count = 0;
    17.  
    18. }
    19.  
    20. }
    21.  
    22. }


    Edit: Tried it, but got an arrayoutofboundsexception
     
  12. Offline

    garbagemule

    Close, but no.

    Let's assume Alice and Carol are currently online. The repeating task starts by considering Alice because she's the first on the list. The counter is incremented, and Carol is second on the list, so she is considered next. The counter is incremented, but now Bob logs on before the task executes again, making him second on the list and Carol third. When the task executes again, the third person on the list is considered, which is now Carol.

    The problem with timers is time and the fact that multiple, unexpected events can happen in-between two other events. Tread carefully.

    elementalgodz11
    First of all, don't suppress warnings unless you know what you're doing. They're there for a reason. Your code is just an unnecessarily complicated way of doing this:
    Code:
    for (Player p : Bukkit.getOnlinePlayers()) {
        p.sendMessage("Test");
    }
    Secondly, the code will not work. There is nothing magical about threads or "tasks" - they are logical pieces of code that are executed procedurally. When your task is executed, the entire run() method is executed, not just one iteration of it. What you want is to keep some sort of state between executions of the run() method.

    Third, consider my example with Alice, Bob, and Carol above. You have to handle this somehow, or you will create inconsistencies in your program and errors in your console ;)

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 7, 2016
    AdamQpzm and BillyGalbreath like this.
  13. Offline

    BillyGalbreath

    To solve this I would simply make a List of player names (or UUIDs if you prefer, not really needed for this). Inside the task, loop through all the online players until you find a player that is NOT in the list. When you find one, add them to the list, perform the action, end the loop there. Now, if the loop ever gets to the end without finding a player then just clear the list and start again. Simple.
     
    AdamQpzm likes this.
  14. Offline

    garbagemule

    We can do better.

    With a List, this algorithm is O(n^2). Using almost any Set implementation, you are guaranteed to get O(log n) lookup and insertion - with a HashSet, it's O(1), making the entire algorithm O(n). Remember that this is per-execution (ultimately bringing us to O(n^3) for the naïve List-based implementation, or O(n^2) for the HashSet-based implementation, albeit spread over multiple ticks), which may not be an issue, but...

    Can we do better?

    Consider a stack-based approach (never use the java.util.Stack class - use the Deque interface and either ArrayDeque or LinkedList for the implementation), where we instead of gradually building a list start by building the list from the online players - we already have the array of all online players, why not use it? If we store the contents of the array in a stack/queue, we can pop the queue in every iteration until it is empty, at which point we just refill it. Using e.g. ArrayDeque or LinkedList, the construction time is probably O(n) (but not worse), but we only do this once per "full cycle", making the entire algorithm O(2n) = O(n) - linear time - because pop() is an O(1) operation.

    Can we do even better?

    If we don't care about having to maintain the count-variable, we can store the array directly (making construction time constant), and then increment it on every execution, resetting both the count-variable and the array when we reach the end. This makes for an O(n) algorithm with a smaller constant. Furthermore, direct array-access is faster than method invocations that are not inlined (not sure if pop() can be inlined - perhaps with ArrayDeque).

    Can we do even better?!

    Perhaps, but it's probably not worth the trouble ;)

    Finally, any kind of "storing Player references" is prone to errors when deferring the usage to later. In all instances, it is necessary to check if the player is, indeed, still online. If not, skip to the next player. Also remember that storing Player references could be problematic w.r.t memory if there is a high churn rate on the server, because the references could be kept in memory for longer than they should due to this program. UUIDs or names may be better, but the added "getPlayer(name)" or "getPlayer(uuid)" lookup may be slow.
     
  15. Offline

    BillyGalbreath

  16. Offline

    garbagemule

    ;)

    In case anyone is unfamiliar with the Big-O notation used in my previous post, here is an excellent, well-written, teaching-by-example guide for beginners: A Beginner's Guide to Big O Notation
     
Thread Status:
Not open for further replies.

Share This Page