command yay!
This commit is contained in:
parent
21856d353b
commit
6c5aca3c3a
|
@ -52,7 +52,10 @@ namespace AscNet.Common.Database
|
||||||
|
|
||||||
public void AddStage(StageDatum stageData)
|
public void AddStage(StageDatum stageData)
|
||||||
{
|
{
|
||||||
Stages.Add(stageData.StageId, stageData);
|
if (Stages.ContainsKey(stageData.StageId))
|
||||||
|
Stages[stageData.StageId] = stageData;
|
||||||
|
else
|
||||||
|
Stages.Add(stageData.StageId, stageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
|
|
|
@ -2452,26 +2452,7 @@ namespace AscNet.Common.MsgPack
|
||||||
[global::MessagePack.MessagePackObject(true)]
|
[global::MessagePack.MessagePackObject(true)]
|
||||||
public class NotifyStageData
|
public class NotifyStageData
|
||||||
{
|
{
|
||||||
[global::MessagePack.MessagePackObject(true)]
|
public List<StageDatum> StageList { get; set; } = new();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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.Database;
|
||||||
using AscNet.Common.MsgPack;
|
using AscNet.Common.MsgPack;
|
||||||
|
using AscNet.Table.share.fuben;
|
||||||
using AscNet.Table.share.guide;
|
using AscNet.Table.share.guide;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -103,7 +104,7 @@ namespace AscNet.GameServer.Handlers
|
||||||
BaseEquipLoginData = new(),
|
BaseEquipLoginData = new(),
|
||||||
FubenData = new()
|
FubenData = new()
|
||||||
{
|
{
|
||||||
StageData = session.stage.Stages,
|
StageData = session.stage.Stages.ToDictionary(x => x.Key, x => x.Value),
|
||||||
FubenBaseData = new()
|
FubenBaseData = new()
|
||||||
},
|
},
|
||||||
FubenMainLineData = new(),
|
FubenMainLineData = new(),
|
||||||
|
@ -116,7 +117,6 @@ namespace AscNet.GameServer.Handlers
|
||||||
notifyLogin.FashionList.AddRange(session.character.Fashions);
|
notifyLogin.FashionList.AddRange(session.character.Fashions);
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Per account settings flag(?)
|
|
||||||
notifyLogin.PlayerData.GuideData = GuideGroupTableReader.Instance.All.Select(x => (long)x.Id).ToList();
|
notifyLogin.PlayerData.GuideData = GuideGroupTableReader.Instance.All.Select(x => (long)x.Id).ToList();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -120,7 +120,7 @@ namespace AscNet.GameServer
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Finished Loading Packet Handlers");
|
log.Info("Finished loading packet handlers");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RequestPacketHandlerDelegate? GetRequestPacketHandler(string name)
|
public static RequestPacketHandlerDelegate? GetRequestPacketHandler(string name)
|
||||||
|
|
|
@ -19,12 +19,16 @@ namespace AscNet.GameServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Server()
|
static Server()
|
||||||
{
|
{
|
||||||
// TODO: add loglevel based on appsettings
|
// TODO: add loglevel based on appsettings
|
||||||
LogLevel logLevel = LogLevel.DEBUG;
|
LogLevel logLevel = LogLevel.DEBUG;
|
||||||
LogLevel fileLogLevel = LogLevel.DEBUG;
|
LogLevel fileLogLevel = LogLevel.DEBUG;
|
||||||
log = new(typeof(Server), logLevel, fileLogLevel);
|
log = new(typeof(Server), logLevel, fileLogLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Server()
|
||||||
|
{
|
||||||
listener = new(IPAddress.Parse("0.0.0.0"), Common.Common.config.GameServer.Port);
|
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>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\AscNet.Common\AscNet.Common.csproj" />
|
<ProjectReference Include="..\AscNet.Common\AscNet.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\AscNet.GameServer\AscNet.GameServer.csproj" />
|
||||||
<ProjectReference Include="..\AscNet.Logging\AscNet.Logging.csproj" />
|
<ProjectReference Include="..\AscNet.Logging\AscNet.Logging.csproj" />
|
||||||
</ItemGroup>
|
</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