107 lines
4.2 KiB
C#
107 lines
4.2 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using GameMethods = Ananta.Server.RpcTypes.Client4229938.Methods.Game;
|
|
using LoginMethods = Ananta.Server.RpcTypes.Client4229938.Methods.LoginGate;
|
|
|
|
namespace Ananta.Server.Protocol.Client4229938;
|
|
|
|
/// <summary>
|
|
/// Typed login/gate payload factories. No serializer calls live here: handlers pass these DTOs to ReturnAsync/NotifyAsync.
|
|
/// </summary>
|
|
internal static class LoginCodec
|
|
{
|
|
internal static LoginMethods.PatchCheckResult PatchCheck()
|
|
// NOTE (2026-09-09): advertising patchVersion=1 loops the client
|
|
// (it polls RequestPatchesCheckDataFromLogin until patch DATA arrives
|
|
// via RequestPatchesFromLogin, whose payload format is still unknown).
|
|
// Keep 0 until the real patch-data response is reverse-engineered.
|
|
=> new();
|
|
|
|
internal static LoginMethods.CheckAccountResult CheckAccount()
|
|
{
|
|
var deviceId = Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(Profile.AccountId)));
|
|
// Do not use an anonymous object here for device-id aliases.
|
|
// System.Text.Json treats property names case-insensitively while building
|
|
// anonymous-type metadata, so having both `deviceid` and `deviceId` makes
|
|
// CheckAccount throw before a response body can be serialized.
|
|
// A dictionary keeps the exact wire keys and avoids that collision.
|
|
var loginJson = JsonSerializer.Serialize(new Dictionary<string, object?>
|
|
{
|
|
["deviceid"] = deviceId,
|
|
["device_id"] = deviceId,
|
|
["udid"] = deviceId,
|
|
["unisdk_device_id"] = deviceId,
|
|
["code"] = 200,
|
|
["subcode"] = 0,
|
|
["msg"] = "ok",
|
|
["uid"] = Profile.AccountId,
|
|
["sdkuid"] = Profile.AccountId,
|
|
["user_id"] = Profile.AccountId,
|
|
["aid"] = Profile.Aid,
|
|
["pid"] = Profile.PlayerPid,
|
|
["player_id"] = Profile.PlayerPid.ToString(),
|
|
["role_id"] = Profile.PlayerPid.ToString(),
|
|
["role_name"] = Profile.DisplayName,
|
|
["server_id"] = Profile.ServerId.ToString(),
|
|
["host_id"] = Profile.ServerId,
|
|
["username"] = Profile.UserName,
|
|
["account"] = Profile.AccountId,
|
|
["platform"] = "pc",
|
|
["token"] = Profile.LoginToken,
|
|
["access_token"] = Profile.LoginToken,
|
|
["ext_access_token"] = Profile.LoginToken,
|
|
["sessionid"] = Profile.LoginToken,
|
|
["login_ticket"] = Profile.LoginToken,
|
|
["realname_status"] = 1,
|
|
["realname_verify_status"] = 1,
|
|
["mobile_bind_status"] = 1
|
|
});
|
|
|
|
return new LoginMethods.CheckAccountResult
|
|
{
|
|
unisdk_login_json = loginJson,
|
|
Token = Profile.LoginToken,
|
|
UserName = Profile.UserName,
|
|
Aid = Profile.Aid,
|
|
NeedRealNameTip = false,
|
|
NeedRoleEnter = false,
|
|
RealNameVerified = true,
|
|
HostId = Profile.ServerId,
|
|
code = 200,
|
|
subcode = 0,
|
|
msg = "ok"
|
|
};
|
|
}
|
|
|
|
internal static LoginMethods.EnterGameResult EnterGame(string gateHost, int gatePort)
|
|
=> new()
|
|
{
|
|
aid = Profile.Aid,
|
|
playerPid = Profile.PlayerPid,
|
|
gate = new LoginMethods.GateServerInfo
|
|
{
|
|
aid = Profile.Aid,
|
|
playerPid = Profile.PlayerPid,
|
|
host = gateHost,
|
|
port = gatePort,
|
|
loginToken = Profile.LoginToken,
|
|
serverId = Profile.ServerId,
|
|
accountId = Profile.AccountId
|
|
}
|
|
};
|
|
|
|
internal static GameMethods.ServerTime ServerTime(double? clientUnixTime = null)
|
|
{
|
|
var serverUnixTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0;
|
|
return new GameMethods.ServerTime
|
|
{
|
|
clientUnixTime = clientUnixTime ?? serverUnixTime,
|
|
serverUnixTime = serverUnixTime
|
|
};
|
|
}
|
|
|
|
internal static LoginMethods.GameServerInfo GameServerInfo(string host, int port)
|
|
=> new() { host = host, port = port, gameToken = Profile.GameToken };
|
|
}
|