Page 1 of 2 12 LastLast
Results 1 to 10 of 12
  1. #1
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491

    Insane Limits Timed Messaging System v2.0

    NOTE: This is not a stand-alone limit, it is a tool for use when creating limits!

    Firstly, a big THANK YOU to PapaCharlie9, the core of this system is basically just a modified version of his code and without him, I'd still be sitting here scratching my head wondering how to do this!

    Secondly, I have tested this ONLY with BF4, as such, I do not know if this will work with any other flavour of Battlefield!

    What this is:

    I recently had the need to send multiple time delayed messages from some limits. Sure I could use Insane Limits' delayed variants of SendGlobalMessage, SendPlayerMessage, SendPlayerYell, etc., but, some of the messages I wanted to send consisted of up to 7 lines of text and I found that Insane Limits' variants would sometimes display these messages in the wrong order, e.g.:
    I wanted:

    Line 1.
    Line 2.
    Line 3.
    Line 4.

    But sometimes got something like:

    Line 1.
    Line 3.
    Line 4.
    Line 2.
    Naturally, this is not very good when the lines of text are, for instance, instructions to a player. The jumbled order would then be confusing.
    A second option would be to use a short lived thread that sleeps for the required duration then displays the messages using the standard Insane Limits message functions. This is fine if your sending the messages to all players simultaneously (globally) at the end of the round, that would only require a single short lived thread OnRoundOver, but what if I wanted to send the messages to individual players OnSpawn or OnKill etc.? This would require multiple threads, and too many threads can crash PRoCon. For example, say you had a 64 slot server and wanted the timed messages to be sent per player OnSpawn, the potential is there for 64 short lived threads. Such a scenario would be very undesirable and could potentially crash PRoCon.

    The solution, use a single long lived background worker thread in combination with a FIFO (First In First Out) queue. Such a thread could quite happily run without impacting on Insane Limits or PRoCon's performance and without crashing the layer/client, that is exactly what this tool is.

    This system basically consists of a single core limit that creates and starts the worker thread. The remainder of the system are instructions and examples on how to use it. This is the Insane Limits Timed Messaging System (ILTMS) core limit (all variable names in this code, the instructions & examples were chosen so as to avoid conflicting with your own variable names):

    IMPORTANT: If you are upgrading from an older version of ILTMS, you will need to firstly disable the ILTMS Core limit then restart your PRoCon layer. You can then replace and enable the ILTMS Core limit with this one.

    ILTMS - Core

    Create a new limit to evaluate OnJoin. Set action to None.

    Set first_check to this Code:
    Code:
    // Insane Limits Timed Messaging System - Core
    // v2.0 - OnJoin
    //
    
    // Check if the thread has already been created
    if (!plugin.Data.issetObject("_ILTMS_THREAD_"))
    {
        // The thread has not been created so create it
        Thread	_Thread = new Thread(
            new ThreadStart(
                delegate
                {
                    // Retrieve the queue
                    Queue<Dictionary<String, Object>> _Queue = (Queue<Dictionary<String, Object>>) plugin.Data.getObject("_ILTMS_QUEUE_");
                    List<Dictionary<String, Object>> _Active = new List<Dictionary<String, Object>>();
    
                    // The thread's main loop
                    while (plugin.Data.getBool("_ILTMS_FLAG_"))
                    {
                        Dictionary<String, Object> _MsgBlock = null;
    
                        lock (_Queue)
                        {
                            if (_Active.Count == 0 && _Queue.Count == 0)
                            {
                                Monitor.Wait(_Queue);
                                if (!plugin.Data.getBool("_ILTMS_FLAG_")) return;
                            }
                            if (_Queue.Count > 0) _MsgBlock = _Queue.Dequeue();
                        }
                        if (_MsgBlock != null) _Active.Add(_MsgBlock);
    
                        // Wait for 1 second if required
                        if (_Active.Count > 0)
                        {
                            Thread.Sleep(1000);
                            if (!plugin.Data.getBool("_ILTMS_FLAG_")) return;
    
                            // Process the active messages
                            List<Dictionary<String, Object>> _Waiting = new List<Dictionary<String, Object>>();
                            foreach (Dictionary<String, Object> _Current in _Active)
                            {
                                Object _oDelay = null;
    
                                if (_Current.TryGetValue("time", out _oDelay))
                                {
                                    int _iDelay = Convert.ToInt32(_oDelay);
                                    // Store the inital delay for system use
                                    if (!_Current.ContainsKey("idly"))
                                    {
                                        _Current.Add("idly", _iDelay);
                                    }
                                    _iDelay = _iDelay - 1;
                                    if (_iDelay <= 0)
                                    {
                                        // Time for this message block has elapsed so process and output it
    
                                        // Target audience
                                        List<String> _lTaud = new List<String>();
                                        List<String> _lMsgs = new List<String>();
                                        List<int> _lLoop = new List<int>();
                                        List<Object> _lAdel = new List<Object>();
                                        Object _oTaud = null;
                                        Object _oMsgs = null;
                                        Object _oYell = null;
                                        Object _oYltm = null;
                                        Object _oAdel = null;
                                        Object _oData = null;
                                        Object _oLoop = null;
                                        Object _oIdly = null;
                                        String _sTaud = "";
                                        String _Yell = "";
                                        bool _Plyr = false;
                                        int _Team = 0;
                                        int _Squad = 0;
                                        int _Yltm = 0;
                                        int _Idly = 0;
                                        int _Loop = 0;
                                        int _Rsnd = 0;
    
                                        if (_Current.TryGetValue("taud", out _oTaud))
                                        {
                                            // Target audience supplied so extract the data
                                            _sTaud = Convert.ToString(_oTaud);
                                            // Determine if the supplied target audience is a player, team, squad or all players
                                            if (_sTaud.Length != 0)
                                            {
                                                if (_sTaud.ToLower() != "all")
                                                {
                                                    if (_sTaud.IndexOf(" ") > 0)
                                                    {
                                                        if (_sTaud.Substring(0, _sTaud.IndexOf(" ")).ToLower().Trim() == "player")
                                                        {
                                                            // The target is a specific player
                                                            _sTaud = _sTaud.Remove(0, _sTaud.IndexOf(" ")).Trim();
                                                            // Make sure the supplied player is in the server
                                                            if (_sTaud.Length != 0)
                                                            {
                                                                // Player name supplied so confirm existence
                                                                PlayerInfoInterface _Player = plugin.GetPlayer(_sTaud, false);
                                                                if (_Player != null)
                                                                {
                                                                    // Player is in the server
                                                                    _lTaud.Add("player");
                                                                    _lTaud.Add(_Player.Name);
                                                                    _Plyr = true;
                                                                }
                                                                else
                                                                {
                                                                    // Player is not in the server so assume target audience is all players
                                                                    _lTaud.Add("player");
                                                                    _Plyr = false;
                                                                }
                                                            }
                                                            else
                                                            {
                                                                // Player name wasn't supplied so assume the target is all players
                                                                _lTaud.Add("player");
                                                                _Plyr = false;
                                                            }
                                                        }
                                                        else if (_sTaud.Substring(0, _sTaud.IndexOf(" ")).ToLower().Trim() == "team")
                                                        {
                                                            // The target is a specific team
                                                            _sTaud = _sTaud.Remove(0, _sTaud.IndexOf(" ")).Trim();
                                                            if (_sTaud.Length != 0)
                                                            {
                                                                // Team ID supplied so confirm it is valid
                                                                _Team = Convert.ToInt32(_sTaud);
                                                                if (_Team >= 1 && _Team <= 4)
                                                                {
                                                                    // Team ID is within valid range so ensure it is valid for the current gamemode
                                                                    if (_Team >= 3)
                                                                    {
                                                                        if (server.Gamemode == "SquadDeathMatch0")
                                                                        {
                                                                            // Team ID is valid
                                                                            _lTaud.Add("team");
                                                                            _lTaud.Add(_Team.ToString());
                                                                        }
                                                                        else
                                                                        {
                                                                            // Team ID is invalid for the current gamemode
                                                                            _lTaud.Add("all");
                                                                        }
                                                                    }
                                                                    else
                                                                    {
                                                                        // Team ID is valid
                                                                        _lTaud.Add("team");
                                                                        _lTaud.Add(_Team.ToString());
                                                                    }
                                                                }
                                                                else
                                                                {
                                                                    // Team ID is invalid so assume the target audience is all players
                                                                    _lTaud.Add("all");
                                                                }
                                                            }
                                                            else
                                                            {
                                                                // Team ID wasn't supplied so assume the target is all players
                                                                _lTaud.Add("all");
                                                            }
                                                        }
                                                        else if (_sTaud.Substring(0, _sTaud.IndexOf(" ")).ToLower().Trim() == "squad")
                                                        {
                                                            // Target is a specific squad
                                                            _sTaud = _sTaud.Remove(0, _sTaud.IndexOf(" ")).Trim();
                                                            if (_sTaud.Length != 0)
                                                            {
                                                                if (_sTaud.IndexOf(" ") > 0)
                                                                {
                                                                    String _sSquad = _sTaud.Remove(0, _sTaud.IndexOf(" ")).Trim();
                                                                    _Squad = Convert.ToInt32(_sSquad);
                                                                }
                                                                else
                                                                {
                                                                    // Squad ID wasn't supplied so assume the target is all players not in a squad
                                                                    _Squad = 0;
                                                                }
                                                                String _sTeam = _sTaud.Substring(0, _sTaud.IndexOf(" ")).Trim();
                                                                _Team = Convert.ToInt32(_sTeam);
                                                                // Check if team ID is within valid range
                                                                if (_Team >= 1 && _Team <= 4)
                                                                {
                                                                    // Team ID is within valid range so ensure it is valid for the current gamemode
                                                                    if (_Team >= 3)
                                                                    {
                                                                        if (server.Gamemode != "SquadDeathMatch0")
                                                                        {
                                                                            // Team ID is invalid
                                                                            _lTaud.Add("all");
                                                                            _Team = 0;
                                                                        }
                                                                    }
                                                                    if (_Team > 0)
                                                                    {
                                                                        // Check if squad ID is valid
                                                                        if (_Squad >= 0 && _Squad <= 32)
                                                                        {
                                                                            // Squad ID is valid
                                                                            _lTaud.Add("squad");
                                                                            _lTaud.Add(_Team.ToString());
                                                                            _lTaud.Add(_Squad.ToString());
                                                                        }
                                                                        else
                                                                        {
                                                                            // Squad ID is invalid
                                                                            _lTaud.Add("all");
                                                                        }
                                                                    }
                                                                    else
                                                                    {
                                                                        // Team ID is invalid
                                                                        _lTaud.Add("all");
                                                                    }
                                                                }
                                                                else
                                                                {
                                                                    // Team ID is invalid so assume target is all players
                                                                    _lTaud.Add("all");
                                                                }
                                                            }
                                                            else
                                                            {
                                                                // Team & squad IDs weren't supplied so assume the target is all players
                                                                _lTaud.Add("all");
                                                            }
                                                        }
                                                        else
                                                        {
                                                            // Target must be all players
                                                            _lTaud.Add("all");
                                                        }
                                                    }
                                                    else
                                                    {
                                                        // Unknown target audience, so assume the target is all players
                                                        _lTaud.Add("all");
                                                    }
                                                }
                                                else
                                                {
                                                    // Target audience is all players
                                                    _lTaud.Add("all");
                                                }
                                            }
                                            else
                                            {
                                                // Unknown or not really supplied target audience, so assume the target is all players
                                                _lTaud.Add("all");
                                            }
                                        }
                                        else
                                        {
                                            // No target audience supplied so assume the target is all players
                                            _lTaud.Add("all");
                                        }
    
                                        if (_Current.TryGetValue("chat", out _oMsgs))
                                        {
                                            // List of chat message lines supplied so extract the data
                                            if (_oMsgs != null)
                                            {
                                                _lMsgs = (List<String>) _oMsgs;
                                                // Ensure there are actual messages
                                                if (_lMsgs.Count > 0)
                                                {
                                                    // Chat Messages supplied so ensure there's no more than 7 lines
                                                    if (_lMsgs.Count > 7)
                                                    {
                                                        // More than 7 lines so truncate
                                                        _lMsgs.RemoveRange(7, _lMsgs.Count - 7);
                                                        _lMsgs.TrimExcess();
                                                    }
                                                }
                                                else
                                                {
                                                    // Chat messages weren't supplied
                                                    _lMsgs.Clear();
                                                    _lMsgs.TrimExcess();
                                                }
                                            }
                                        }
    
                                        if (_Current.TryGetValue("yell", out _oYell))
                                        {
                                            // Yell message supplied so retrieve the data
                                            _Yell = Convert.ToString(_oYell);
                                            if (_Current.TryGetValue("yltm", out _oYltm))
                                            {
                                                // Yell duration supplied so retrieve the data
                                                _Yltm = Convert.ToInt32(_oYltm);
                                                // Ensure the yell duration is a minimum of 1 second
                                                if (_Yltm < 1) _Yltm = 1;
                                            }
                                        }
    
                                        // Output any given message(s)
                                        if (_lMsgs.Count > 0)
                                        {
                                            // Messages exist
                                            for (int i = 0; i < _lMsgs.Count; i++)
                                            {
                                                // Output to the correct target
                                                switch (_lTaud.Count)
                                                {
                                                    case 3:
                                                        // Squad message(s)
                                                        plugin.ServerCommand("admin.say", _lMsgs[i], _lTaud[0], _lTaud[1], _lTaud[2]);
                                                        break;
                                                    case 2:
                                                        // Team or Player message(s)
                                                        if (_lTaud[0] == "player" && _Plyr == false) break;
                                                        plugin.ServerCommand("admin.say", _lMsgs[i], _lTaud[0], _lTaud[1]);
                                                        break;
                                                    default:
                                                        // Global message(s)
                                                        plugin.ServerCommand("admin.say", _lMsgs[i], _lTaud[0]);
                                                        break;
                                                }
                                            }
                                        }
    
                                        // Output any given yell message
                                        if (_Yell != "")
                                        {
                                            // Output to the correct target
                                            switch (_lTaud.Count)
                                            {
                                                case 3:
                                                    // Squad yell
                                                    plugin.ServerCommand("admin.yell", _Yell, _Yltm.ToString(), _lTaud[0], _lTaud[1], _lTaud[2]);
                                                    break;
                                                case 2:
                                                    // Team or Player yell
                                                    if (_lTaud[0] == "player" && _Plyr == false) break;
                                                    plugin.ServerCommand("admin.yell", _Yell, _Yltm.ToString(), _lTaud[0], _lTaud[1]);
                                                    break;
                                                default:
                                                    // Global yell
                                                    plugin.ServerCommand("admin.yell", _Yell, _Yltm.ToString(), _lTaud[0]);
                                                    break;
                                            }
                                        }
    
                                        // Action delegate
                                        if (_Current.TryGetValue("adel", out _oAdel))
                                        {
                                            if (_oAdel != null)
                                            {
                                                _lAdel = (List<Object>) _oAdel;
                                                if (_lAdel.Count == 2)
                                                {
                                                    Action<Object> _Adel = (Action<Object>) _lAdel[0];
                                                    _Adel(_lAdel[1]);
                                                }
                                            }
                                        }
    
                                        // Repeats & reschedules
                                        if (_Current.TryGetValue("loop", out _oLoop))
                                        {
                                            if (_oLoop != null)
                                            {
                                                _lLoop = (List<int>) _oLoop;
                                                if (_lLoop.Count != 0)
                                                {
                                                    _Loop = _lLoop[0];
                                                    if (_lLoop.Count > 1) _Rsnd = _lLoop[1];
                                                    if (_Current.TryGetValue("idly", out _oIdly))
                                                    {
                                                        _Idly = Convert.ToInt32(_oIdly);
                                                    }
                                                    if (_Loop != 0)
                                                    {
                                                        Dictionary<String, Object> _Rescheduled = new Dictionary<String, Object>();
                                                        _Rescheduled.Add("idly", _Idly);
                                                        if (_Loop != -1)
                                                        {
                                                            _Loop = _Loop - 1;
                                                        }
                                                        _lLoop[0] = _Loop;
                                                        _Rescheduled.Add("loop", _lLoop);
                                                        if (_Rsnd != 0)
                                                        {
                                                            _Idly = _Rsnd + _Idly;
                                                        }
                                                        _Rescheduled.Add("time", _Idly);
                                                        switch (_lTaud.Count)
                                                        {
                                                            case 3:
                                                                _Rescheduled.Add("taud", _lTaud[0] + " " + _lTaud[1] + " " + _lTaud[2]);
                                                                break;
                                                            case 2:
                                                                _Rescheduled.Add("taud", _lTaud[0] + " " + _lTaud[1]);
                                                                break;
                                                            default:
                                                                _Rescheduled.Add("taud", _lTaud[0]);
                                                                break;
                                                        }
                                                        if (_lMsgs != null) _Rescheduled.Add("chat", _lMsgs);
                                                        if (_Yell != "")
                                                        {
                                                            _Rescheduled.Add("yell",  _Yell);
                                                            _Rescheduled.Add("yltm", _Yltm);
                                                        }
                                                        if (_lAdel.Count == 2) _Rescheduled.Add("adel", _lAdel);
                                                        _Waiting.Add(_Rescheduled);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        _Current["time"] = _iDelay;
                                        _Waiting.Add(_Current);
                                    }
                                }
                            }
                            _Active = _Waiting;
                        }
                    }
                }
            )
        );
    
        // Create and store the queue
        Queue<Dictionary<String, Object>> _oQueue = new Queue<Dictionary<String, Object>>();
        plugin.Data.setObject("_ILTMS_QUEUE_", _oQueue);
    
        // Setup thread details
        _Thread.IsBackground = true;
        _Thread.Name = "Insane Limits Timed Messaging System";
        // Set the system's version, store the thread and set it as active
        plugin.Data.setString("_ILTMS_VERSION_", "2.0");
        plugin.Data.setObject("_ILTMS_THREAD_", _Thread);
        plugin.Data.setBool("_ILTMS_FLAG_", true);
        // Start the thread
        _Thread.Start();
    
        // Send a dummy message to ensure the system is ready
        Dictionary<String, Object> _oDummy = new Dictionary<String, Object>();
        _oDummy.Add("time", 1);
        lock (_oQueue)
        {
            _oQueue.Enqueue(_oDummy);
            Monitor.Pulse(_oQueue);
        }
    
        // Log to PRoCon
        plugin.ConsoleWrite("^b^1ILTMS^0^n: Insane Limits Timed Messaging System v2.0 initialized and ready for use!");
        plugin.PRoConChat("^b^1ILTMS^0^n: Insane Limits Timed Messaging System v2.0 initialized and ready for use!");
        plugin.PRoConEvent("ILTMS: Insane Limits Timed Messaging System v2.0 initialized and ready for use!", "Insane Limits");
    }
    
    return false;
    Why is the core on an OnJoin event? I chose this event because it ensures the ILTMS is created and started, if it's not already running, when the first player joins the server.


    How to use the system:

    You use this system within your own limits. To do so, there are a few things you need to know:

    1. You should firstly test if the system is active before you attempt to use it.
    2. This system uses a shared queue which you will need to retrieve before you can pass messages to it.
    3. You use a Dictionary<String, Object> to pass a message block to the queue.
    4. Since we are dealing with a threaded system here, you need to lock the queue before passing on the message blocks.
    5. When you are completely finished with the system and want it to shutdown, you must set it's active flag to false.

    Testing if active & shutting down the system:

    This is how we test if the system is active and at the same time retrieve the shared queue (code is commented):
    Code:
    // First we create a global variable to hold the shared queue.
    Queue<Dictionary<String, Object>> _Queue = null;
    
    // Now we can actually test if the system is active and set the shared queue variable in the process.
    if (plugin.Data.issetObject("_ILTMS_THREAD_") && plugin.Data.issetObject("_ILTMS_QUEUE_") && plugin.Data.issetBool("_ILTMS_FLAG_"))
    {
    	if (plugin.Data.getBool("_ILTMS_FLAG_"))
    	{
    		_Queue = (Queue<Dictionary<String, Object>>) plugin.Data.getObject("_ILTMS_QUEUE_");
    	}
    }
    
    // We can now test if the queue variable is equal to null, if so this would indicate that the system is inactive.
    // In this case we're just going to exit but you could instead conditionally do other things.
    if (_Queue == null) return false;
    To let the system know it should shutdown, we must set it's active flag to false. We do this like so:
    Code:
    // Let the system know it should shutdown.
    // DO NOT USE THIS IF YOU WANT THE SYSTEM TO REMAIN ACTIVE FOR OTHER LIMITS!!!
    plugin.Data.setBool("_ILTMS_FLAG_", false);

    Message Block details:

    A message block is actually just a Dictionary with String keys and Object values (Dictionary<String, Object>), it contains all the information the system needs to send the message(s). The key/value pairs are as follows:

    Key Value
    Delay "time" Integer The message delay in seconds (minimum 1 second)
    Target "taud" String The target audience string can be:
    "all"
    "player Name"
    "team TeamId"
    "squad TeamId SquadId"
    Message(s) "chat" List<String> The chat line messages (maximum 7 lines)
    Yell "yell" String The yell message(s)
    Duration "yltm" Integer The yell message duration in seconds (minimum 1 second)
    Repeats/Reschedules "loop" List<int> This is used for repeating and/or rescheduling message blocks.
    Callback "adel" List<Object> This is used for passing an Action<T> delegate that is called after the delay.

    Delay is the number of seconds before the message block will be displayed. This is an integer value of 1 or higher, the value will default to 1 second if not supplied.
    Target is the target audience for the message block. This is a string and can be all players, a player, a team or a squad.
    Chat is the in-game message lines. This is a list of strings with each element representing a single line.
    Yell is the in-game yell message. This is a string representing the message to yell.
    Duration is the yell message duration. This is an integer value of 1 or higher, the value will default to 1 second if not supplied.
    Repeats/Reschedules is for repeating and/or rescheduling message blocks. This is a list of integers and must contain 2 elements. The first element represents the number of repeats, this can be -1 for infinite repeats, 0 for no repeats or a value of 1 or higher. The second element represents a rescheduling time in seconds. This can be 0 for no rescheduling or a value of 1 or higher. The inital message delay time is added to the rescheduling time, e.g. if you send a message with an initial delay of 15 seconds and have a rescheduling time of 600 (10 minutes), the message will resecheduled for 615 seconds (10 minutes and 15 seconds). When rescheduling, you must have at least 1 repeat.
    Callback is used for passing an Action<T> delegate to be called after the delay. This is a list of objects and must contain 2 elements. The first element represents the action delegate. The second element represents the action delegate's parameter variable, this can be null. The action delegate must accept a parameter of type Object, e.g. the delegate's definition must be:
    Code:
    Action<Object> DelegateName = delegate(Object ParameterVariableName) {};
    Example message blocks:
    Code:
    // Setup the dictionary variable & chat message variable.
    Dictionary<String, Object> myMsgBlock = null;
    List<String> myMsgs = null;
    List<Object> myActDel = null;
    List<int> myRepeats = null;
    
    // Setup the chat message list for 7 lines of text.
    myMsgs = new List<String>();
    myMsgs.Add("Line 1.");
    myMsgs.Add("Line 2.");
    myMsgs.Add("Line 3.");
    myMsgs.Add("Line 4.");
    myMsgs.Add("Line 5.");
    myMsgs.Add("Line 6.");
    myMsgs.Add("Line 7.");
    
    // Setup the message block for a global message with yell.
    myMsgBlock = new Dictionary<String, Object>();
    myMsgBlock.Add("time", 30);                           // Delay = 30 seconds.
    myMsgBlock.Add("taud", "all");                      // Target = All players (global).
    myMsgBlock.Add("chat", myMsgs);                    // Message(s).
    myMsgBlock.Add("yell", "Yell message.");    // Yell message.
    myMsgBlock.Add("yltm", 8);                            // Yell duration = 8 seconds.
    
    // Setup a repeat/rescheduling block.
    myRepeats = new List<int>();
    myRepeats.Add(5);   // The number of repeats.
    myRepeats.Add(0);   // The rescheduling time in seconds.
    
    // Setup the message block for a player message only with 5 repeats.
    myMsgBlock = new Dictionary<String, Object>();
    myMsgBlock.Add("time", 30);                                       // Delay = 30 seconds.
    myMsgBlock.Add("taud", "player " + player.Name);    // Target = A players.
    myMsgBlock.Add("chat", myMsgs);                                // Message(s).
    myMsgBlock.Add("loop", myRepeats);                           // The repeat/rescheduling block.
    
    // Setup a repeat/rescheduling block.
    myRepeats = new List<int>();
    myRepeats.Add(-1);   // The number of repeats (infinite).
    myRepeats.Add(600); // The rescheduling time in seconds.
    
    // Setup the message block for a team yell only with infite 10 minute rescheduling.
    myMsgBlock = new Dictionary<String, Object>();
    myMsgBlock.Add("time", 30);                                                       // Delay = 30 seconds.
    myMsgBlock.Add("taud", "team " + player.TeamId.ToString()); // Target = A team.
    myMsgBlock.Add("yell", "Yell message.");                                 // Yell message.
    myMsgBlock.Add("yltm", 8);                                                         // Yell duration = 8 seconds.
    myMsgBlock.Add("loop", myRepeats);                                           // The repeat/rescheduling block.
    
    // Create a callback delegate
    Action<Object> myCallback = delegate(Object myParam)
                                                  {
                                                      if (myParam != null)
                                                      {
                                                          plugin.PRoConChat(Convert.ToString(myParam));
                                                      }
                                                  };
    
    // Setup a callback block.
    myActDel = new List<Object>();
    myActDel.Add(myCallback);                                // The Action<T> delegate.
    myActDel.Add("Callback delegate called!");  // The callback's parameter.
    
    // Setup the message block for a squad message with yell and a callback.
    myMsgBlock = new Dictionary<String, Object>();
    myMsgBlock.Add("time", 30);                                                                                                                   // Delay = 30 seconds.
    myMsgBlock.Add("taud", "squad " + player.TeamId.ToString() + " " + player.SquadId.ToString());  // Target = A squad.
    myMsgBlock.Add("chat", myMsgs);                                                                                                            // Message(s).
    myMsgBlock.Add("yell", "Yell message.");                                                                                            // Yell message.
    myMsgBlock.Add("yltm", 8);                                                                                                                    // Yell duration = 8 seconds.
    myMsgBlock.Add("adel", myActDel);                                                                                                        // The callback block.

    How to queue the message blocks:

    To add a message block to the queue, you must first make sure the system is active, then lock the queue, then en-queue the message block, then pulse the shared queue to signal there was a message block queued. This is how that is achieved:
    Code:
    // Add the message block to the system's queue if the system is active.
    if (plugin.Data.getBool("_ILTMS_FLAG_"))
    {
    	lock (_Queue)
    	{
    		_Queue.Enqueue(myMsgBlock);
    		Monitor.Pulse(_Queue);
    	}
    }

    Checking the version:

    You can check the version of the ILTMS like this:
    Code:
    // Check the system's version.
    String ILTMS_Version = "";
    if (plugin.Data.issetString("_ILTMS_VERSION_")) ILTMS_Version = plugin.Data.getString("_ILTMS_VERSION_");
    // ILTMS_Version will now either contain a string representing the version or be "" if not retrieved.
    // In this case we're just going to exit if ILTMS_Version equals "" but you could conditionally do other things.
    if (ILTMS_Version != "") return false;

    Continued in 2nd post...
    Last edited by LCARSx64; 01-09-2014 at 10:18. Reason: Updated to version 2.0

  2. #2
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    Alternative simpler methods to the above:

    Instead of having to remember all of the above queuing and message block building, I've created some delegates to simplify the process.
    The delegates are divided into 2 sections, required and optional. The required delegates need to be included in order to use the optional ones. You may use all or only the needed optional delegates.
    These are the delegates (the code is commented so understanding should be easy):

    Due to space restriction, this zip file contains the delegates: Insane Limits Timed Messaging System Delegates v2.0.zip

    For more on how to use theses delegates, scroll down and see Example #2.


    History:

    v2.0 - Added repeating & rescheduling and a Callback method.
    v1.0 - Initial release.



    Examples:

    The following 2 examples both do exactly the same thing, however, example #1 is a demonstration of how to use the raw format and example #2 demonstrates the use of delegates instead.

    Both examples have 2 commands:
    !teststart - Execute the test.
    !testend - Terminate the ILTMS.


    ILTMS Example #1 - Raw Format

    Create a new limit to evaluate OnAnyChat. Set action to None.

    Set first_check to this Expression:
    Code:
    (plugin.IsInGameCommand(player.LastChat))
    Set second_check to this Code:
    Code:
    // Insane Limits Timed Messaging System - Example #1
    // Raw messaging v2.0
    //
    
    // This example will demonstrate how to send how to test if the system is active and how to build & queue
    // timed messages using the system's raw format.
    
    
    
    // In-game test commands setup.
    //
    // Commands are:
    // !teststart   - Start the test. This creates each of the message blocks and sends them to the Insane Limits Timed Messaging System.
    // !testend     - End the test. This sets the flag which shutsdown the Insane Limits Timed Messaging System.
    //
    String command = "";
    bool canKill = false;
    bool canKick = false;
    bool canBan = false;
    bool canMove = false;
    bool canChangeLevel = false;
    
    // Extract the command.
    command = plugin.ExtractInGameCommand(player.LastChat);
    // Remove excessive command prefixes.
    if ((command.Length != 0) && (plugin.ExtractCommandPrefix(command).Length != 0)) command = plugin.ExtractInGameCommand(command);
    // Make sure the command really exists.
    if (null == command || command.Length == 0) return false;
    // Match on the !teststart and !testend.
    if (!Regex.Match(command, @"^\b(TESTSTART|TESTEND)\b", RegexOptions.IgnoreCase).Success) return false;
    // Remove any arguments.
    if (command.IndexOf(" ") != -1) command = command.Substring(0, command.IndexOf(" ")).ToLower().Trim();
    // Make sure it's an Admin that called this command.
    if (!plugin.CheckAccount(player.Name, out canKill, out canKick, out canBan, out canMove, out canChangeLevel))
    {
        // Not an Admin.
        return false;
    }
    else
    {
        if (!canKill && !canKick && !canBan && !canMove && !canChangeLevel)
        {
            // Not a full Admin
            return false;
        }
    }
    
    
    
    // ***** 1ST SECTION - START OF ILTMS NEEDED CODE ***** //
    
    // First we need to try to setup the queue, this will also allow us to see if the system is active or not.
    Queue<Dictionary<String, Object>> _Queue = null;
    if (plugin.Data.issetObject("_ILTMS_THREAD_") && plugin.Data.issetObject("_ILTMS_QUEUE_") && plugin.Data.issetBool("_ILTMS_FLAG_"))
    {
        if (plugin.Data.getBool("_ILTMS_FLAG_"))
        {
            _Queue = (Queue<Dictionary<String, Object>>) plugin.Data.getObject("_ILTMS_QUEUE_");
        }
    }
    // Test if _Queue is null, if so then the system is not active so we'll just exit.
    if (_Queue == null) return false;
    
    // ***** END OF 1ST SECTION - ILTMS NEEDED CODE ***** //
    
    
    
    // Process the commands.
    switch (command)
    {
        case "teststart":
            // Start of the test.
            plugin.SendPlayerMessage(player.Name, "Starting Insane Limits Timed Messaging System test...");
            // Get and display the ILTMS version.
            String ILTMS_Version = "";
            if (plugin.Data.issetString("_ILTMS_VERSION_")) ILTMS_Version = plugin.Data.getString("_ILTMS_VERSION_");
            if (ILTMS_Version != "") plugin.SendPlayerMessage(player.Name, "ILTMS Version: " + ILTMS_Version);
    
            // Setuo the variables we are going to use.
            Dictionary<String, Object> myMsgBlock = null;
            List<String> myMsgs = null;
            List<Object> myActionDelegate = null;
            List<int> myRepeats = null;
    
            // First let's send a 7 line message & 2 line yell to all players after a 30 second delay (yell duration will be 8 seconds).
            // Setup the messages.
            myMsgs = new List<String>();
            myMsgs.Add("Global message - Line 1 of 7.");
            myMsgs.Add("Global message - Line 2 of 7.");
            myMsgs.Add("Global message - Line 3 of 7.");
            myMsgs.Add("Global message - Line 4 of 7.");
            myMsgs.Add("Global message - Line 5 of 7.");
            myMsgs.Add("Global message - Line 6 of 7.");
            myMsgs.Add("Global message - Line 7 of 7.");
    
            // Setup the dictionary.
            myMsgBlock = new Dictionary<String, Object>();
            myMsgBlock.Add("time", 30);                                                                                                  // The delay in seconds.
            myMsgBlock.Add("taud", "all");                                                                                             // The target audience.
            myMsgBlock.Add("chat", myMsgs);                                                                                           // The chat message(s).
            myMsgBlock.Add("yell", "Global yell - Line 1 of 2.\nGlobal yell - Line 2 of 2.");   // The yell message(s).
            myMsgBlock.Add("yltm", 8);                                                                                                   // The yell duration in seconds.
    
            // Send the message block to the system's queue if the system is active.
            if (plugin.Data.getBool("_ILTMS_FLAG_"))
            {
                lock (_Queue)
                {
                    _Queue.Enqueue(myMsgBlock);
                    Monitor.Pulse(_Queue);
                }
            }
    
            // Next we're going to send a 4 line message without a yell to the current player with a 15 second delay.
            // Again, we need to setup the messages.
            myMsgs = new List<String>();
            myMsgs.Add("Player message - Line 1 of 4");
            myMsgs.Add("Player message - Line 2 of 4");
            myMsgs.Add("Player message - Line 2 of 4");
            myMsgs.Add("Player message - Line 2 of 4");
    
            // Setup the dictionary.
            myMsgBlock = new Dictionary<String, Object>();
            myMsgBlock.Add("time", 15);                                       // The delay in seconds.
            myMsgBlock.Add("taud", "player " + player.Name);    // The target audience.
            myMsgBlock.Add("chat", myMsgs);                                // The chat message(s).
    
            // Send the message block to the system's queue if the system is active.
            if (plugin.Data.getBool("_ILTMS_FLAG_"))
            {
                lock (_Queue)
                {
                    _Queue.Enqueue(myMsgBlock);
                    Monitor.Pulse(_Queue);
                }
            }
    
            // This time we're going to send a single line yell message only to the current player's team with a 12 second delay (yell duration will be 5 seconds).
            // We don't need the messages since we are only sending a yell message, so setup the dictionary.
            myMsgBlock = new Dictionary<String, Object>();
            myMsgBlock.Add("time", 12);                                                       // The delay in seconds.
            myMsgBlock.Add("taud", "team " + player.TeamId.ToString()); // The target audience.
            myMsgBlock.Add("yell", "Team yell - Line 1 of 1.");             // The yell message(s).
            myMsgBlock.Add("yltm", 5);                                                         // The yell duration in seconds.
    
            // Send the message block to the system's queue if the system is active.
            if (plugin.Data.getBool("_ILTMS_FLAG_"))
            {
                lock (_Queue)
                {
                    _Queue.Enqueue(myMsgBlock);
                    Monitor.Pulse(_Queue);
                }
            }
    
            // For the next example, we are going to send both a 2 line message & a 3 line yell to the current player's squad with a 6 second delay (yell duration will be 5 seconds).
            // Yet again, setup the messages.
            myMsgs = new List<String>();
            myMsgs.Add("Squad message - Line 1 of 2");
            myMsgs.Add("Squad message - Line 2 of 2");
    
            // Setup the dictionary.
            myMsgBlock = new Dictionary<String, Object>();
            myMsgBlock.Add("time", 6);                                                                                                                                             // The delay in seconds.
            myMsgBlock.Add("taud", "squad " + player.TeamId.ToString() + " " + player.SquadId.ToString());                           // The target audience.
            myMsgBlock.Add("chat", myMsgs);                                                                                                                                     // The chat message(s).
            myMsgBlock.Add("yell", "Squad yell - Line 1 of 3.\nSquad yell - Line 2 of 3.\nSquad yell - Line 3 of 3.");  // The yell message(s).
            myMsgBlock.Add("yltm", 5);                                                                                                                                              // The yell duration in seconds.
    
            // Send the message block to the system's queue if the system is active.
            if (plugin.Data.getBool("_ILTMS_FLAG_"))
            {
                lock (_Queue)
                {
                    _Queue.Enqueue(myMsgBlock);
                    Monitor.Pulse(_Queue);
                }
            }
    
            // Now we're going to take advantage of some new features in ILTMS v2.0.
            // First we're going to send a callback delegate which will simply display the server uptime to the current player, this will repeat 10 times (Inital + 9 times), the delay will be 20 seconds.
            Action<Object> myCallback = delegate(Object cbPlayer)
                                                           {
                                                               if (cbPlayer != null)
                                                               {
                                                                   String pName = Convert.ToString(cbPlayer);
                                                                   plugin.SendPlayerMessage(pName, "Server Uptime: " + Convert.ToString(server.TimeUp));
                                                               }
                                                           };
    
            // Setup the repeats/rescheduling.
            myRepeats = new List<int>();
            myRepeats.Add(9);   // The number of repeats.
            myRepeats.Add(0);   // The rescheduling time in seconds.
    
            // Setup the callback.
            myActionDelegate = new List<Object>();
            myActionDelegate.Add(myCallback);    // The actual Action<T> delegate.
            myActionDelegate.Add(player.Name);  // The parameter for the callback delegate.
    
            // Setup the dictionary.
            myMsgBlock = new Dictionary<String, Object>();
            myMsgBlock.Add("time", 20);                           // The delay in seconds.
            myMsgBlock.Add("loop", myRepeats);               // The repeats/rescheduling block.
            myMsgBlock.Add("adel", myActionDelegate);   // The callback block.
    
            // Send the message block to the system's queue if the system is active & if the system's version is at least v2.0.
            if (plugin.Data.getBool("_ILTMS_FLAG_"))
            {
                String _sVers = "";
                double _Vers = 0.0;
                if (plugin.Data.issetString("_ILTMS_VERSION_")) _sVers = plugin.Data.getString("_ILTMS_VERSION_");
                if (_sVers != "") _Vers = Convert.ToDouble(_sVers);
                if (_Vers >= 2.0)
                {
                    lock (_Queue)
                    {
                        _Queue.Enqueue(myMsgBlock);
                        Monitor.Pulse(_Queue);
                    }
                }
            }
    
            // The final example will send a 3 line scheduled yell message to the current player, the initial delay will be 15 seconds and then will reschedule every 10 minutes (10 minutes & 15 seconds each time) indefinitely.
            myRepeats = new List<int>();
            myRepeats.Add(-1);        // The number of repeats (in this case infinite).
            myRepeats.Add(10*60);   // The rescheduling time in seconds (10 minutes).
    
            // Setup the dictionary.
            myMsgBlock = new Dictionary<String, Object>();
            myMsgBlock.Add("time", 15);                                                                                                                                                                             // The delay in seconds.
            myMsgBlock.Add("taud", "player " + player.Name);                                                                                                                                          // The target audience.
            myMsgBlock.Add("yell", "Rescheduled yell - Line 1 of 3.\nRescheduled yell - Line 2 of 3.\nRescheduled yell - Line 3 of 3.");    // The yell message(s).
            myMsgBlock.Add("yltm", 8);                                                                                                                                                                               // The yell duration in seconds.
            myMsgBlock.Add("loop", myRepeats);                                                                                                                                                                 // The repeats/rescheduling block.
    
            // Send the message block to the system's queue if the system is active & if the system's version is at least v2.0.
            if (plugin.Data.getBool("_ILTMS_FLAG_"))
            {
                String _sVers = "";
                double _Vers = 0.0;
                if (plugin.Data.issetString("_ILTMS_VERSION_")) _sVers = plugin.Data.getString("_ILTMS_VERSION_");
                if (_sVers != "") _Vers = Convert.ToDouble(_sVers);
                if (_Vers >= 2.0)
                {
                    lock (_Queue)
                    {
                        _Queue.Enqueue(myMsgBlock);
                        Monitor.Pulse(_Queue);
                    }
                }
            }
    
            break;
        case "testend":
            // Shutdown the Insane Limits Timed Messaging System.
            plugin.SendPlayerMessage(player.Name, "Disabling the Insane Limits Timed Messaging System!");
            plugin.Data.setBool("_ILTMS_FLAG_", false);
    
            break;
    }
    
    return false;

    Continue in 3rd post...
    Last edited by LCARSx64; 01-09-2014 at 10:17. Reason: Updated to version 2.0

  3. #3
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491

    ILTMS Example #2 - Using Delegates

    Create a new limit to evaluate OnAnyChat. Set action to None.

    Set first_check to this Expression:
    Code:
    (plugin.IsInGameCommand(player.LastChat))
    Set second_check to this Code:

    Again, due to space restriction, this zip file contains the code for Example #2: Insane Limits Timed Messaging System Example 2 v2.0.zip


    End of posts.
    Last edited by LCARSx64; 01-09-2014 at 10:17. Reason: Updated to version 2.0

  4. #4
    Good stuff! Thanks for doing this.

    A variation to think about. As it stands, all of the message processing is in the Queue loop thread. So if you wanted to add a new message type or variable, you'd have to modify that code.

    How about adding "callback" as a message an take an Action delegate as the value? Add "context" as another message and take Object as a value, to be passed back when the Action delegate is called? Then you can plug in whatever callback function you want and have it executed after a delay.

    This is a common use case I get asked for all the time and have to explain that anything with timers is too hard. Like, after doing some kind of action or punishment, like a vote to nuke the other team, impose some kind of cooldown period. This can be done with a plugin.Data flag that is set to true (meaning, cooledown is in force, no votes allowed). Create an Action delegate that sets the flag to false and then queue it with a delay of whatever the cooldown is.

    Which reminds me, if you wanted the message to be rescheduled after X seconds, how would that be done? Like, after sending 3 messages to a specific player, one message every 15 seconds, I want to wait 10 minutes, and then reschedule the same 3 messages to the same player again.
    Last edited by PapaCharlie9; 27-08-2014 at 17:32.
    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.

  5. #5
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    Quote Originally Posted by PapaCharlie9 View Post
    Good stuff! Thanks for doing this.

    A variation to think about. As it stands, all of the message processing is in the Queue loop thread. So if you wanted to add a new message type or variable, you'd have to modify that code.

    How about adding "callback" as a message an take an Action delegate as the value? Add "context" as another message and take Object as a value, to be passed back when the Action delegate is called? Then you can plug in whatever callback function you want and have it executed after a delay.
    Awesome idea, I'll definitely add that in for v2.

  6. #6
    Sorry, I was editing the message while you were reading it. Take another look, I added stuff.
    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.

  7. #7
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    Hmmm, I'd have to give the resend a bit of thought but I'm sure I could come up with some way of doing it. Actually, I just had an idea, as I typed that last sentence, on how to achieve that.

  8. #8
    Senior Member
    Join Date
    Mar 2014
    Location
    ny
    Posts
    179
    wow awesome work man !!! thumb up

  9. #9
    Senior Member
    Join Date
    Dec 2013
    Location
    Sydney, Australia
    Posts
    491
    Quote Originally Posted by HARDCOREBF View Post
    wow awesome work man !!! thumb up
    Thanks mate

    Updated original posts, code and information with version 2.0.

  10. #10
    That's fantastic! You added both requested features! The callback adel command looks awesome.
    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
  •