Page 3 of 4 FirstFirst 1234 LastLast
Results 21 to 30 of 40
  1. #21
    Senior Member
    Join Date
    Nov 2013
    Location
    Nsw, Australia
    Posts
    399
    the plugin i was going to write was just going to contain

    Code:
    public override void OnRoundOver(int winningTeamId)
            {
    			ExecuteCommand("procon.protected.tasks.add", "EndRound", "10", "0", "1", "procon.protected.send", "mapList.RunNextMap");
            }
    Tho i have no idea if that works or not i just Notepadded it quickly :P but i would have made the 10 and int that is changable via the UI and probably an Ingame command (im a huge fan of ingame commands)...

  2. #22
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    I just figured out another way to add delays:
    Code:
    System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
    double dDuration = 30; // Delay in seconds
    
    stopwatch.Start();
    while (stopwatch.Elapsed.TotalSeconds < dDuration) {}
    stopwatch.Stop();
    This could be used like this:
    Code:
    Action<double> Wait = delegate(double dDuration)
                          {
                              System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
                              stopwatch.Start();
                              while (stopwatch.Elapsed.TotalSeconds != dDuration) {}
                              stopwatch.Stop();
                          };
    
    // ***** Some code here *****
    
    Wait(30); // Wait for 30 seconds
    
    // ***** Rest of code here *****
    Papa, would this be alright in a limit?
    Last edited by LCARSx64; 21-07-2014 at 15:22.

  3. #23
    Quote Originally Posted by LCARSx64 View Post
    I just figured out another way to add delays:
    Code:
    System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
    double dDuration = 30; // Delay in seconds
    
    stopwatch.Start();
    while (stopwatch.Elapsed.TotalSeconds < dDuration) {}
    stopwatch.Stop();
    This could be used like this:
    Code:
    Action<double> Wait = delegate(double dDuration)
                          {
                              System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
                              stopwatch.Start();
                              while (stopwatch.Elapsed.TotalSeconds != dDuration) {}
                              stopwatch.Stop();
                          };
    
    // ***** Some code here *****
    
    Wait(30); // Wait for 30 seconds
    
    // ***** Rest of code here *****
    Papa, would this be alright in a limit?
    No, that blocks the main thread in a busy-wait loop. It's worse than Thread.Sleep because it will burn 100% of a CPU core doing the while loop.

    All of the correct solutions boil down to using a separate thread. LjMollnir's method would work, because adding a procon task creates a new thread with a built-in timer.

    Another popular way to do timer threads is with a BackgroundWorker class instance, but it is just a convenience wrapper around a thread:

    http://www.dotnetperls.com/backgroundworker

    EDIT: I forgot, there is one other correct solution that does not require a separate thread. In an event-based system, like Procon, as long as you have frequent and steady events, at least one per second, you can just watch elapsed time and wait until the time is equal or greater than the amount needed to wait. It's not as accurate, but it's cheaper and safer.

    For example, if you are using OnKill and you are getting a steady stream of events, you can do something like this to do a 30 second timer:

    Code:
    String key = "Timer";
    if (!plugin.RoundData.issetObject(key)) {
        plugin.RoundData.setObject(key, DateTime.Now); // start the timer
        return false;
    }
    // otherwise get the start time
    DateTime start = (DateTime)plugin.RoundData.getObject(key);
    
    // Check elapsed time
    double seconds = 30; // do something after 30 seconds
    if (DateTime.Now.Subtract(start).TotalSeconds >= seconds) {
        // ... do whatever you want to do after 30 seconds
        plugin.RoundData.unsetObject(key);
    }
    return false;
    Last edited by PapaCharlie9; 21-07-2014 at 16:51.
    Don't send me private messages (PMs) unless you really need privacy, like your game server password. If you just have a question or need help, post in one of the threads. It's extra work for me to answer questions and give help in private messages and no one else gets the benefit of the answer.

  4. #24
    Senior Member
    Join Date
    Nov 2013
    Location
    Nsw, Australia
    Posts
    399
    Quote Originally Posted by PapaCharlie9 View Post
    EDIT: I forgot, there is one other correct solution that does not require a separate thread. In an event-based system, like Procon, as long as you have frequent and steady events, at least one per second, you can just watch elapsed time and wait until the time is equal or greater than the amount needed to wait. It's not as accurate, but it's cheaper and safer.
    Good during game when there is alot events.. but on round over there isnt too many.. unless you count people leaving a server ...

    thats why i was going to go with the tasks.add... sure its a whole seperate plugin but its the only way i could see it working correctly.. (and ive never played with Insane limits so was unsure what it could do)...

    actually does one of the main admin plugins support something like this already ??

  5. #25
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    Quote Originally Posted by PapaCharlie9 View Post
    No, that blocks the main thread in a busy-wait loop. It's worse than Thread.Sleep because it will burn 100% of a CPU core doing the while loop.

    All of the correct solutions boil down to using a separate thread. LjMollnir's method would work, because adding a procon task creates a new thread with a built-in timer.

    Another popular way to do timer threads is with a BackgroundWorker class instance, but it is just a convenience wrapper around a thread:

    http://www.dotnetperls.com/backgroundworker

    EDIT: I forgot, there is one other correct solution that does not require a separate thread. In an event-based system, like Procon, as long as you have frequent and steady events, at least one per second, you can just watch elapsed time and wait until the time is equal or greater than the amount needed to wait. It's not as accurate, but it's cheaper and safer.

    For example, if you are using OnKill and you are getting a steady stream of events, you can do something like this to do a 30 second timer:

    Code:
    String key = "Timer";
    if (!plugin.RoundData.issetObject(key)) {
        plugin.RoundData.setObject(key, DateTime.Now); // start the timer
        return false;
    }
    // otherwise get the start time
    DateTime start = (DateTime)plugin.RoundData.getObject(key);
    
    // Check elapsed time
    double seconds = 30; // do something after 30 seconds
    if (DateTime.Now.Subtract(start).TotalSeconds >= seconds) {
        // ... do whatever you want to do after 30 seconds
        plugin.RoundData.unsetObject(key);
    }
    return false;
    Thanks for both the detailed answer and your patience Papa. I'm sorry to be asking so many questions, but if I don't ask I can't learn.

    The reason I'm actually hung up on this is because I'm trying to achieve a particular result, as an example of what I'm trying to do, I'll (hopefully) demonstrate using messages as an example (although I'm actually wanting to have some commands trigger as specific times too).

    Suppose I have 3 message blocks I want to send to either a single player or all players. These message blocks consist of 7 lines of chat each and their order has to be strict (even though I could use the message with delay versions of the functions in IL, I've noticed that sometimes each of the 7 lines get scrambled up). The first message block is easy because that is displayed immediately, the second block is then displayed 12 seconds later. Finally the third block is displayed 27 seconds later (these aren't necessarily the times I want to use but are just examples).
    This sort of thing could be alright to do in an OnRoundOver event since it isn't going to required a lot of threads if sent globally, however, what if these messages needed to be sent during an OnSpawn event in a 64 slot server. The possibility exists, however unlikely, that these messages would need to be sent 64 times at once (to each individual as a private message).
    You see my dilemma, basically I'm trying to figure out if this sort of thing is possible and how I'd go about achieving it.
    Thanks once again Papa, I really do appreciate your help.

  6. #26
    Senior Member
    Join Date
    Nov 2013
    Location
    Nsw, Australia
    Posts
    399
    LCARS.. in that case you could use a tasks.add for a single task that is called once every second or so that just calls back to a routine that handles the timers much like PC9 mentioned in the previous post.. that way you are only making 1 thread and you have a constant tick you can rely on

    thats how i think it would work anyway 8) as long as you could get everything done within that 1 second.. you may need to extend the timer on it or have an internal check to see if the previous call completed correctly

  7. #27
    Quote Originally Posted by LjMjollnir View Post
    LCARS.. in that case you could use a tasks.add for a single task that is called once every second or so that just calls back to a routine that handles the timers much like PC9 mentioned in the previous post.. that way you are only making 1 thread and you have a constant tick you can rely on

    thats how i think it would work anyway 8) as long as you could get everything done within that 1 second.. you may need to extend the timer on it or have an internal check to see if the previous call completed correctly
    There's no way to do a tasks.add in IL.

    For the messaging case, the best thing to do is use a single long-lived thread. You feed it with a fifo (Systems.Generics.Collections.Queue, for example) of "commands", composed of a message string, a delay, and a target audience (player, team, all, whatever). The command or trigger you mention pushes commands into the Queue, and the thread wakes up whenever there is something in the Queue and pulls out commands until it is empty. This is a textbook producer-consumer pattern, where the producer is trivial.

    Your IL code initializes the thread and sets a flag (or stores the thread as an Object in plugin.Data), so that you know that one exists already and don't create another one. This is a Singleton pattern. The thread then lives for as long as Procon is running -- hmm, might be some issues with disabling the plugin and re-enabling, might create another thread, I'll have to check if plugin.Data is cleared on plugin disable.

    Once the thread is running, its just a matter of synchronizing the Queue object, which is easily done with lock and Monitor. The locks are important to make sure the thread doesn't get ahead of the code pushing the commands. The basic code template is this:

    Thread code:

    Code:
    // Dictionary<String,String> is a command, with keys "message", "delay" (seconds as a string), and "target"
    Queue myQueue = new Queue<Dictionary<String,String>>();
    
    while (flagStillEnabled) { // exit thread if no longer needed
        Dictionary<String,String> command = null;
        lock (myQueue) {
    	while (myQueue.Count == 0) {
    	    Monitor.Wait(myQueue); // Wait until someone puts something in the queue
    	    if (!flagStillEnabled) return;
    	}
    	command = myQueue.Dequeue(); // pull the next command off the head of the queue
        }
    
        // Process the command ... it's okay to do Thread.Sleep here, since you are not in the main thread!
        // You could Thread.Sleep for just 1 second at a time and associate an elapsed time with each command
        // You might have to change the while loop logic above to be the queue is empty AND there are no active commands counting down in time
        // Otherwise you will get stuck in the Monitor.Wait. You only want to wait if there is nothing to do
    }
    The code for pushing a command into the queue, from your in-game chat command handler or whatever:

    Code:
        Dictionary<String,String> command = new Dictionary<String,String>();
        int iDelay = 27; // or whatever
        command["message"] = "your message";
        command["delay"] = iDelay.ToString();
        command["target"] = "player:" + player.Name; // or "all" or "team:1" or whatever
        lock (myQueue) {
            myQueue.Enqueue(command);
            Monitor.Pulse(myQueue); // Tell the waiting thread there is something to do
        }
    The variables myQueue and flagStillEnabled come from plugin.Data, I left out the get/set Object/bool code for clarity.
    Last edited by PapaCharlie9; 23-07-2014 at 15:59.
    Don't send me private messages (PMs) unless you really need privacy, like your game server password. If you just have a question or need help, post in one of the threads. It's extra work for me to answer questions and give help in private messages and no one else gets the benefit of the answer.

  8. #28
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    Quote Originally Posted by PapaCharlie9 View Post
    There's no way to do a tasks.add in IL.

    For the messaging case, the best thing to do is use a single long-lived thread. You feed it with a fifo (Systems.Generics.Collections.Queue, for example) of "commands", composed of a message string, a delay, and a target audience (player, team, all, whatever). The command or trigger you mention pushes commands into the Queue, and the thread wakes up whenever there is something in the Queue and pulls out commands until it is empty. This is a textbook producer-consumer pattern, where the producer is trivial.

    Your IL code initializes the thread and sets a flag (or stores the thread as an Object in plugin.Data), so that you know that one exists already and don't create another one. This is a Singleton pattern. The thread then lives for as long as Procon is running -- hmm, might be some issues with disabling the plugin and re-enabling, might create another thread, I'll have to check if plugin.Data is cleared on plugin disable.

    Once the thread is running, its just a matter of synchronizing the Queue object, which is easily done with lock and Monitor. The locks are important to make sure the thread doesn't get ahead of the code pushing the commands. The basic code template is this:

    Thread code:

    Code:
    // Dictionary<String,String> is a command, with keys "message", "delay" (seconds as a string), and "target"
    Queue myQueue = new Queue<Dictionary<String,String>>();
    
    while (flagStillEnabled) { // exit thread if no longer needed
        Dictionary<String,String> command = null;
        lock (myQueue) {
    	while (myQueue.Count == 0) {
    	    Monitor.Wait(myQueue); // Wait until someone puts something in the queue
    	    if (!flagStillEnabled) return;
    	}
    	command = myQueue.Dequeue(); // pull the next command off the head of the queue
        }
    
        // Process the command ... it's okay to do Thread.Sleep here, since you are not in the main thread!
        // You could Thread.Sleep for just 1 second at a time and associate an elapsed time with each command
        // You might have to change the while loop logic above to be the queue is empty AND there are no active commands counting down in time
        // Otherwise you will get stuck in the Monitor.Wait. You only want to wait if there is nothing to do
    }
    The code for pushing a command into the queue, from your in-game chat command handler or whatever:

    Code:
        Dictionary<String,String> command = new Dictionary<String,String>();
        int iDelay = 27; // or whatever
        command["message"] = "your message";
        command["delay"] = iDelay.ToString();
        command["target"] = "player:" + player.Name; // or "all" or "team:1" or whatever
        lock (myQueue) {
            myQueue.Enqueue(command);
            Monitor.Pulse(myQueue); // Tell the waiting thread there is something to do
        }
    The variables myQueue and flagStillEnabled come from plugin.Data, I left out the get/set Object/bool code for clarity.
    Wow, awesome Papa! Thanks very much, I'll give it a try and let you know how I go.

  9. #29
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    Papa I love you, your method works perfectly!
    What was that you were saying about you being obsolete? Proof right here that you and still very much needed.

  10. #30
    But what happens when I get to the bottom of my bag of magic tricks?
    Don't send me private messages (PMs) unless you really need privacy, like your game server password. If you just have a question or need help, post in one of the threads. It's extra work for me to answer questions and give help in private messages and no one else gets the benefit of the answer.

 

 

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •