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