forked from PGR/ascnet
command yay!
This commit is contained in:
parent
21856d353b
commit
6c5aca3c3a
|
@ -52,6 +52,9 @@ namespace AscNet.Common.Database
|
|||
|
||||
public void AddStage(StageDatum stageData)
|
||||
{
|
||||
if (Stages.ContainsKey(stageData.StageId))
|
||||
Stages[stageData.StageId] = stageData;
|
||||
else
|
||||
Stages.Add(stageData.StageId, stageData);
|
||||
}
|
||||
|
||||
|
|
|
@ -2452,26 +2452,7 @@ namespace AscNet.Common.MsgPack
|
|||
[global::MessagePack.MessagePackObject(true)]
|
||||
public class NotifyStageData
|
||||
{
|
||||
[global::MessagePack.MessagePackObject(true)]
|
||||
public class NotifyStageDataStage
|
||||
{
|
||||
public UInt32 StageId { get; set; }
|
||||
public Int32 StarsMark { get; set; }
|
||||
public Boolean Passed { get; set; }
|
||||
public Int32 PassTimesToday { get; set; }
|
||||
public Int32 PassTimesTotal { get; set; }
|
||||
public Int32 BuyCount { get; set; }
|
||||
public Int32 Score { get; set; }
|
||||
public UInt32 LastPassTime { get; set; }
|
||||
public UInt32 RefreshTime { get; set; }
|
||||
public UInt32 CreateTime { get; set; }
|
||||
public Int32 BestRecordTime { get; set; }
|
||||
public Int32 LastRecordTime { get; set; }
|
||||
public List<UInt32> BestCardIds { get; set; } = new();
|
||||
public List<UInt32> LastCardIds { get; set; } = new();
|
||||
}
|
||||
|
||||
public List<NotifyStageDataStage> StageList { get; set; } = new();
|
||||
public List<StageDatum> StageList { get; set; } = new();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using AscNet.Logging;
|
||||
|
||||
namespace AscNet.GameServer.Commands
|
||||
{
|
||||
public abstract class Command
|
||||
{
|
||||
protected Session session;
|
||||
protected string[] args;
|
||||
|
||||
public abstract string Help { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Make sure to handle me well...
|
||||
/// </summary>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public Command(Session session, string[] args, bool validate = true)
|
||||
{
|
||||
this.session = session;
|
||||
this.args = args;
|
||||
|
||||
string? ret = Validate();
|
||||
if (ret is not null && validate)
|
||||
throw new ArgumentException(ret);
|
||||
}
|
||||
|
||||
public string? Validate()
|
||||
{
|
||||
List<PropertyInfo> argsProperties = GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetCustomAttribute(typeof(ArgumentAttribute)) is not null).ToList();
|
||||
if (argsProperties.Count != args.Length)
|
||||
return "Invalid args length!";
|
||||
|
||||
foreach (var argProp in argsProperties)
|
||||
{
|
||||
ArgumentAttribute attr = (ArgumentAttribute)argProp.GetCustomAttribute(typeof(ArgumentAttribute))!;
|
||||
if (!attr.Pattern.IsMatch(args[attr.Position]))
|
||||
return $"Argument {argProp.Name} is invalid!";
|
||||
|
||||
argProp.SetValue(this, args[attr.Position]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void Execute();
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class ArgumentAttribute : Attribute
|
||||
{
|
||||
public int Position { get; }
|
||||
public Regex Pattern { get; }
|
||||
public string? Description { get; }
|
||||
|
||||
public ArgumentAttribute(int position, string pattern, string? description = null)
|
||||
{
|
||||
Position = position;
|
||||
Pattern = new(pattern);
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class CommandNameAttribute : Attribute
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public CommandNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
[CommandName("help")]
|
||||
internal class HelpCommand : Command
|
||||
{
|
||||
public HelpCommand(Session session, string[] args, bool validate = true) : base(session, args, validate) { }
|
||||
|
||||
public override string Help => "Show this help.";
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
string helpText = string.Empty;
|
||||
|
||||
foreach (var command in CommandFactory.commands.Keys)
|
||||
{
|
||||
Command? cmd = CommandFactory.CreateCommand(command, session, args, false);
|
||||
if (cmd is not null)
|
||||
helpText += $"{command}\n\t└─{cmd.Help}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandFactory
|
||||
{
|
||||
public static readonly Dictionary<string, Type> commands = new();
|
||||
|
||||
internal static readonly Logger log = new(typeof(CommandFactory), LogLevel.DEBUG, LogLevel.DEBUG);
|
||||
|
||||
public static void LoadCommands()
|
||||
{
|
||||
log.LogLevelColor[LogLevel.INFO] = ConsoleColor.White;
|
||||
log.Info("Loading commands...");
|
||||
|
||||
IEnumerable<Type> classes = from t in Assembly.GetExecutingAssembly().GetTypes()
|
||||
where t.IsClass && t.GetCustomAttribute<CommandNameAttribute>() is not null
|
||||
select t;
|
||||
|
||||
foreach (var command in classes)
|
||||
{
|
||||
CommandNameAttribute nameAttr = command.GetCustomAttribute<CommandNameAttribute>()!;
|
||||
commands.Add(nameAttr.Name, command);
|
||||
#if DEBUG
|
||||
log.Info($"Loaded {nameAttr.Name} command");
|
||||
#endif
|
||||
}
|
||||
|
||||
log.Info("Finished loading commands");
|
||||
}
|
||||
|
||||
public static Command? CreateCommand(string name, Session session, string[] args, bool validate = true)
|
||||
{
|
||||
Type? command = commands.GetValueOrDefault(name);
|
||||
if (command is null)
|
||||
return null;
|
||||
|
||||
return (Command)Activator.CreateInstance(command, new object[] { session, args, validate })!;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
using AscNet.Common.MsgPack;
|
||||
using AscNet.Table.share.fuben;
|
||||
|
||||
namespace AscNet.GameServer.Commands
|
||||
{
|
||||
[CommandName("stage")]
|
||||
internal class StageCommand : Command
|
||||
{
|
||||
public StageCommand(Session session, string[] args, bool validate = true) : base(session, args, validate) { }
|
||||
|
||||
[Argument(0, @"^[0-9]+$|^all$", "The target stage, value is stage id or 'all'")]
|
||||
string TargetStage { get; set; } = string.Empty;
|
||||
|
||||
public override string Help => "Modify the stage completion status of the account.";
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
if (TargetStage == "all")
|
||||
{
|
||||
session.stage.Stages.Clear();
|
||||
foreach (var stageData in StageTableReader.Instance.All)
|
||||
{
|
||||
session.stage.Stages.Add(stageData.StageId, new()
|
||||
{
|
||||
StageId = stageData.StageId,
|
||||
StarsMark = 7,
|
||||
Passed = true,
|
||||
PassTimesToday = 0,
|
||||
PassTimesTotal = 1,
|
||||
BuyCount = 0,
|
||||
Score = 0,
|
||||
LastPassTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
|
||||
RefreshTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
|
||||
CreateTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
|
||||
BestRecordTime = 0,
|
||||
LastRecordTime = 0,
|
||||
BestCardIds = new List<long> { 1021001 },
|
||||
LastCardIds = new List<long> { 1021001 }
|
||||
});
|
||||
}
|
||||
|
||||
session.SendPush(new NotifyStageData() { StageList = session.stage.Stages.Select(x => x.Value).ToList() });
|
||||
}
|
||||
else
|
||||
{
|
||||
StageTable? stageData = StageTableReader.Instance.FromStageId(int.Parse(TargetStage));
|
||||
if (stageData is not null && !session.stage.Stages.ContainsKey(stageData.StageId))
|
||||
{
|
||||
StageDatum stage = new()
|
||||
{
|
||||
StageId = stageData.StageId,
|
||||
StarsMark = 7,
|
||||
Passed = true,
|
||||
PassTimesToday = 0,
|
||||
PassTimesTotal = 1,
|
||||
BuyCount = 0,
|
||||
Score = 0,
|
||||
LastPassTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
|
||||
RefreshTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
|
||||
CreateTime = DateTimeOffset.Now.ToUnixTimeSeconds(),
|
||||
BestRecordTime = 0,
|
||||
LastRecordTime = 0,
|
||||
BestCardIds = new List<long> { 1021001 },
|
||||
LastCardIds = new List<long> { 1021001 }
|
||||
};
|
||||
session.stage.Stages.Add(stageData.StageId, stage);
|
||||
|
||||
session.SendPush(new NotifyStageData() { StageList = new() { stage } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using AscNet.Common.Database;
|
||||
using AscNet.Common.MsgPack;
|
||||
using AscNet.Table.share.fuben;
|
||||
using AscNet.Table.share.guide;
|
||||
using MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
|
@ -103,7 +104,7 @@ namespace AscNet.GameServer.Handlers
|
|||
BaseEquipLoginData = new(),
|
||||
FubenData = new()
|
||||
{
|
||||
StageData = session.stage.Stages,
|
||||
StageData = session.stage.Stages.ToDictionary(x => x.Key, x => x.Value),
|
||||
FubenBaseData = new()
|
||||
},
|
||||
FubenMainLineData = new(),
|
||||
|
@ -116,7 +117,6 @@ namespace AscNet.GameServer.Handlers
|
|||
notifyLogin.FashionList.AddRange(session.character.Fashions);
|
||||
|
||||
#if DEBUG
|
||||
// Per account settings flag(?)
|
||||
notifyLogin.PlayerData.GuideData = GuideGroupTableReader.Instance.All.Select(x => (long)x.Id).ToList();
|
||||
#endif
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -120,7 +120,7 @@ namespace AscNet.GameServer
|
|||
#endif
|
||||
}
|
||||
|
||||
log.Info("Finished Loading Packet Handlers");
|
||||
log.Info("Finished loading packet handlers");
|
||||
}
|
||||
|
||||
public static RequestPacketHandlerDelegate? GetRequestPacketHandler(string name)
|
||||
|
|
|
@ -19,12 +19,16 @@ namespace AscNet.GameServer
|
|||
}
|
||||
}
|
||||
|
||||
public Server()
|
||||
static Server()
|
||||
{
|
||||
// TODO: add loglevel based on appsettings
|
||||
LogLevel logLevel = LogLevel.DEBUG;
|
||||
LogLevel fileLogLevel = LogLevel.DEBUG;
|
||||
log = new(typeof(Server), logLevel, fileLogLevel);
|
||||
}
|
||||
|
||||
public Server()
|
||||
{
|
||||
listener = new(IPAddress.Parse("0.0.0.0"), Common.Common.config.GameServer.Port);
|
||||
}
|
||||
|
||||
|
@ -54,5 +58,10 @@ namespace AscNet.GameServer
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Session? SessionFromUID(long uid)
|
||||
{
|
||||
return Sessions.FirstOrDefault(x => x.Value.player.PlayerData.Id == uid).Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AscNet.Common\AscNet.Common.csproj" />
|
||||
<ProjectReference Include="..\AscNet.GameServer\AscNet.GameServer.csproj" />
|
||||
<ProjectReference Include="..\AscNet.Logging\AscNet.Logging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
using AscNet.Common.Database;
|
||||
using AscNet.GameServer;
|
||||
using AscNet.GameServer.Commands;
|
||||
using AscNet.SDKServer.Models ;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AscNet.SDKServer.Controllers
|
||||
{
|
||||
public class CommandController : IRegisterable
|
||||
{
|
||||
public static void Register(WebApplication app)
|
||||
{
|
||||
app.MapGet("/api/AscNet/commands", (HttpContext ctx) =>
|
||||
{
|
||||
List<object> commands = new();
|
||||
|
||||
foreach (var command in CommandFactory.commands.Keys)
|
||||
{
|
||||
Command? cmd = CommandFactory.CreateCommand(command, null!, Array.Empty<string>(), false);
|
||||
if (cmd is not null)
|
||||
commands.Add(new
|
||||
{
|
||||
name = command,
|
||||
help = cmd.Help
|
||||
});
|
||||
}
|
||||
|
||||
return ctx.Response.WriteAsync(JsonConvert.SerializeObject(new { code = 0, msg = "OK", data = commands }));
|
||||
});
|
||||
|
||||
app.MapPost("/api/AscNet/command/{command}", ([FromHeader] string authorization, [FromBody] ExecuteCommandBody body, string command, HttpContext ctx) =>
|
||||
{
|
||||
Player? player = Player.FromToken(authorization);
|
||||
if (player is null)
|
||||
{
|
||||
ctx.Response.StatusCode = 401;
|
||||
return ctx.Response.WriteAsync(JsonConvert.SerializeObject(new { msg = "Invalid token!" }));
|
||||
}
|
||||
|
||||
Session? session = Server.Instance.SessionFromUID(player.PlayerData.Id);
|
||||
if (session is null)
|
||||
{
|
||||
ctx.Response.StatusCode = 400;
|
||||
return ctx.Response.WriteAsync(JsonConvert.SerializeObject(new { msg = "Player is offline!" }));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Command? cmd = CommandFactory.CreateCommand(command, session, body.Args);
|
||||
if (cmd is null)
|
||||
{
|
||||
ctx.Response.StatusCode = 404;
|
||||
return ctx.Response.WriteAsync(JsonConvert.SerializeObject(new { msg = "Command does not exists!" }));
|
||||
}
|
||||
|
||||
cmd.Execute();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ctx.Response.StatusCode = 400;
|
||||
return ctx.Response.WriteAsync(JsonConvert.SerializeObject(new { msg = "Execution error!", err = ex.InnerException?.Message ?? ex.Message }));
|
||||
}
|
||||
|
||||
return ctx.Response.WriteAsync(JsonConvert.SerializeObject(new { msg = "OK" }));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace AscNet.SDKServer.Models
|
||||
{
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
public class ExecuteCommandBody
|
||||
{
|
||||
[JsonProperty("args", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string[] Args { get; set; }
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue