Jump to content

Insane Limits Timed Messaging System v2.0


ImportBot

Recommended Posts

Originally Posted by LCARSx64*:

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:

  • You should firstly test if the system is active before you attempt to use it.
  • This system uses a shared queue which you will need to retrieve before you can pass messages to it.
  • You use a Dictionary to pass a message block to the queue.
  • Since we are dealing with a threaded system here, you need to lock the queue before passing on the message blocks.
  • 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), it contains all the information the system needs to send the message(s). The key/value pairs are as follows:


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 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...

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by LCARSx64*:

 

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...

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by LCARSx64*:

 


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.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by PapaCharlie9*:

 

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.

* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by LCARSx64*:

 

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. :biggrin:
* Restored post. It could be that the author is no longer active.
Link to comment

Originally Posted by LCARSx64*:

 

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.

* Restored post. It could be that the author is no longer active.
Link to comment
  • 2 years later...

Originally Posted by HATANO_KENJI*:

 

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

Please let me confirm again!

 

#1

The content of this post we do not have to modify?

 

Because I am poor in English, So do not understand what you mean, If you have trouble also please forgive me.

* Restored post. It could be that the author is no longer active.
Link to comment

Archived

This topic is now archived and is closed to further replies.



  • Our picks

    • Game Server Hosting:

      We're happy to announce that EZRCON will branch out into the game server provider scene. This is a big step for us so please having patience if something doesn't go right in this area. Now, what makes us different compared to other providers? Well, we're going with the idea of having a scaleable server hosting and providing more control in how you set up your server. For example, in Minecraft, you have the ability to control how many CPU cores you wish your server to have access to, how much RAM you want to use, how much disk space you want to use. This type of control can't be offered in a single service package so you're able to configure a custom package the way you want it.

      You can see all the available games here. Currently, we have the following games available.

      Valheim (From $1.50 USD)


      Rust (From $3.20 USD)


      Minecraft (Basic) (From $4.00 USD)


      Call of Duty 4X (From $7.00 USD)


      OpenTTD (From $4.00 USD)


      Squad (From $9.00 USD)


      Insurgency: Sandstorm (From $6.40 USD)


      Changes to US-East:

      Starting in January 2022, we will be moving to a different provider that has better support, better infrastructure, and better connectivity. We've noticed that the connection/routes to this location are not ideal and it's been hard getting support to correct this. Our contract for our two servers ends in March/April respectively. If you currently have servers in this location you will be migrated over to the new provider. We'll have more details when the time comes closer to January. The new location for this change will be based out of Atlanta, GA. If you have any questions/concerns please open a ticket and we'll do our best to answer them.
      • 5 replies
    • Hello All,

      I wanted to give an update to how EZRCON is doing. As of today we have 56 active customers using the services offered. I'm glad its doing so well and it hasn't been 1 year yet. To those that have services with EZRCON, I hope the service is doing well and if not please let us know so that we can improve it where possible. We've done quite a few changes behind the scenes to improve the performance hopefully. 

      We'll be launching a new location for hosting procon layers in either Los Angeles, USA or Chicago, IL. Still being decided on where the placement should be but these two locations are not set in stone yet. We would like to get feedback on where we should have a new location for hosting the Procon Layers, which you can do by replying to this topic. A poll will be created where people can vote on which location they would like to see.

      We're also looking for some suggestions on what else you would like to see for hosting provider options. So please let us know your thoughts on this matter.
      • 4 replies
    • Added ability to disable the new API check for player country info


      Updated GeoIP database file


      Removed usage sending stats


      Added EZRCON ad banner



      If you are upgrading then you may need to add these two lines to your existing installation in the file procon.cfg. To enable these options just change False to True.

      procon.private.options.UseGeoIpFileOnly False
      procon.private.options.BlockRssFeedNews False



       
      • 2 replies
    • I wanted I let you know that I am starting to build out the foundation for the hosting services that I talked about here. The pricing model I was originally going for wasn't going to be suitable for how I want to build it. So instead I decided to offer each service as it's own product instead of a package deal. In the future, hopefully, I will be able to do this and offer discounts to those that choose it.

      Here is how the pricing is laid out for each service as well as information about each. This is as of 7/12/2020.

      Single MySQL database (up to 30 GB) is $10 USD per month.



      If you go over the 30 GB usage for the database then each additional gigabyte is charged at $0.10 USD each billing cycle. If you're under 30GB you don't need to worry about this.


      Databases are replicated across 3 zones (regions) for redundancy. One (1) on the east coast of the USA, One (1) in Frankfurt, and One (1) in Singapore. Depending on the demand, this would grow to more regions.


      Databases will also be backed up daily and retained for 7 days.




      Procon Layer will be $2 USD per month.


      Each layer will only allow one (1) game server connection. The reason behind this is for performance.


      Each layer will also come with all available plugins installed by default. This is to help facilitate faster deployments and get you up and running quickly.


      Each layer will automatically restart if Procon crashes. 


      Each layer will also automatically restart daily at midnight to make sure it stays in tip-top shape.


      Custom plugins can be installed by submitting a support ticket.




      Battlefield Admin Control Panel (BFACP) will be $5 USD per month


      As I am still working on building version 3 of the software, I will be installing the last version I did. Once I complete version 3 it will automatically be upgraded for you.





      All these services will be managed by me so you don't have to worry about the technical side of things to get up and going.

      If you would like to see how much it would cost for the services, I made a calculator that you can use. It can be found here https://ezrcon.com/calculator.html

       
      • 11 replies
    • I have pushed out a new minor release which updates the geodata pull (flags in the playerlisting). This should be way more accurate now. As always, please let me know if any problems show up.

       
      • 9 replies
×
×
  • Create New...

Important Information

Please review our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.