using ScriptedEvents.Enums;
using ScriptedEvents.Interfaces;
namespace ScriptedEvents
{
using System;
using System.Collections.Generic;
using System.Linq;
using CommandSystem;
using Discord;
using Exiled.API.Features;
using Exiled.API.Features.Pools;
using ScriptedEvents.API.Features;
using ScriptedEvents.API.Modules;
using ScriptedEvents.Structures;
using ScriptedEvents.Variables;
using ScriptedEvents.Variables.Interfaces;
///
/// Represents a script.
///
public class Script : IDisposable
{
///
/// Initializes a new instance of the class.
/// Creates a new script and assigns its to a new .
///
public Script()
{
Labels = DictionaryPool.Pool.Get();
FunctionLabels = DictionaryPool.Pool.Get();
Flags = ListPool.Pool.Get();
LocalLiteralVariables = DictionaryPool.Pool.Get();
LocalPlayerVariables = DictionaryPool.Pool.Get();
UniqueId = Guid.NewGuid();
Logger.Debug($"Created new script object | ID: {UniqueId}");
}
///
/// Finalizes an instance of the class.
///
~Script()
{
Dispose();
}
///
/// Gets the unique ID referring to this Script instance.
///
public Guid UniqueId { get; }
///
/// Gets or sets the name of the script.
///
public string ScriptName { get; set; } = string.Empty;
///
/// Gets or sets the permission required to read the script.
///
public string ReadPermission { get; set; } = "script.read";
///
/// Gets or sets the permission required to execute the script.
///
public string ExecutePermission { get; set; } = "script.execute";
///
/// Gets or sets the path to the script, on the host's computer.
///
public string FilePath { get; set; }
///
/// Gets or sets the last time the script was read.
///
public DateTime LastRead { get; set; }
///
/// Gets or sets the last time the script was edited.
///
public DateTime LastEdited { get; set; }
///
/// Gets or sets the raw text of the script.
///
public string RawText { get; set; } = string.Empty;
///
/// Gets or sets a list of of each action.
///
public IAction[] Actions { get; set; }
///
/// Gets or sets a list of Labels.
///
public Dictionary Labels { get; set; }
///
/// Gets or sets a list of function labels.
///
public Dictionary FunctionLabels { get; set; }
///
/// Gets or sets the line the script is currently on.
///
public int CurrentLine { get; set; } = 0;
///
/// Gets a value indicating whether or not the script is currently executing.
///
public bool IsRunning { get; internal set; } = false;
///
/// Gets or sets a value indicating the time that the script began running.
///
public DateTime RunDate { get; set; }
///
/// Gets the amount of time the script has been running.
///
public TimeSpan RunDuration => DateTime.Now - RunDate;
///
/// Gets a list of flags on the script.
///
public List Flags { get; }
///
/// Gets a value indicating whether or not the script is enabled.
///
public bool IsDisabled => HasFlag("DISABLE");
///
/// Gets a value indicating whether or not the script is running in debug mode.
///
public bool IsDebug => HasFlag("DEBUG") || MainPlugin.Configs.Debug;
///
/// Gets a value indicating whether or not the script is marked as an admin-event (CedMod compatibility).
///
public bool AdminEvent => HasFlag("ADMINEVENT");
///
/// Gets a value indicating whether or not warnings are suppressed.
///
public bool SuppressWarnings => HasFlag("SUPPRESSWARNINGS");
///
/// Gets the context that the script was executed in.
///
public ExecuteContext Context { get; internal set; }
///
/// Gets the sender of the user who executed the script.
///
public ICommandSender? Sender { get; internal set; }
///
/// Gets or sets all line positions from where a JUMP action was executed.
///
public List FunctionLabelHistory { get; set; } = new();
///
/// Gets the original script which ran this script using the CALL action.
///
public Script? CallerScript { get; internal set; }
///
/// Gets a of variables that are unique to this script.
///
public Dictionary LocalLiteralVariables { get; }
///
/// Gets a of player variables that are unique to this script.
///
public Dictionary LocalPlayerVariables { get; }
///
/// Gets a of coroutines run by this script.
///
public List Coroutines { get; } = new();
///
/// Gets or sets the info about an ongoing player loop.
///
public PlayerLoopInfo? PlayerLoopInfo { get; set; } = null;
///
/// Gets the original action arguments from when the script was read for the first time.
///
public Dictionary OriginalActionArgs { get; } = new();
///
/// Gets the attached arguments for specified action.
///
public Dictionary>[]> AttachedArguments { get; } = new();
///
/// Gets the names under which to create variables as result of an successful action.
///
public Dictionary ResultVariableNames { get; } = new();
///
/// Gets the lambda method that checks if action is allowed to run.
///
public Dictionary> SingleLineIfStatements { get; } = new();
///
public void Dispose()
{
Logger.Debug($"Disposing script object | ID: {UniqueId}");
Sender = null;
Actions = Array.Empty();
RawText = string.Empty;
FilePath = string.Empty;
DictionaryPool.Pool.Return(Labels);
ListPool.Pool.Return(Flags);
DictionaryPool.Pool.Return(LocalLiteralVariables);
DictionaryPool.Pool.Return(LocalPlayerVariables);
GC.SuppressFinalize(this);
}
///
/// Moves the to the specified line.
///
/// The line to move to.
public void Jump(int line)
{
CurrentLine = line - 1;
}
///
/// Moves the to the specified location.
///
/// Keyword (START, label, or number).
/// Whether or not the jump was successful.
public bool JumpToLabel(string keyword)
{
switch (keyword.ToUpper())
{
case "START":
CurrentLine = -1;
return true;
}
if (Labels.TryGetValue(keyword, out int line))
{
CurrentLine = line;
return true;
}
return false;
}
public bool JumpToFunctionLabel(string keyword)
{
if (FunctionLabels.TryGetValue(keyword, out int line))
{
CurrentLine = line;
return true;
}
return false;
}
///
/// Moves to the next line.
///
public void NextLine() => CurrentLine++;
///
/// Logs a debug message to the console.
///
/// The input to Logger.
public void DebugLog(string input)
{
if (IsDebug)
Log.Send($"[{MainPlugin.Singleton.Name}] {input}", LogLevel.Debug, ConsoleColor.Green);
}
///
/// Adds a variable.
///
/// Name of the variable.
/// The value of the variable.
/// Whether variable name has already been verified to be valid.
public void AddLiteralVariable(string name, string value, bool isVariableNameVerified)
{
if (!VariableSystem.IsValidVariableSyntax(name, out name, out var info) && !isVariableNameVerified)
{
throw new ArgumentException(info!.ToTrace().Format());
}
LocalLiteralVariables[name] = new(name, string.Empty, value);
}
///
/// Adds a player variable.
///
/// Name of the variable.
/// The of Players for this variable.
/// Whether variable name has already been verified to be valid.
public void AddPlayerVariable(string name, IEnumerable value, bool isVariableNameVerified)
{
if (!VariableSystem.IsValidVariableSyntax(name, out var processedName, out var info) && !isVariableNameVerified)
{
throw new ArgumentException(info!.ToTrace().Format());
}
LocalPlayerVariables[processedName] = new(processedName, string.Empty, value);
}
public bool RemoveVariable(T var)
where T : IVariable
{
return var switch
{
IPlayerVariable playerVariable => LocalPlayerVariables.Remove(playerVariable.Name),
ILiteralVariable literalVariable => LocalLiteralVariables.Remove(literalVariable.Name),
_ => throw new ArgumentException($"Variable '{var}' is not a valid variable type.")
};
}
public bool IsVariableLocal(T var)
where T : IVariable
{
return var switch
{
IPlayerVariable playerVariable => LocalPlayerVariables.ContainsKey(playerVariable.Name),
ILiteralVariable literalVariable => LocalLiteralVariables.ContainsKey(literalVariable.Name),
_ => throw new ArgumentException($"Variable '{var}' is not a valid variable type.")
};
}
public bool HasFlag(string key, out Flag flag)
{
flag = Flags.FirstOrDefault(fl => fl.Key == key);
return flag.Key is not null;
}
public bool HasFlag(string key)
=> HasFlag(key, out _);
public void AddFlag(string key, IEnumerable? arguments = null)
=> Flags.Add(new(key, arguments ?? Array.Empty()));
}
}