diff --git a/AscNet.GameServer/Commands/Command.cs b/AscNet.GameServer/Commands/Command.cs index 7dfdaa5..3033475 100644 --- a/AscNet.GameServer/Commands/Command.cs +++ b/AscNet.GameServer/Commands/Command.cs @@ -1,6 +1,8 @@ -using System.Reflection; +using System; +using System.Reflection; using System.Text.RegularExpressions; using AscNet.Logging; +using Newtonsoft.Json; namespace AscNet.GameServer.Commands { @@ -30,12 +32,17 @@ namespace AscNet.GameServer.Commands public string? Validate() { List argsProperties = GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetCustomAttribute(typeof(ArgumentAttribute)) is not null).ToList(); - if (argsProperties.Count != args.Length) + if (argsProperties.Where(x => (((ArgumentAttribute)x.GetCustomAttribute(typeof(ArgumentAttribute))!).Flags & ArgumentFlags.Optional) != ArgumentFlags.Optional).Count() > args.Length) return "Invalid args length!"; foreach (var argProp in argsProperties) { ArgumentAttribute attr = (ArgumentAttribute)argProp.GetCustomAttribute(typeof(ArgumentAttribute))!; + if (attr.Position + 1 > args.Length && (attr.Flags & ArgumentFlags.Optional) != ArgumentFlags.Optional) + return $"Argument {argProp.Name} is required!"; + else if (attr.Position + 1 > args.Length) + return null; + if (!attr.Pattern.IsMatch(args[attr.Position])) return $"Argument {argProp.Name} is invalid!"; @@ -53,15 +60,23 @@ namespace AscNet.GameServer.Commands public int Position { get; } public Regex Pattern { get; } public string? Description { get; } + public ArgumentFlags Flags { get; } - public ArgumentAttribute(int position, string pattern, string? description = null) + public ArgumentAttribute(int position, string pattern, string? description = null, ArgumentFlags flags = ArgumentFlags.None) { Position = position; Pattern = new(pattern); Description = description; + Flags = flags; } } + public enum ArgumentFlags + { + None = 0, + Optional = 1 + } + [AttributeUsage(AttributeTargets.Class)] public class CommandNameAttribute : Attribute { @@ -91,7 +106,7 @@ namespace AscNet.GameServer.Commands { List argsProperties = cmd.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetCustomAttribute(typeof(ArgumentAttribute)) is not null).ToList(); - helpText += $"{command} {string.Join(" ", argsProperties.Select(x => $"<{x.Name}>"))}\n{cmd.Help}\n"; + helpText += $"{command} {string.Join(" ", argsProperties.Select(x => (((ArgumentAttribute)x.GetCustomAttribute(typeof(ArgumentAttribute))!).Flags & ArgumentFlags.Optional) == ArgumentFlags.Optional ? $"[{x.Name}]" : $"<{x.Name}>"))}\n{cmd.Help}\n"; foreach (var argProp in argsProperties) { ArgumentAttribute attr = (ArgumentAttribute)argProp.GetCustomAttribute(typeof(ArgumentAttribute))!; diff --git a/AscNet.GameServer/Commands/ItemCommand.cs b/AscNet.GameServer/Commands/ItemCommand.cs new file mode 100644 index 0000000..a1ef14c --- /dev/null +++ b/AscNet.GameServer/Commands/ItemCommand.cs @@ -0,0 +1,66 @@ +using AscNet.Common.MsgPack; +using AscNet.Common.Util; +using AscNet.Table.V2.share.item; + +namespace AscNet.GameServer.Commands +{ + [CommandName("item")] + internal class ItemCommand : Command + { + public ItemCommand(Session session, string[] args, bool validate = true) : base(session, args, validate) { } + + public override string Help => "Command to interact with user's items"; + + [Argument(0, @"^add$|^clear$|^reset$", "The operation selected (add, clear, reset)")] + string Op { get; set; } = string.Empty; + + [Argument(1, @"^[0-9]+$|^all$", "The target item, value is item id or 'all'", ArgumentFlags.Optional)] + string Target { get; set; } = string.Empty; + + [Argument(2, @"^[0-9]+$|^max$", "The target item amount, value is number or 'max' (when using other than max amount will be limited to game's max limit)", ArgumentFlags.Optional)] + string Amount { get; set; } = string.Empty; + + public override void Execute() + { + switch (Op) + { + case "add": + if (Target == "all") + { + session.inventory.Items = TableReaderV2.Parse().Select(x => new Item() + { + Id = x.Id, + Count = x.MaxCount ?? 999_999_999, + RefreshTime = DateTimeOffset.Now.ToUnixTimeSeconds(), + CreateTime = DateTimeOffset.Now.ToUnixTimeSeconds() + }).ToList(); + + NotifyItemDataList notifyItemData = new() + { + ItemDataList = session.inventory.Items + }; + session.SendPush(notifyItemData); + } + else + { + if (string.IsNullOrEmpty(Target) || string.IsNullOrEmpty(Amount)) + { + throw new ArgumentException("Invalid Target / Amount!"); + } + + if (!TableReaderV2.Parse().Any(x => x.Id == Miscs.ParseIntOr(Target))) + { + throw new ArgumentException("Invalid Target item id!"); + } + + NotifyItemDataList notifyItemData = new(); + notifyItemData.ItemDataList.Add(session.inventory.Do(Miscs.ParseIntOr(Target), Miscs.ParseIntOr(Amount))); + session.SendPush(notifyItemData); + } + break; + default: + throw new InvalidOperationException("Invalid operation!"); + } + } + } +} diff --git a/AscNet.GameServer/Handlers/ChatModule.cs b/AscNet.GameServer/Handlers/ChatModule.cs index 3aad490..cc18178 100644 --- a/AscNet.GameServer/Handlers/ChatModule.cs +++ b/AscNet.GameServer/Handlers/ChatModule.cs @@ -174,9 +174,13 @@ namespace AscNet.GameServer.Handlers { notifyWorldChat.ChatMessages.Add(MakeLuciaMessage(ex.Message)); } - catch (Exception) + catch (Exception ex) { - notifyWorldChat.ChatMessages.Add(MakeLuciaMessage($"Command {cmdStrings.First().Split('/').Last()} failed to execute!")); +#if DEBUG + notifyWorldChat.ChatMessages.Add(MakeLuciaMessage($"Command {cmdStrings.First().Split('/').Last()} failed to execute!, " + ex.ToString())); +#else + notifyWorldChat.ChatMessages.Add(MakeLuciaMessage($"Command {cmdStrings.First().Split('/').Last()} failed to execute!, " + ex.Message)); +#endif } }