Как сообразить таймер после срабатывания функции с последующей его проверкой C#?

Есть плагин написанный на C# (это для сервера игры Rust Legacy Oxide)

    using System.Collections.Generic;
    using System;
    using System.Reflection;
    using System.Data;
    using UnityEngine;
    using Oxide.Core;
    using RustExtended;
    using Oxide.Core.Plugins;
    using RustProto;
    
    namespace Oxide.Plugins
    {
        [Info("CallAirDrop", "XBOCT", "1.0.2")]
        [Description("Вызов дропа игроками за валюту")]
        class CallAirDrop : RustLegacyPlugin
        {
            const float AirCallTime = 300f;//Время обновления до следующего вызова самолета
            NetUser cachedUser;
            string cachedSteamid;
            string cachedReason;
            string cachedName;
            Vector3 cachedPos;
            string chatName = "MegaPUB";
    
            static string calledAirdrop = "Самолёт уже летит...";
            static string airdropPlayer = "Дроп упадет возле [color red]{0}";
            static string wrongarguments = "Что то не правильно написал, или такого игрока нет";
            
            Dictionary<ulong, DateTime> players = new Dictionary<ulong, DateTime>();
    
            void LoadDefaultConfig() { }
    
            private void CheckCfg<T>(string Key, ref T var)
            {
                if (Config[Key] is T)
                    var = (T)Config[Key];
                else
                    Config[Key] = var;
            }
    
            void Init()
            {
                CheckCfg<string>("Messages: Called 1 airdrop", ref calledAirdrop);
                CheckCfg<string>("Messages: Airdrop on player position", ref airdropPlayer);
                CheckCfg<string>("Messages: Wrong Arguments", ref wrongarguments);
                SaveConfig();
            }
    
            [ChatCommand("air")] //Команда вызова
            void cmdChatBan(NetUser netuser, string command, string[] args)
            {
                ulong money = RustExtended.Economy.GetBalance(netuser.userID);
    
                // Меню информации
                if(args.Length == 0) {
                    rust.SendChatMessage(netuser, chatName, "[COLOR#00BFFF]Вызов [COLOR#d9ae04]Самолета с лутом[COLOR#00BFFF]:");
                    rust.SendChatMessage(netuser, chatName, "➩[COLOR#00BFFF]/air map Случайное место - [COLOR#FFFFFF]Цена: 10 000 Р");
                    rust.SendChatMessage(netuser, chatName, "➩[COLOR#00BFFF]/air me Вызвать себе - [COLOR#FFFFFF]Цена: 50 000 Р");
                    rust.SendChatMessage(netuser, chatName, "➩[COLOR#00BFFF]/air [COLOR#FFFFFF][ник] [COLOR#00BFFF]Вызвать игроку - [COLOR#FFFFFF]Цена: 75 000 Р");
                    return;
                }
    
    
                // Случайную точку
                if (args.Length == 1 && args[0].ToString() == "map")
                { 
                    if (money < 10000)
                    {
                        ulong ost = 10000 - money;
                        rust.SendChatMessage(netuser, "[COLOR#00BFFF]Вам не хватает: [COLOR#d9ae04]" + ost + "[COLOR#00BFFF]. У вас: [COLOR#17c90e]" + money);
                        Puts(netuser + "Попытался вызвать случайный дроп. Не хватает: " + ost + ". Денег всего: " + money);
                        return;
                    } else {
                        RustExtended.Economy.BalanceSub(netuser.userID, 10000);
                        SendReply(netuser, calledAirdrop); SupplyDropZone.CallAirDrop(); return;
                        Puts(netuser + " вызвал дроп в случайную точку. Денег осталось: " +money);
                    }
                }
    
                // Вызвать себе
                if (args.Length == 1 && args[0].ToString() == "me")
                {
                    if (money < 50000)
                    {
                        ulong ost = 50000 - money;
                        rust.SendChatMessage(netuser, "[COLOR#00BFFF]Вам не хватает: [COLOR#d9ae04]" + ost + "[COLOR#00BFFF]. У вас: [COLOR#17c90e]" + money);
                        Puts(netuser + "Попытался вызвать дроп себе. Не хватает: " + ost + ". Денег всего: " + money);
                        return;
                    } else {
                        RustExtended.Economy.BalanceSub(netuser.userID, 50000);
                        cachedPos = netuser.playerClient.lastKnownPosition;
                        if(cachedPos != default(Vector3))
                        {
                            SupplyDropZone.CallAirDropAt(cachedPos);
                            SendReply(netuser, string.Format(airdropPlayer, cachedPos.ToString(), netuser.playerClient.userName.ToString()));
                            Puts(netuser + " вызвал себе дроп. Денег осталось: " +money);
                        }
                    }
                }
    
                // Вызвать игроку
                if (money < 75000)
                {
                    ulong ost = 75000 - money;
                    rust.SendChatMessage(netuser, "[COLOR#00BFFF]Вам не хватает: [COLOR#d9ae04]" + ost + "[COLOR#00BFFF]. У вас: [COLOR#17c90e]" + money);
                    Puts(netuser + "Попытался вызвать дроп себе. Не хватает: " + ost + ". Денег всего: " + money);
                    return;
                } else {
                    RustExtended.Economy.BalanceSub(netuser.userID, 75000);
                    NetUser targetuser = rust.FindPlayer(args[0]);
                    if (targetuser != null)
                    {
                        cachedPos = targetuser.playerClient.lastKnownPosition;
                        if(cachedPos != default(Vector3))
                        {
                            SupplyDropZone.CallAirDropAt(cachedPos);
                            SendReply(netuser, string.Format(airdropPlayer, cachedPos.ToString(), targetuser.playerClient.userName.ToString()));
                            Puts(netuser + " вызвал дроп для " + targetuser + ". Денег осталось: " +money);
                        }
                        return;
                    } else {
                        SendReply(netuser, wrongarguments);
                    }
                }
            }
        }

Я в C# вообще дуб, бдубом. Этот плагин был сделан мной на основе другого плагина, я не использую visual studio, а отлаживаю плагины прямо на сервере, благо консоль выдаст ошибку если что то не так. Пытаюсь встроить таймер в эту систему, таким образом, что бы после каждого вызова cmdChatBan при чем для каждого его аргумента отдельно запускался таймер и шла проверка на его истечение, если истек то команду можно использовать повторно, если нет выводить об этом сообщение в саму игру таким образом rust.SendChatMessage(netuser, "Самолет заправляется, ждите еще: "+TimeSpan.FromMinutes(AirCallTime));. Из другого плагина скопировал переменную const float AirCallTime = 300f; в которой указано время в секундах, так же из того же плагина я скопировал это Dictionary<ulong, DateTime> players = new Dictionary<ulong, DateTime>(); так как считаю, что без него мне не обойтись. Так как блокировать команду нужно всем игрокам, из того же вспомогательного плагина копирую еще код

foreach (var playerClient in PlayerClient.All)
{
    if (!players.ContainsKey(playerClient.userID))
    {
        players.Add(playerClient.userID, DateTime.Now);
        rust.SendChatMessage(NetUser.FindByUserID(playerClient.userID), ChatName, $"[COLOR#FF0000]Вам заблокированы команды /{String.Join(", /", BlockedCommands.Concat(BlockedPluginCommands).ToArray())} на {AirCallTime} секунд");
    }
    else
    {
        if (DateTime.Now - players[playerClient.userID] >= TimeSpan.FromSeconds(AirCallTime))
        {
            rust.SendChatMessage(NetUser.FindByUserID(playerClient.userID), ChatName, $"[COLOR#FF0000]Вам заблокированы команды /{String.Join(", /", BlockedCommands.Concat(BlockedPluginCommands).ToArray())} на {AirCallTime} секунд");
        }
        players[playerClient.userID] = DateTime.Now;
    }
}

Естественно все текста перепишу, просто не могу понять, как совместить сие чудо? Или может есть вариант попроще?

(Вкратце, возможно более понятней: плагин написанный на C# который я целиком выложил выше, был мною переделан из другого который лежал на github, смысл его работы в том, что любой игрок на сервере может прописать команду /air при чем с аргументами '/air map', '/air me', '/air <ник другого игрока>', все было бы хорошо, игроки беспощадно пользуются данными командами тем самым нагружая сервер и что бы их ограничить, пытаюсь втулить сюда ограничение на вызов по времени, если кто то из игроков вызвал любую из команд, команда сразу блокируется для всех игроков на время указанное в константе)

З.Ы. Сильно не пинайте за всю эту ахинею, делаю все что могу, но получается пока что лишь самая малость. upd:рабочий плагин с таймером

using System.Collections.Generic;
using System;
using System.Reflection;
using UnityEngine;
using Oxide.Core;
using System.Collections;
using RustExtended;

namespace Oxide.Plugins
{
    [Info("MathEvent", "XBOCT", "1.3.0")]
    [Description("Математический ивент в чате с наградой")]
    class MathEvent : RustLegacyPlugin
    {
        float repeatTime = 300f;//Ивент каждые * секнуда (f после числа обязательно)
        float timeForAnswer = 15f;//Время для ответа (f после числа обязательно)
        int minReward = 500;//Минимальная награда
        int maxReward = 2000;//Максимальная награда
        string ChatName = "MegaPUB";//Префикс в чате

        int TrueAnswer;
        int Reward;
        bool eventRunning = false;

        void Loaded()
        {
            timer.Repeat(repeatTime, 0, () =>
            {
                timer.Once(timeForAnswer, () =>
                {
                    if (eventRunning)
                    {
                        Broadcast.MessageAll($"[COLOR#FF0000]Время для ответа вышло. Следующий ивент через {repeatTime - timeForAnswer} секунд!", ChatName);
                        eventRunning = false;
                    }
                });
                StartEvent();
            });
        }

        void StartEvent()
        {
            eventRunning = true;
            int number_1 = Core.Random.Range(1, 500);
            int number_2 = Core.Random.Range(1, 500);

            Reward = Core.Random.Range(minReward, maxReward);
            TrueAnswer = number_1 + number_2;
            
            string text = $"[COLOR#FF0000][COLOR#FFFF00]Сколько будет {number_1} + {number_2}? Победитель получит [COLOR#00FF00]{Reward} Р[COLOR#FFFF00] за правильный ответ!";
            Broadcast.MessageAll(text, ChatName);
        }

        bool OnPlayerChat(NetUser netUser, string message)
        {
            if (eventRunning && message.Length < 5)
            {
                int answer;
                
                try
                {
                    answer = Int32.Parse(message);
                }
                catch
                {
                    return false;
                }

                if (answer == TrueAnswer)
                {
                    eventRunning = false;
                    string text = $"[COLOR#FF0000]{netUser.user.displayname_} [COLOR#FFFF00]дал правильный ответ [COLOR#FF0000]{TrueAnswer} [COLOR#FFFF00]и получает [COLOR#00FF00]{Reward} Р[COLOR#FFFF00] на свой счет!";
                    Broadcast.MessageAll(text, ChatName);
                    ulong reward = (ulong) Reward;
                    Economy.Get(netUser.userID).Balance += reward;
                    return true;
                }
            }
            return false;
        }
    }
}

Ответы (1 шт):

Автор решения: aepot

Вот вариант с таймером и очередью.

private readonly Queue<Vector3> dropQueue = new Queue<Vector3>();

void Loaded()
{
    timer.Repeat(1f, 0, () =>
    {
        if (dropQueue.Count > 0)
        {
            Vector3 pos = dropQueue.Dequeue();
            if (pos != default(Vector3))
                SupplyDropZone.CallAirDropAt(pos);
            else
                SupplyDropZone.CallAirDrop();
        }
    });
}

В остальном коде замените:

  • SupplyDropZone.CallAirDropAt(cachedPos) на dropQueue.Enqueue(cachedPos)
  • SupplyDropZone.CallAirDrop() на dropQueue.Enqueue(default(Vector3))
→ Ссылка