diff --git a/BLHX.Server.Common/BLHX.Server.Common.csproj b/BLHX.Server.Common/BLHX.Server.Common.csproj
index 51069f8..7eaa8da 100644
--- a/BLHX.Server.Common/BLHX.Server.Common.csproj
+++ b/BLHX.Server.Common/BLHX.Server.Common.csproj
@@ -7,6 +7,7 @@
+
diff --git a/BLHX.Server.Common/Database/Account.cs b/BLHX.Server.Common/Database/Account.cs
new file mode 100644
index 0000000..cd83db6
--- /dev/null
+++ b/BLHX.Server.Common/Database/Account.cs
@@ -0,0 +1,34 @@
+using Microsoft.EntityFrameworkCore;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace BLHX.Server.Common.Database
+{
+ public sealed class AccountContext : DbContext, IBLHXDBContext
+ {
+ public static string DbPath => "Databases/accounts.db";
+ public DbSet Accounts { get; set; }
+
+ public AccountContext()
+ {
+ Database.EnsureCreated();
+ }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder options)
+ => options.UseSqlite($"Data Source={((IBLHXDBContext)this).GetFullDbPath()}");
+ }
+
+ [PrimaryKey(nameof(Uid))]
+ public class Account
+ {
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public uint Uid { get; set; }
+ public string DeviceId { get; set; }
+ public string Token { get; set; }
+
+ public Account(string deviceId, string token)
+ {
+ DeviceId = deviceId;
+ Token = token;
+ }
+ }
+}
diff --git a/BLHX.Server.Common/Database/DBManager.cs b/BLHX.Server.Common/Database/DBManager.cs
new file mode 100644
index 0000000..be69dfc
--- /dev/null
+++ b/BLHX.Server.Common/Database/DBManager.cs
@@ -0,0 +1,36 @@
+using BLHX.Server.Common.Utils;
+using Microsoft.EntityFrameworkCore;
+using System.Reflection;
+
+namespace BLHX.Server.Common.Database
+{
+ public static class DBManager
+ {
+ public static readonly Logger c = new(nameof(DBManager), ConsoleColor.DarkCyan);
+ public static AccountContext AccountContext { get; }
+
+ static DBManager()
+ {
+ foreach (var dbCtx in Assembly.GetExecutingAssembly().GetTypes().Where(p => typeof(IBLHXDBContext).IsAssignableFrom(p) && !p.IsInterface))
+ {
+ var dbPath = (string)dbCtx.GetProperty(nameof(AccountContext.DbPath))!.GetValue(null)!;
+ var saveDir = Path.GetDirectoryName(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, dbPath))!;
+ if (!Directory.Exists(saveDir))
+ Directory.CreateDirectory(saveDir);
+ }
+
+ AccountContext = new AccountContext();
+ }
+ }
+
+ public interface IBLHXDBContext
+ {
+ public static abstract string DbPath { get; }
+ public string GetFullDbPath();
+ }
+
+ public interface IBLHXDBContext : IBLHXDBContext where TSelf : DbContext
+ {
+ string IBLHXDBContext.GetFullDbPath() => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, (string)typeof(TSelf).GetProperty(nameof(DbPath))!.GetValue(null)!);
+ }
+}
diff --git a/BLHX.Server.SDK/Controllers/AccountController.cs b/BLHX.Server.SDK/Controllers/AccountController.cs
index 05b9a55..c740c18 100644
--- a/BLHX.Server.SDK/Controllers/AccountController.cs
+++ b/BLHX.Server.SDK/Controllers/AccountController.cs
@@ -1,4 +1,6 @@
-using BLHX.Server.Sdk;
+using BLHX.Server.Common.Database;
+using BLHX.Server.Sdk;
+using BLHX.Server.SDK.Models;
namespace BLHX.Server.SDK.Controllers
{
@@ -6,7 +8,43 @@ namespace BLHX.Server.SDK.Controllers
{
public static void Register(WebApplication app)
{
+ app.MapPost("user/create", (HttpContext ctx, UserCreateRequest req) =>
+ {
+ var account = DBManager.AccountContext.Accounts.SingleOrDefault(x => x.DeviceId == req.DeviceId);
+ UserCreateResponse rsp = new() { Result = 0, IsNew = 0 };
+ if (account is null)
+ {
+ account = new(req.DeviceId, Guid.NewGuid().ToString());
+ DBManager.AccountContext.Add(account);
+ DBManager.AccountContext.SaveChanges();
+ rsp.IsNew = 1;
+ }
+ rsp.Uid = account.Uid;
+ rsp.Token = account.Token;
+
+ return ctx.Response.WriteAsJsonAsync(rsp);
+ });
+
+ app.MapPost("user/login", (HttpContext ctx, UserLoginRequest req) =>
+ {
+ var account = DBManager.AccountContext.Accounts.SingleOrDefault(x => x.Uid == req.Uid);
+
+ if (account is not null && account.Token == req.Token)
+ {
+ return ctx.Response.WriteAsJsonAsync(new UserLoginResponse()
+ {
+ AccessToken = account.Token,
+ Birth = null,
+ ChannelId = req.StoreId,
+ CurrentTimestampMs = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
+ KrKmcStatus = 2,
+ Result = 0,
+ TransCode = "NULL"
+ });
+ }
+ return ctx.Response.WriteAsJsonAsync(new BaseResponse() { Result = 1 });
+ });
}
}
}
diff --git a/BLHX.Server.SDK/Models/Base.cs b/BLHX.Server.SDK/Models/Base.cs
new file mode 100644
index 0000000..8ae0899
--- /dev/null
+++ b/BLHX.Server.SDK/Models/Base.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+
+namespace BLHX.Server.SDK.Models
+{
+ class BaseResponse
+ {
+ [JsonPropertyName("result")]
+ public int Result { get; set; }
+ }
+}
diff --git a/BLHX.Server.SDK/Models/User.cs b/BLHX.Server.SDK/Models/User.cs
new file mode 100644
index 0000000..a71a4a0
--- /dev/null
+++ b/BLHX.Server.SDK/Models/User.cs
@@ -0,0 +1,43 @@
+using BLHX.Server.Sdk;
+using System.Text.Json.Serialization;
+
+namespace BLHX.Server.SDK.Models
+{
+ record UserCreateRequest(string ChannelId, string DeviceId) : BindableFormRequest;
+ record UserLoginRequest(uint Uid, string StoreId, string DeviceId, string Platform, string Token) : BindableFormRequest;
+
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+ class UserLoginResponse : BaseResponse
+ {
+ [JsonPropertyName("accessToken")]
+ public string AccessToken { get; set; }
+
+ [JsonPropertyName("birth")]
+ public dynamic? Birth { get; set; }
+
+ [JsonPropertyName("channelId")]
+ public string ChannelId { get; set; }
+
+ [JsonPropertyName("current_timestamp_ms")]
+ public long CurrentTimestampMs { get; set; }
+
+ [JsonPropertyName("kr_kmc_status")]
+ public int KrKmcStatus { get; set; }
+
+ [JsonPropertyName("transcode")]
+ public string TransCode { get; set; }
+ }
+
+ class UserCreateResponse : BaseResponse
+ {
+ [JsonPropertyName("uid")]
+ public uint Uid { get; set; }
+
+ [JsonPropertyName("token")]
+ public string Token { get; set; }
+
+ [JsonPropertyName("isNew")]
+ public int IsNew { get; set; }
+ }
+#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+}
diff --git a/BLHX.Server.SDK/SDKServer.cs b/BLHX.Server.SDK/SDKServer.cs
index 256de82..ef73891 100644
--- a/BLHX.Server.SDK/SDKServer.cs
+++ b/BLHX.Server.SDK/SDKServer.cs
@@ -1,11 +1,15 @@
using BLHX.Server.Common.Utils;
+using Microsoft.AspNetCore.Mvc.Abstractions;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc;
+using System.Globalization;
using System.Reflection;
namespace BLHX.Server.Sdk
{
public class SDKServer
{
- static readonly Logger c = new(nameof(SDKServer), ConsoleColor.Green);
+ public static readonly Logger c = new(nameof(SDKServer), ConsoleColor.Green);
static Task? runTask = null;
public static void Main(string[] args)
@@ -13,11 +17,15 @@ namespace BLHX.Server.Sdk
var builder = WebApplication.CreateBuilder(args);
// Disables default logger
builder.Logging.ClearProviders();
+ builder.Services.AddMvcCore();
+ builder.Services.AddAntiforgery();
var app = builder.Build();
app.Urls.Add("http://*:80");
app.Urls.Add("https://*:443");
+ app.UseStatusCodePages();
+ app.UseAntiforgery();
app.UseMiddleware();
foreach (Type controller in Assembly.GetExecutingAssembly().GetTypes().Where(p => typeof(IRegisterable).IsAssignableFrom(p) && !p.IsInterface))
@@ -63,4 +71,46 @@ namespace BLHX.Server.Sdk
{
public abstract static void Register(WebApplication app);
}
+
+ public static class BindingExtensions
+ {
+ public static async Task BindFromForm(this HttpContext httpContext)
+ {
+ var serviceProvider = httpContext.RequestServices;
+ var factory = serviceProvider.GetRequiredService();
+ var metadataProvider = serviceProvider.GetRequiredService();
+
+ var metadata = metadataProvider.GetMetadataForType(typeof(T));
+ var modelBinder = factory.CreateBinder(new()
+ {
+ Metadata = metadata
+ });
+
+ var context = new DefaultModelBindingContext
+ {
+ ModelMetadata = metadata,
+ ModelName = string.Empty,
+ ValueProvider = new FormValueProvider(
+ BindingSource.Form,
+ httpContext.Request.Form,
+ CultureInfo.InvariantCulture
+ ),
+ ActionContext = new ActionContext(
+ httpContext,
+ new RouteData(),
+ new ActionDescriptor()),
+ ModelState = new ModelStateDictionary()
+ };
+ await modelBinder.BindModelAsync(context);
+ return (T?)context.Result.Model;
+ }
+ }
+
+ public abstract record BindableFormRequest
+ {
+ public static async ValueTask BindAsync(HttpContext httpContext, ParameterInfo parameter)
+ {
+ return await httpContext.BindFromForm();
+ }
+ }
}
diff --git a/BLHX.Server/Program.cs b/BLHX.Server/Program.cs
index 12a7f19..4bca586 100644
--- a/BLHX.Server/Program.cs
+++ b/BLHX.Server/Program.cs
@@ -1,4 +1,5 @@
-using BLHX.Server.Common.Utils;
+using BLHX.Server.Common.Database;
+using BLHX.Server.Common.Utils;
using BLHX.Server.Game;
using BLHX.Server.Sdk;
using System.Net.NetworkInformation;