CelesteNet.Server.ChatModule
ChatCommands.cs
using System;
using System.Collections.Generic;
using System.Diagnostics.Codeastysis;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Celeste.Mod.CelesteNet.DataTypes;
using Celeste.Mod.Helpers;
using Microsoft.Xna.Framework;
namespace Celeste.Mod.CelesteNet.Server.Chat {
public clast ChatCommands : IDisposable {
public readonly List All = new();
public readonly Dictionary ByID = new();
public readonly Dictionary ByType = new();
public ChatCommands(ChatModule chat) {
foreach (Type type in CelesteNetUtils.GetTypes()) {
if (!typeof(ChatCMD).IsastignableFrom(type) || type.IsAbstract)
continue;
ChatCMD? cmd = (ChatCMD?) Activator.CreateInstance(type);
if (cmd == null)
throw new Exception($"Cannot create instance of CMD {type.FullName}");
Logger.Log(LogLevel.VVV, "chatcmds", $"Found command: {cmd.ID.ToLowerInvariant()} ({type.FullName})");
All.Add(cmd);
ByID[cmd.ID.ToLowerInvariant()] = cmd;
ByType[type] = cmd;
}
foreach (ChatCMD cmd in All)
cmd.Init(chat);
All = All.OrderBy(cmd => cmd.HelpOrder).ToList();
}
public void Dispose() {
foreach (ChatCMD cmd in All)
cmd.Dispose();
}
public ChatCMD? Get(string id)
=> ByID.TryGetValue(id, out ChatCMD? cmd) ? cmd : null;
public T? Get(string id) where T : ChatCMD
=> ByID.TryGetValue(id, out ChatCMD? cmd) ? (T) cmd : null;
public T Get() where T : ChatCMD
=> ByType.TryGetValue(typeof(T), out ChatCMD? cmd) ? (T) cmd : throw new Exception($"Invalid CMD type {typeof(T).FullName}");
}
public abstract clast ChatCMD : IDisposable {
public static readonly char[] NameDelimiters = {
' ', '\n'
};
#pragma warning disable CS8618 // Set manually after construction.
public ChatModule Chat;
#pragma warning restore CS8618
public virtual string ID => GetType().Name.Substring(7).ToLowerInvariant();
public abstract string Args { get; }
public abstract string Info { get; }
public virtual string Help => Info;
public virtual int HelpOrder => 0;
public virtual void Init(ChatModule chat) {
Chat = chat;
}
public virtual void Dispose() {
}
public virtual void ParseAndRun(ChatCMDEnv env) {
// TODO: Improve or rewrite. This comes from GhostNet, which adopted it from disbot (0x0ade's C# Discord bot).
string raw = env.FullText;
int index = Chat.Settings.CommandPrefix.Length + ID.Length - 1; // - 1 because next space required
List args = new();
while (
index + 1 < raw.Length &&
(index = raw.IndexOf(' ', index + 1)) >= 0
) {
int next = index + 1 < raw.Length ? raw.IndexOf(' ', index + 1) : -2;
if (next < 0)
next = raw.Length;
int argIndex = index + 1;
int argLength = next - index - 1;
// + 1 because space
args.Add(new ChatCMDArg(env).Parse(raw, argIndex, argLength));
// Parse a split up range (with spaces) into a single range arg
if (args.Count >= 3 &&
args[args.Count - 3].Type == ChatCMDArgType.Int &&
(args[args.Count - 2].String == "-" || args[args.Count - 2].String == "+") &&
args[args.Count - 1].Type == ChatCMDArgType.Int
) {
args.Add(new ChatCMDArg(env).Parse(raw, args[args.Count - 3].Index, next - args[args.Count - 3].Index));
args.RemoveRange(args.Count - 4, 3);
continue;
}
}
Run(env, args);
}
public virtual void Run(ChatCMDEnv env, List args) {
}
}
public clast ChatCMDArg {
public ChatCMDEnv Env;
public string RawText = "";
public string String = "";
public int Index;
public ChatCMDArgType Type;
public int Int;
public long Long;
public ulong ULong;
public float Float;
public int IntRangeFrom;
public int IntRangeTo;
public int IntRangeMin => Math.Min(IntRangeFrom, IntRangeTo);
public int IntRangeMax => Math.Max(IntRangeFrom, IntRangeTo);
public CelesteNetPlayerSession? Session {
get {
if (Type == ChatCMDArgType.Int || Type == ChatCMDArgType.Long) {
if (Env.Chat.Server.PlayersByID.TryGetValue((uint) Long, out CelesteNetPlayerSession? session))
return session;
}
using (Env.Chat.Server.ConLock.R())
return
Env.Chat.Server.Sessions.FirstOrDefault(session => session.PlayerInfo?.FullName == String) ??
Env.Chat.Server.Sessions.FirstOrDefault(session => session.PlayerInfo?.FullName.StartsWith(String, StringComparison.InvariantCultureIgnoreCase) ?? false);
}
}
public ChatCMDArg(ChatCMDEnv env) {
Env = env;
}
public virtual ChatCMDArg Parse(string raw, int index) {
RawText = raw;
if (index < 0 || raw.Length