diff --git a/AscNet.Common/AscNet.Common.csproj b/AscNet.Common/AscNet.Common.csproj new file mode 100644 index 00000000..73ad7b0f --- /dev/null +++ b/AscNet.Common/AscNet.Common.csproj @@ -0,0 +1,15 @@ + + + + net7.0 + enable + enable + + + + + + + + + diff --git a/AscNet.Common/Common.cs b/AscNet.Common/Common.cs new file mode 100644 index 00000000..0adc3404 --- /dev/null +++ b/AscNet.Common/Common.cs @@ -0,0 +1,25 @@ +using MongoDB.Driver; +using Config.Net; + +namespace AscNet.Common +{ + public static class Common + { + public static readonly IConfig config; + public static readonly MongoClient mongoClient; + public static readonly IMongoDatabase db; + + static Common() + { + config = new ConfigurationBuilder().UseJsonFile("Configs/config.json").Build(); + mongoClient = new( + new MongoClientSettings + { + Server = new MongoServerAddress(config.Database.Host, config.Database.Port), + // Credential = MongoCredential.CreateCredential("admin", config.Database.Username, config.Database.Password) + } + ); + db = mongoClient.GetDatabase(config.Database.Name); + } + } +} diff --git a/AscNet.Common/Config.cs b/AscNet.Common/Config.cs new file mode 100644 index 00000000..34889831 --- /dev/null +++ b/AscNet.Common/Config.cs @@ -0,0 +1,50 @@ +using Config.Net; + +namespace AscNet.Common +{ + public interface IConfig + { + [Option(DefaultValue = VerboseLevel.Normal)] + VerboseLevel VerboseLevel { get; set; } + + [Option] + IGameServer GameServer { get; set; } + + [Option] + IDatabase Database { get; set; } + + [Option(DefaultValue = false)] + bool SaveClientLogs { get; set; } + + + interface IGameServer + { + [Option(DefaultValue = "127.0.0.1")] + string Host { get; set; } + + [Option(DefaultValue = (ushort)2335)] + ushort Port { get; set; } + } + + interface IDatabase + { + [Option(DefaultValue = "127.0.0.1")] + string Host { get; set; } + + [Option(DefaultValue = (ushort)27017)] + ushort Port { get; set; } + + [Option(DefaultValue = "sf")] + string Name { get; set; } + } + + } + + public enum VerboseLevel + { + Silent = 0, + Normal = 1, + Debug = 2, + SuperDebug = 3 + } +} diff --git a/AscNet.Common/Database/Account.cs b/AscNet.Common/Database/Account.cs new file mode 100644 index 00000000..34d704a6 --- /dev/null +++ b/AscNet.Common/Database/Account.cs @@ -0,0 +1,104 @@ +using MongoDB.Bson; +using MongoDB.Driver; +using MongoDB.Bson.Serialization.Attributes; + +namespace AscNet.Common.Database +{ +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public class Account + { + public static readonly IMongoCollection collection = Common.db.GetCollection("accounts"); + + public static Account? FromUID(long uid) + { + return collection.AsQueryable().FirstOrDefault(x => x.Uid == uid); + } + + public static Account? FromAccessToken(string token) + { + return collection.AsQueryable().FirstOrDefault(x => x.AccessToken == token); + } + + public static Account? FromPhone(string phone) + { + return collection.AsQueryable().FirstOrDefault(x => x.PhoneNum == phone); + } + + public static Account? FromPhone(string phone, string password) + { + return collection.AsQueryable().FirstOrDefault(x => x.PhoneNum == phone && x.Password == password); + } + + /// + public static Account Create(string phone, string password) + { + if (collection.AsQueryable().FirstOrDefault(x => x.PhoneNum == phone) is not null) + throw new ArgumentException("Phone is already registered!", "phone"); + + Account account = new() + { + Uid = (collection.AsQueryable().OrderByDescending(x => x.Uid).FirstOrDefault()?.Uid ?? 0) + 1, + PhoneNum = phone, + Email = "", + Password = password, + AccessToken = Guid.NewGuid().ToString(), + Age = 0, + IsActivation = false, + IsAdult = true, + IsGuest = false, + UnfreezeTime = 0, + IsReal = true + }; + + collection.InsertOne(account); + return account; + } + + [BsonId] + public ObjectId Id { get; set; } + + [BsonElement("uid")] + [BsonRequired] + public long Uid { get; set; } + + [BsonElement("phone_num")] + [BsonRequired] + public string PhoneNum { get; set; } + + [BsonElement("email")] + [BsonRequired] + public string Email { get; set; } + + [BsonElement("password")] + [BsonRequired] + public string Password { get; set; } + + [BsonElement("access_token")] + [BsonRequired] + public string AccessToken { get; set; } + + [BsonElement("age")] + [BsonRequired] + public int Age { get; set; } + + [BsonElement("is_activation")] + [BsonRequired] + public bool IsActivation { get; set; } + + [BsonElement("is_adult")] + [BsonRequired] + public bool IsAdult { get; set; } + + [BsonElement("is_guest")] + [BsonRequired] + public bool IsGuest { get; set; } + + [BsonElement("unfreeze_time")] + [BsonRequired] + public int UnfreezeTime { get; set; } + + [BsonElement("is_real")] + [BsonRequired] + public bool IsReal { get; set; } + } +} diff --git a/AscNet.Common/Util/Crypto.cs b/AscNet.Common/Util/Crypto.cs new file mode 100644 index 00000000..adb2b284 --- /dev/null +++ b/AscNet.Common/Util/Crypto.cs @@ -0,0 +1,15 @@ +namespace AscNet.Common.Util +{ + public static class Crypto + { + public static byte[] XORCrypt(byte[] data, byte[] key) + { + byte[] encryptedData = new byte[data.Length]; + + for (int i = 0; i < data.Length; i++) + encryptedData[i] = (byte)(data[i] ^ key[i % key.Length]); + + return encryptedData; + } + } +} diff --git a/AscNet.Common/Util/Logger.cs b/AscNet.Common/Util/Logger.cs new file mode 100644 index 00000000..5d47fd5e --- /dev/null +++ b/AscNet.Common/Util/Logger.cs @@ -0,0 +1,100 @@ +using System.Diagnostics; + +namespace AscNet.Common.Util +{ + public class Logger + { + public static readonly Logger c = new("SF", ConsoleColor.DarkRed); + private readonly string _name; + private readonly bool TraceOnError; + private readonly ConsoleColor _color; + + public Logger(string name, ConsoleColor color = ConsoleColor.Cyan, bool traceOnError = true) + { + _name = name; + _color = color; + TraceOnError = traceOnError; + } + + public void Log(params string[] message) + { + Console.ForegroundColor = ConsoleColor.White; + Console.Write(DateTime.Now.ToString("HH:mm:ss ")); + Console.ResetColor(); + Console.Write("<"); + Console.ForegroundColor = _color; + Console.Write(_name); + Console.ResetColor(); + Console.Write("> "); + Console.WriteLine(string.Join("\t", message)); + Console.ResetColor(); + } + + public void Warn(params string[] message) + { + Console.ForegroundColor = ConsoleColor.White; + Console.Write(DateTime.Now.ToString("HH:mm:ss ")); + Console.ResetColor(); + Console.Write("<"); + Console.ForegroundColor = ConsoleColor.Yellow; + Console.Write(_name); + Console.ResetColor(); + Console.Write("> "); + Console.WriteLine(string.Join("\t", message)); + Console.ResetColor(); + } + + public void Trail(params string[] msg) + { + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine($"\t└── {string.Join(' ', msg)}"); + Console.ResetColor(); + } + + public void Error(params string[] message) + { + Console.ForegroundColor = ConsoleColor.White; + Console.Write(DateTime.Now.ToString("HH:mm:ss ")); + Console.ResetColor(); + Console.Write("<"); + Console.ForegroundColor = ConsoleColor.Red; + Console.Write(_name); + Console.ResetColor(); + Console.Write("> "); + Console.ForegroundColor = ConsoleColor.White; + if (TraceOnError) + Console.BackgroundColor = ConsoleColor.DarkRed; + Console.WriteLine(string.Join("\t", message)); + Console.ResetColor(); +#if DEBUG + StackTrace trace = new(true); + if (TraceOnError) + Trail(trace.ToString()); +#endif + } + + public void Debug(params string[] message) + { +#if DEBUG + Console.ForegroundColor = ConsoleColor.White; + Console.Write(DateTime.Now.ToString("HH:mm:ss ")); + Console.ResetColor(); + Console.Write("<"); + Console.ForegroundColor = ConsoleColor.Cyan; + Console.Write(_name); + Console.ResetColor(); + Console.Write("> "); + Console.ForegroundColor = ConsoleColor.White; + Console.BackgroundColor = ConsoleColor.DarkMagenta; + Console.WriteLine(string.Join("\t", message)); + Console.ResetColor(); + Console.BackgroundColor = ConsoleColor.Black; + + StackTrace trace = new(true); + if (TraceOnError) + Trail(trace.ToString()); +#endif + } + } +} + diff --git a/AscNet.Common/Util/Miscs.cs b/AscNet.Common/Util/Miscs.cs new file mode 100644 index 00000000..96a86fc9 --- /dev/null +++ b/AscNet.Common/Util/Miscs.cs @@ -0,0 +1,35 @@ +namespace AscNet.Common.Util +{ + public static class Miscs + { + public static byte[] HexStringToByteArray(string hex) + { + if (hex.Length % 2 == 1) + throw new Exception("The binary key cannot have an odd number of digits"); + + byte[] arr = new byte[hex.Length >> 1]; + + for (int i = 0; i < hex.Length >> 1; ++i) + { + arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1]))); + } + + return arr; + } + + private static int GetHexVal(char hex) + { + int val = (int)hex; + //For uppercase A-F letters: + //return val - (val < 58 ? 48 : 55); + //For lowercase a-f letters: + //return val - (val < 58 ? 48 : 87); + //Or the two combined, but a bit slower: + return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); + } + public static byte ToByte(this bool val) + { + return val ? (byte)1 : (byte)0; + } + } +} diff --git a/AscNet.SDKServer/AscNet.SDKServer.csproj b/AscNet.SDKServer/AscNet.SDKServer.csproj new file mode 100644 index 00000000..3badfc4b --- /dev/null +++ b/AscNet.SDKServer/AscNet.SDKServer.csproj @@ -0,0 +1,23 @@ + + + + net7.0 + enable + enable + Library + + + + + + + + + + + + + + + + diff --git a/AscNet.SDKServer/Controllers/AccountController.cs b/AscNet.SDKServer/Controllers/AccountController.cs new file mode 100644 index 00000000..8ebc4bbc --- /dev/null +++ b/AscNet.SDKServer/Controllers/AccountController.cs @@ -0,0 +1,10 @@ +namespace AscNet.SDKServer.Controllers +{ + public class AccountController : IRegisterable + { + public static void Register(WebApplication app) + { + + } + } +} diff --git a/AscNet.SDKServer/Controllers/ConfigController.cs b/AscNet.SDKServer/Controllers/ConfigController.cs new file mode 100644 index 00000000..7ab32bb9 --- /dev/null +++ b/AscNet.SDKServer/Controllers/ConfigController.cs @@ -0,0 +1,10 @@ +namespace AscNet.SDKServer.Controllers +{ + internal class ConfigController : IRegisterable + { + public static void Register(WebApplication app) + { + + } + } +} diff --git a/AscNet.SDKServer/Properties/launchSettings.json b/AscNet.SDKServer/Properties/launchSettings.json new file mode 100644 index 00000000..bf66c889 --- /dev/null +++ b/AscNet.SDKServer/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "AscNet.SDKServer": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:52155;http://localhost:52156" + } + } +} \ No newline at end of file diff --git a/AscNet.SDKServer/SDKServer.cs b/AscNet.SDKServer/SDKServer.cs new file mode 100644 index 00000000..f8270c46 --- /dev/null +++ b/AscNet.SDKServer/SDKServer.cs @@ -0,0 +1,66 @@ +namespace AscNet.SDKServer +{ + public class SDKServer + { + public static readonly Common.Util.Logger c = new(nameof(SDKServer), ConsoleColor.Green); + + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Disables default logger + builder.Logging.ClearProviders(); + + var app = builder.Build(); + + app.Urls.Add("http://*:80"); + app.Urls.Add("https://*:443"); + + IEnumerable controllers = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(s => s.GetTypes()) + .Where(p => typeof(IRegisterable).IsAssignableFrom(p) && !p.IsInterface) + .Select(x => x); + + foreach (Type controller in controllers) + { + controller.GetMethod(nameof(IRegisterable.Register))!.Invoke(null, new object[] { app }); +#if DEBUG + c.Log($"Registered HTTP controller '{controller.Name}'"); +#endif + } + + app.UseMiddleware(); + + new Thread(() => app.Run()).Start(); + c.Log($"{nameof(SDKServer)} started in port {string.Join(", ", app.Urls.Select(x => x.Split(':').Last()))}!"); + } + + private class RequestLoggingMiddleware + { + private readonly RequestDelegate _next; + private static readonly string[] SurpressedRoutes = new string[] { "/report", "/sdk/dataUpload" }; + + public RequestLoggingMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + try + { + await _next(context); + } + finally + { + c.Log($"{context.Response.StatusCode} {context.Request.Method.ToUpper()} {context.Request.Path + context.Request.QueryString}"); + } + } + } + } + + public interface IRegisterable + { + public abstract static void Register(WebApplication app); + } +} \ No newline at end of file diff --git a/AscNet.SDKServer/appsettings.Development.json b/AscNet.SDKServer/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/AscNet.SDKServer/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/AscNet.SDKServer/appsettings.json b/AscNet.SDKServer/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/AscNet.SDKServer/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/AscNet.sln b/AscNet.sln new file mode 100644 index 00000000..9e372d85 --- /dev/null +++ b/AscNet.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33205.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AscNet", "AscNet\AscNet.csproj", "{817773C2-B4F3-48D5-9CDE-977BB0A0920E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AscNet.Common", "AscNet.Common\AscNet.Common.csproj", "{BE6BB857-5DA2-4A7D-B8AB-6901460DC2B9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AscNet.SDKServer", "AscNet.SDKServer\AscNet.SDKServer.csproj", "{B5040F93-BA7F-4E76-AF54-E3FAA857A5DA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {817773C2-B4F3-48D5-9CDE-977BB0A0920E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {817773C2-B4F3-48D5-9CDE-977BB0A0920E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {817773C2-B4F3-48D5-9CDE-977BB0A0920E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {817773C2-B4F3-48D5-9CDE-977BB0A0920E}.Release|Any CPU.Build.0 = Release|Any CPU + {BE6BB857-5DA2-4A7D-B8AB-6901460DC2B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE6BB857-5DA2-4A7D-B8AB-6901460DC2B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE6BB857-5DA2-4A7D-B8AB-6901460DC2B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE6BB857-5DA2-4A7D-B8AB-6901460DC2B9}.Release|Any CPU.Build.0 = Release|Any CPU + {B5040F93-BA7F-4E76-AF54-E3FAA857A5DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5040F93-BA7F-4E76-AF54-E3FAA857A5DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5040F93-BA7F-4E76-AF54-E3FAA857A5DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5040F93-BA7F-4E76-AF54-E3FAA857A5DA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0B18F04D-9322-4DF3-AEDE-FBB4F919E558} + EndGlobalSection +EndGlobal diff --git a/AscNet/AscNet.csproj b/AscNet/AscNet.csproj new file mode 100644 index 00000000..c039af73 --- /dev/null +++ b/AscNet/AscNet.csproj @@ -0,0 +1,19 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + PreserveNewest + + + + diff --git a/AscNet/Program.cs b/AscNet/Program.cs new file mode 100644 index 00000000..5dfa032e --- /dev/null +++ b/AscNet/Program.cs @@ -0,0 +1,13 @@ +using AscNet.Common.Util; + +namespace AscNet +{ + internal class Program + { + static void Main(string[] args) + { + Logger.c.Log("Starting..."); + SDKServer.SDKServer.Main(args); + } + } +} \ No newline at end of file diff --git a/Resources/Configs/config.json b/Resources/Configs/config.json new file mode 100644 index 00000000..0e0dcd23 --- /dev/null +++ b/Resources/Configs/config.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file