fork from 1.3

This commit is contained in:
moux23333
2024-01-27 21:06:07 +08:00
commit 22fc0b0848
1507 changed files with 24139 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
namespace FreeSR.Dispatch.Configuration
{
using Newtonsoft.Json.Linq;
internal class RegionConfiguration
{
public string Name { get; set; }
public string EnvType { get; set; }
public string DispatchUrl { get; set; }
}
}

View File

@@ -0,0 +1,59 @@
namespace FreeSR.Dispatch
{
using FreeSR.Database;
using FreeSR.Database.Account;
using FreeSR.Database.Configuration;
using FreeSR.Dispatch.Service;
using FreeSR.Dispatch.Service.Manager;
using FreeSR.Shared.Configuration;
using FreeSR.Shared.Exceptions;
using NLog;
internal static class DispatchServer
{
private const string Title = "FreeSR Dispatch Server (EXPERIMENTAL OPEN SOURCE BUILD)";
private static readonly Logger s_log = LogManager.GetCurrentClassLogger();
private static void Main(string[] args)
{
Directory.SetCurrentDirectory(AppContext.BaseDirectory);
AppDomain.CurrentDomain.UnhandledException += OnFatalException;
Console.Title = Title;
s_log.Info("Initializing...");
ConfigurationManager<DispatchServerConfiguration>.Instance.Initialize("DispatchServer.json");
var serverConfiguration = ConfigurationManager<DispatchServerConfiguration>.Instance.Model;
DatabaseManager.Instance.Initialize(serverConfiguration.Database);
var mongoDatabase = DatabaseManager.Instance.MongoDatabase;
DatabaseManager.Instance.Add(new AccountDatabase(mongoDatabase, DatabaseManager.Instance.GetCollectionName(DatabaseType.Account)));
RegionManager.Initialize(serverConfiguration.Region);
HttpDispatchService.Initialize(serverConfiguration.Network);
s_log.Info("Server is ready!");
Thread.Sleep(-1); // TODO: Console handler
}
private static void OnFatalException(object sender, UnhandledExceptionEventArgs args)
{
if (args.ExceptionObject is ServerInitializationException initException)
{
Console.WriteLine("Server initialization failed, unhandled exception!");
Console.WriteLine(initException);
}
else
{
Console.WriteLine("Unhandled exception in runtime!");
Console.WriteLine(args.ExceptionObject);
}
Console.WriteLine("Press enter to close this window...");
Console.ReadLine();
}
}
}

View File

@@ -0,0 +1,21 @@
{
"Network": {
"Host": "0.0.0.0",
"Port": 8888
},
"Database": {
"ConnectionString": "mongodb://127.0.0.1:27017/",
"Name": "FreeSR",
"Entries": [
{
"CollectionName": "accounts",
"Type": "Account"
}
]
},
"Region": {
"Name": "FreeSR",
"EnvType": "2",
"DispatchUrl": "http://dispatch.starrails.com/query_gateway"
}
}

View File

@@ -0,0 +1,13 @@
namespace FreeSR.Dispatch
{
using FreeSR.Database.Configuration;
using FreeSR.Dispatch.Configuration;
using FreeSR.Shared.Configuration;
internal class DispatchServerConfiguration
{
public NetworkConfiguration Network { get; set; }
public DatabaseConfiguration Database { get; set; }
public RegionConfiguration Region { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ceen.Httpd" Version="0.9.10" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FreeSR.Database.Account\FreeSR.Database.Account.csproj" />
<ProjectReference Include="..\FreeSR.Proto\FreeSR.Proto.csproj" />
<ProjectReference Include="..\FreeSR.Shared\FreeSR.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="DispatchServer.example.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,32 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using Newtonsoft.Json;
internal class ComboGranterApiGetConfigHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAllJsonAsync(JsonConvert.SerializeObject(new
{
retcode = 0,
message = "OK",
data =
new
{
protocol = true,
qr_enabled = true,
log_level = "INFO",
announce_url = "https://sdk.hoyoverse.com/hkrpg/announcement/index.html?sdk_presentation_style=fullscreen\u0026sdk_screen_transparent=true\u0026auth_appid=announcement\u0026authkey_ver=1\u0026sign_type=2#/",
push_alias_type = 0,
disable_ysdk_guard = true,
enable_announce_pic_popup = true
}
}));
return true;
}
}
}

View File

@@ -0,0 +1,39 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
internal class ComboTokenRequestHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
var data = await context.Request.Body.ReadAllAsStringAsync();
var json = JObject.Parse(data);
var clientData = JObject.Parse((string)json["data"]);
var dataObject = new JObject
{
{"combo_id", 1},
{"open_id", clientData["uid"]},
{"combo_token", clientData["token"]},
{"data", new JObject {{"guest", false}}},
{"heartbeat", false},
{"account_type", 1},
{"fatigue_remind", null}
};
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_SUCC)
.Message("OK")
.Object("data", dataObject)
.Build());
return true;
}
}
}

View File

@@ -0,0 +1,34 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
using Newtonsoft.Json.Linq;
internal class GetAgreementInfosHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_SUCC)
.Message("OK")
.Object("data", Data)
.Build());
return true;
}
private static JObject Data
{
get
{
return new JObject
{
{"marketing_agreements", new JArray()}
};
}
}
}
}

View File

@@ -0,0 +1,22 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
internal class GetExperimentListHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_SUCC)
.Boolean("success", true)
.String("message", "")
.Build());
return true;
}
}
}

View File

@@ -0,0 +1,26 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Util;
using NLog;
using System.Threading.Tasks;
internal class HkrpgDataUploadHandler : IHttpModule
{
private static readonly Logger s_log = LogManager.GetCurrentClassLogger();
public async Task<bool> HandleAsync(IHttpContext context)
{
string logs = await context.Request.Body.ReadAllAsStringAsync();
s_log.Info(logs);
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Code(0)
.Build());
return true;
}
}
}

View File

@@ -0,0 +1,57 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Database;
using FreeSR.Database.Account;
using FreeSR.Database.Account.Model;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
internal class LoginRequestHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
string data = await context.Request.Body.ReadAllAsStringAsync();
JObject loginJson = JObject.Parse(data);
AccountDatabase accountDatabase = DatabaseManager.Instance.Get<AccountDatabase>();
string accountName = (string)loginJson["account"];
string password = (string)loginJson["password"];
AccountModel account = await accountDatabase.GetByName(accountName);
if (account == null)
{
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_FAIL)
.Message("Account not found.")
.Object("data", null)
.Build());
return true;
}
// no password check, because client patch is closed-source for now.
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_SUCC)
.Message("OK")
.Object("data", new JObject
{
{"account", account.ToLoginResponseData()},
{"device_grant_required", false},
{"safe_moblie_required", false},
{"realperson_required", false},
{"reactivate_required", false},
{"realname_operation", "None"}
, })
.Build());
return true;
}
}
}

View File

@@ -0,0 +1,36 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Service.Manager;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
using NLog;
using System.Threading.Tasks;
internal class QueryDispatchHandler : IHttpModule
{
private static readonly Logger s_log = LogManager.GetCurrentClassLogger();
public async Task<bool> HandleAsync(IHttpContext context)
{
var query = context.Request.QueryString;
var version = query["version"];
var timestamp = query["t"];
var languageType = query["language_type"];
var platformType = query["platform_type"];
s_log.Info($"query_dispatch: version: {version}, time: {timestamp}, language: {languageType}, platform: {platformType}");
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "text/plain";
await context.Response.WriteAllAsync(Convert.ToBase64String(ProtobufUtil.Serialize(new DispatchRegionsData
{
Retcode = Retcode.RETCODE_RET_SUCC,
TopSeverRegionName = RegionManager.GetTopServerRegionName(),
RegionList = RegionManager.GetRegionList()
})));
return true;
}
}
}

View File

@@ -0,0 +1,34 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
using System.Threading.Tasks;
internal class QueryGatewayHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "text/plain";
await context.Response.WriteAllAsync(Convert.ToBase64String(ProtobufUtil.Serialize(new Gateserver
{
Retcode = Retcode.RETCODE_RET_SUCC,
Msg = "OK",
Ip = "127.0.0.1",
RegionName = "FreeSR",
Port = 22301,
Mljaogdhcki = true,
LoginWhiteMsg = "Access verification failed. Please check if you have logged in to the correct account and server.",
Jcdlppbocpe = true,
Gifijbibiin = true,
Ibplbfhmgkf = true,
MbResVersion = "5335706",
AssetBundleUrl = "https://autopatchos.starrails.com/asb/V1.3Live/output_5355192_0007722cfc",
ExResourceUrl = "https://autopatchos.starrails.com/design_data/V1.3Live/output_5371504_9fdb2fe63e",
})));
return true;
}
}
}

View File

@@ -0,0 +1,37 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
internal class RiskyApiCheckHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_SUCC)
.Message("OK")
.Object("data", CaptchaData)
.Build());
return true;
}
private static JObject CaptchaData
{
get
{
return new JObject
{
{"id", ""},
{"action", "ACTION_NONE"},
{"geetest", null}
};
}
}
}
}

View File

@@ -0,0 +1,32 @@
namespace FreeSR.Dispatch.Handlers.Sdk
{
using Ceen;
using FreeSR.Database;
using FreeSR.Database.Account;
internal class CreateAccountHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
var query = context.Request.QueryString;
var name = query["user"];
var password = query["pass"];
var database = DatabaseManager.Instance.Get<AccountDatabase>();
var account = await database.Create(name, password);
context.Response.StatusCode = HttpStatusCode.OK;
if (account == null)
{
await context.Response.WriteAllAsync("Sorry, this username is already taken.", "text/plain");
}
else
{
await context.Response.WriteAllAsync($"Successfully created account with name {account.Name}, uid: {account.Uid}", "text/plain");
}
return true;
}
}
}

View File

@@ -0,0 +1,20 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Dispatch.Util;
using System.Threading.Tasks;
internal class SdkDataUploadHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
context.Response.StatusCode = HttpStatusCode.OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Code(0)
.Build());
return true;
}
}
}

View File

@@ -0,0 +1,61 @@
namespace FreeSR.Dispatch.Handlers
{
using Ceen;
using FreeSR.Database;
using FreeSR.Database.Account;
using FreeSR.Database.Account.Model;
using FreeSR.Dispatch.Util;
using FreeSR.Proto;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
internal class TokenLoginRequestHandler : IHttpModule
{
public async Task<bool> HandleAsync(IHttpContext context)
{
var data = await context.Request.Body.ReadAllAsStringAsync();
var json = JObject.Parse(data);
var uid = int.Parse((string)json["uid"]);
var token = (string)json["token"];
AccountDatabase accountDatabase = DatabaseManager.Instance.Get<AccountDatabase>();
AccountModel account = await accountDatabase.GetByUid(uid);
if (account == null)
{
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_FAIL)
.Message("Account not found.")
.Object("data", null)
.Build());
return true;
}
else if (account.Token != token)
{
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_FAIL)
.Message("Invalid user token.")
.Object("data", null)
.Build());
return true;
}
await context.Response.WriteAllJsonAsync(DispatchResponseBuilder.Create()
.Retcode(Retcode.RETCODE_RET_SUCC)
.Message("OK")
.Object("data", new JObject
{
{"account", account.ToLoginResponseData()},
{"device_grant_required", false},
{"safe_moblie_required", false},
{"realperson_required", false},
{"reactivate_required", false},
{"realname_operation", "None"}
})
.Build());
return true;
}
}
}

View File

@@ -0,0 +1,45 @@
namespace FreeSR.Dispatch.Service
{
using Ceen.Httpd;
using Ceen.Httpd.Logging;
using FreeSR.Dispatch.Handlers;
using FreeSR.Dispatch.Handlers.Sdk;
using FreeSR.Shared.Configuration;
using System.Net;
internal static class HttpDispatchService
{
private static ServerConfig s_httpdConfiguration;
public static void Initialize(NetworkConfiguration config)
{
s_httpdConfiguration = CreateConfiguration();
_ = BootHttpAsync(config);
}
private static ServerConfig CreateConfiguration()
{
return new ServerConfig().AddLogger(new CLFStdOut())
.AddRoute("/query_dispatch", new QueryDispatchHandler())
.AddRoute("/query_gateway", new QueryGatewayHandler())
.AddRoute("/hkrpg_global/mdk/shield/api/login", new LoginRequestHandler())
.AddRoute("/hkrpg_global/combo/granter/login/v2/login", new ComboTokenRequestHandler())
.AddRoute("/hkrpg_global/mdk/shield/api/verify", new TokenLoginRequestHandler())
.AddRoute("/sdk/dataUpload", new SdkDataUploadHandler())
.AddRoute("/hkrpg/dataUpload", new HkrpgDataUploadHandler())
.AddRoute("/account/risky/api/check", new RiskyApiCheckHandler())
.AddRoute("/hkrpg_global/mdk/agreement/api/getAgreementInfos", new GetAgreementInfosHandler())
.AddRoute("/data_abtest_api/config/experiment/list", new GetExperimentListHandler())
.AddRoute("/hkrpg_global/combo/granter/api/getConfig", new ComboGranterApiGetConfigHandler())
.AddRoute("/sdk/createaccount", new CreateAccountHandler());
}
private static async Task BootHttpAsync(NetworkConfiguration config)
{
await HttpServer.ListenAsync(new IPEndPoint(
IPAddress.Parse(config.Host),
config.Port),
false, s_httpdConfiguration);
}
}
}

View File

@@ -0,0 +1,31 @@
namespace FreeSR.Dispatch.Service.Manager
{
using FreeSR.Dispatch.Configuration;
using FreeSR.Proto;
internal static class RegionManager
{
private static RegionConfiguration s_configuration;
public static void Initialize(RegionConfiguration configuration)
{
s_configuration = configuration;
}
public static List<RegionEntry> GetRegionList()
{
var region = new RegionEntry
{
EnvType = s_configuration.EnvType,
DispatchUrl = s_configuration.DispatchUrl,
Name = s_configuration.Name,
DisplayName = s_configuration.Name,
Title = s_configuration.Name
};
return new List<RegionEntry> { region };
}
public static string GetTopServerRegionName() => s_configuration.Name;
}
}

View File

@@ -0,0 +1,38 @@
namespace FreeSR.Dispatch.Util
{
using FreeSR.Database.Account.Model;
using Newtonsoft.Json.Linq;
internal static class DispatchHelper
{
public static JObject ToLoginResponseData(this AccountModel model)
{
return new JObject
{
{"uid", model.Uid},
{"name", model.Name},
{"email", ""},
{"mobile", ""},
{"is_email_verify", "0"},
{"realname", ""},
{"identity_card", ""},
{"safe_mobile", ""},
{"facebook_name", ""},
{"google_name", ""},
{"twitter_name", ""},
{"game_center_name", ""},
{"apple_name", ""},
{"sony_name", ""},
{"tap_name", ""},
{"country", "CN"},
{"reactivate_ticket", ""},
{"area_code", "**"},
{"device_grant_ticket", ""},
{"steam_name", ""},
{"unmasked_email", ""},
{"unmasked_email_type", 0},
{"token", model.Token}
};
}
}
}

View File

@@ -0,0 +1,69 @@
namespace FreeSR.Dispatch.Util
{
using Newtonsoft.Json.Linq;
internal class DispatchResponseBuilder
{
private readonly JObject _jsonObject;
private DispatchResponseBuilder()
{
_jsonObject = new JObject();
}
public string Build()
{
return _jsonObject.ToString();
}
public DispatchResponseBuilder Code(int code)
{
_jsonObject["code"] = code;
return this;
}
public DispatchResponseBuilder Retcode(int retcode)
{
_jsonObject["retcode"] = retcode;
return this;
}
public DispatchResponseBuilder Message(string message)
{
_jsonObject["message"] = message;
return this;
}
public DispatchResponseBuilder Boolean(string field, bool value)
{
_jsonObject[field] = value;
return this;
}
public DispatchResponseBuilder String(string field, string value)
{
_jsonObject[field] = value;
return this;
}
public DispatchResponseBuilder Int(string field, int value)
{
_jsonObject[field] = value;
return this;
}
public DispatchResponseBuilder Array(string field, JArray array)
{
_jsonObject[field] = array;
return this;
}
public DispatchResponseBuilder Object(string field, JObject jsonObject)
{
_jsonObject[field] = jsonObject;
return this;
}
public static DispatchResponseBuilder Create() => new DispatchResponseBuilder();
}
}

View File

@@ -0,0 +1,15 @@
namespace FreeSR.Dispatch.Util
{
using ProtoBuf;
internal static class ProtobufUtil
{
public static byte[] Serialize<T>(T obj) where T : class
{
var stream = new MemoryStream();
Serializer.Serialize(stream, obj);
return stream.ToArray();
}
}
}