Skip to content

Commit d02bb05

Browse files
add "cooldown" argument to CustomCommand flag
1 parent 531b9ef commit d02bb05

File tree

5 files changed

+112
-19
lines changed

5 files changed

+112
-19
lines changed

Code/FlagSystem/Flags/CustomCommandFlag.cs

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
using CommandSystem;
22
using JetBrains.Annotations;
33
using LabApi.Features.Permissions;
4+
using LabApi.Features.Wrappers;
45
using RemoteAdmin;
56
using SER.Code.Helpers.Exceptions;
67
using SER.Code.Helpers.Extensions;
78
using SER.Code.Helpers.ResultSystem;
89
using SER.Code.ScriptSystem;
910
using SER.Code.ScriptSystem.Structures;
1011
using SER.Code.TokenSystem;
12+
using SER.Code.TokenSystem.Tokens;
1113
using SER.Code.ValueSystem;
1214
using SER.Code.VariableSystem.Bases;
1315
using SER.Code.VariableSystem.Variables;
@@ -81,6 +83,12 @@ public class CustomCommandFlag : Flag
8183
"You can provide multiple ranks, and if the player has any of the listed ranks, they will be able to use the command.",
8284
AddNeededRank,
8385
false
86+
),
87+
new(
88+
"cooldown",
89+
"The time the player has to wait before being able to use the command again.",
90+
AddCooldown,
91+
false
8492
)
8593
];
8694

@@ -169,6 +177,8 @@ public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out s
169177
public ConsoleType ConsoleTypes { get; set; } = ConsoleType.Server;
170178
public string[] Usage { get; set; } = [];
171179
public string[] NeededRanks { get; set; } = [];
180+
public TimeSpan PlayerCooldown { get; set; } = TimeSpan.Zero;
181+
public Dictionary<Player, DateTime> NextEligableDateForPlayer { get; } = [];
172182
public string GetHelp(ArraySegment<string> arguments)
173183
{
174184
return $"Description: {Description}\n" +
@@ -180,18 +190,14 @@ public string GetHelp(ArraySegment<string> arguments)
180190

181191
public CustomCommand Command = null!;
182192

183-
public static Result RunAttachedScript(CustomCommand requestingCommand, ScriptExecutor sender, string[] args)
193+
public static Result RunAttachedScript(CustomCommand cmd, ScriptExecutor sender, string[] args)
184194
{
185-
if (requestingCommand.NeededRanks.Any() && sender is IPlayerExecutor { Player: { } player })
195+
if (sender is IPlayerExecutor { Player: { } player } && HandlePlayer(cmd, player) is { } plrErr)
186196
{
187-
if (player.UserGroup is not { } group || requestingCommand.NeededRanks.All(rank => group.Name != rank))
188-
{
189-
return "This command is reserved for players with a rank: " +
190-
$"{requestingCommand.NeededRanks.JoinStrings(", ")}";
191-
}
197+
return plrErr;
192198
}
193199

194-
if (!ScriptCommands.TryGetValue(requestingCommand, out var flag))
200+
if (!ScriptCommands.TryGetValue(cmd, out var flag))
195201
{
196202
return "The script that was supposed to handle this command was not found.";
197203
}
@@ -203,16 +209,16 @@ public static Result RunAttachedScript(CustomCommand requestingCommand, ScriptEx
203209
}
204210

205211
var slices = outSlices.ToArray();
206-
if (slices.Length < requestingCommand.Usage.Length)
212+
if (slices.Length < cmd.Usage.Length)
207213
{
208214
return "Not enough arguments. " +
209-
$"Expected {requestingCommand.Usage.Length} but got {slices.Length}.";
215+
$"Expected {cmd.Usage.Length} but got {slices.Length}.";
210216
}
211217

212-
if (slices.Length > requestingCommand.Usage.Length)
218+
if (slices.Length > cmd.Usage.Length)
213219
{
214220
return "Too many arguments. " +
215-
$"Expected {requestingCommand.Usage.Length} but got {slices.Length}.";
221+
$"Expected {cmd.Usage.Length} but got {slices.Length}.";
216222
}
217223

218224
if (Script.CreateByScriptName(flag.ScriptName, sender)
@@ -221,10 +227,10 @@ public static Result RunAttachedScript(CustomCommand requestingCommand, ScriptEx
221227
return error;
222228
}
223229

224-
for (var index = 0; index < requestingCommand.Usage.Length; index++)
230+
for (var index = 0; index < cmd.Usage.Length; index++)
225231
{
226232
var slice = slices[index];
227-
var argVariable = requestingCommand.Usage[index];
233+
var argVariable = cmd.Usage[index];
228234
var name = argVariable[0].ToString().ToLower() + argVariable[1..];
229235

230236
if (Tokenizer.GetTokenFromSlice(slice, null!, 0)
@@ -243,6 +249,30 @@ public static Result RunAttachedScript(CustomCommand requestingCommand, ScriptEx
243249
script.Run(RunContext.Command);
244250
return true;
245251
}
252+
253+
private static string? HandlePlayer(CustomCommand cmd, Player plr)
254+
{
255+
var hasRank = plr.UserGroup is not { } group || cmd.NeededRanks.All(rank => group.Name != rank);
256+
if (cmd.NeededRanks.Any() && hasRank)
257+
{
258+
return "This command is reserved for players with a rank: " +
259+
$"{cmd.NeededRanks.JoinStrings(", ")}";
260+
}
261+
262+
if (cmd.PlayerCooldown <= TimeSpan.Zero)
263+
{
264+
return null;
265+
}
266+
267+
if (cmd.NextEligableDateForPlayer.TryGetValue(plr, out var nextEligableDate) && nextEligableDate < DateTime.UtcNow)
268+
{
269+
return $"You are on cooldown! You can use this command in " +
270+
$"{(nextEligableDate - DateTime.UtcNow).Seconds} seconds.";
271+
}
272+
273+
cmd.NextEligableDateForPlayer[plr] = DateTime.UtcNow + cmd.PlayerCooldown;
274+
return null;
275+
}
246276

247277
private Result AddArguments(string[] args)
248278
{
@@ -288,4 +318,23 @@ private Result AddNeededRank(string[] args)
288318
Command.NeededRanks = args;
289319
return true;
290320
}
321+
322+
private Result AddCooldown(string[] args)
323+
{
324+
switch (args.Length)
325+
{
326+
case < 1: return "Cooldown requires a duration value.";
327+
case > 2: return $"Cooldown expects only a single duration value, got given {args.Length} instead.";
328+
}
329+
330+
var rawValue = args[0];
331+
if (Tokenizer.GetTokenFromString(rawValue, null, null).HasErrored(out _, out var token)
332+
|| token is not DurationToken durationToken)
333+
{
334+
return $"Value '{rawValue}' is not a valid duration.";
335+
}
336+
337+
Command.PlayerCooldown = durationToken.Value;
338+
return true;
339+
}
291340
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Reflection;
2+
3+
namespace SER.Code.Helpers.Attributes;
4+
5+
/// <summary>
6+
/// Used on methods meant to clean up data after a round is over.
7+
/// </summary>
8+
[AttributeUsage(AttributeTargets.Method)]
9+
public class ClearDataAttribute : Attribute
10+
{
11+
public static void RunAttachedMethods()
12+
{
13+
var methods = Assembly.GetExecutingAssembly()
14+
.GetTypes()
15+
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
16+
.Where(m => m.GetCustomAttribute<ClearDataAttribute>() != null);
17+
18+
foreach (var method in methods)
19+
{
20+
method.Invoke(null, null);
21+
}
22+
}
23+
}

Code/Helpers/ResultSystem/TryGet.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ public sealed class TryGet<TValue>(TValue? value, string? errorMsg)
1111
private bool WasSuccess => string.IsNullOrEmpty(errorMsg);
1212
public Result Result => new(WasSuccess, ErrorMsg ?? "");
1313

14+
[Pure]
15+
public bool HasErrored()
16+
{
17+
return !WasSuccess;
18+
}
19+
1420
[Pure]
1521
public bool HasErrored([NotNullWhen(true)] out string? error)
1622
{

Code/TokenSystem/Tokenizer.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public static TryGet<BaseToken[]> TokenizeLine(IEnumerable<Slice> slices, Script
158158
return tokens.Select(t => t.Value!).ToArray();
159159
}
160160

161-
public static TryGet<BaseToken> GetTokenFromSlice(Slice slice, Script scr, uint? lineNum)
161+
public static TryGet<BaseToken> GetTokenFromSlice(Slice slice, Script? scr, uint? lineNum)
162162
{
163163
var tokenCollection = slice is CollectionSlice
164164
? OrderedImportanceTokensFromCollectionSlices
@@ -167,7 +167,7 @@ public static TryGet<BaseToken> GetTokenFromSlice(Slice slice, Script scr, uint?
167167
foreach (var tokenType in tokenCollection)
168168
{
169169
var token = tokenType.CreateInstance<BaseToken>();
170-
switch (token.TryInit(slice, scr, lineNum))
170+
switch (token.TryInit(slice, scr!, lineNum))
171171
{
172172
case BaseToken.Success: return token;
173173
case BaseToken.Ignore: continue;
@@ -177,7 +177,23 @@ public static TryGet<BaseToken> GetTokenFromSlice(Slice slice, Script scr, uint?
177177
}
178178

179179
var unspecified = new BaseToken();
180-
unspecified.TryInit(slice, scr, lineNum);
180+
unspecified.TryInit(slice, scr!, lineNum);
181181
return unspecified;
182182
}
183+
184+
public static TryGet<BaseToken> GetTokenFromString(string str, Script? scr, uint? lineNum)
185+
{
186+
if (SliceLine(str).HasErrored(out var err, out var slices))
187+
{
188+
return err;
189+
}
190+
191+
var bettaSlices = slices as Slice[] ?? slices.ToArray();
192+
if (bettaSlices.Length > 1)
193+
{
194+
return $"Value '{str}' contains multiple slices.";
195+
}
196+
197+
return GetTokenFromSlice(bettaSlices.First(), scr, lineNum);
198+
}
183199
}

Code/TokenSystem/Tokens/LiteralValueToken.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ protected set
1919
field = value;
2020
}
2121
} = null!;
22-
23-
public TryGet<LiteralValue> ExactValue => Value;
22+
2423
TryGet<Value> IValueToken.Value() => Value;
2524
public TypeOfValue PossibleValues => new TypeOfValue<T>();
2625
public bool IsConstant => true;

0 commit comments

Comments
 (0)