Initial commit: R0Installer source, patch generator, API backend and build scripts

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-27 18:03:44 +08:00
commit 6124d6060c
33 changed files with 8389 additions and 0 deletions
+171
View File
@@ -0,0 +1,171 @@
using System;
using System.IO;
using System.Net;
using System.Windows.Forms;
namespace R0Installer
{
public enum UpdateMode
{
MainProgram,
AntiCheat
}
public enum InstallMode
{
Full,
ClientOnly,
AcOnly
}
static class Program
{
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
ServicePointManager.DefaultConnectionLimit = 64;
string installerConfig = ConfigManager.GetInstallerConfig();
if (args.Length >= 2)
{
string command = args[0].ToLower();
string currentVersion = args[1];
ConfigManager.MatchResult cmdMatch = new ConfigManager.MatchResult();
if (installerConfig != null)
{
cmdMatch = ConfigManager.MatchCommand(installerConfig, command);
}
if (!string.IsNullOrEmpty(cmdMatch.ProjectId))
{
string projectConfig = ConfigManager.GetProjectConfigSync(cmdMatch.ProjectId);
if (projectConfig == null)
{
Console.WriteLine("ERROR: cannot load project config");
return;
}
string updateTarget = cmdMatch.UpdateTarget;
if (string.IsNullOrEmpty(updateTarget)) updateTarget = "update";
string updateConfig = ConfigManager.GetUpdateConfig(projectConfig, updateTarget);
if (string.IsNullOrEmpty(updateConfig))
{
Console.WriteLine("ERROR: update target not found");
return;
}
string checkApi = ConfigManager.ExtractJsonValue(updateConfig, "check_api");
if (string.IsNullOrEmpty(checkApi))
{
Console.WriteLine("ERROR: check_api not configured");
return;
}
ConfigManager.UpdateSummary summary = ConfigManager.GetUpdateSummarySync(cmdMatch.ProjectId, checkApi, currentVersion);
if (!summary.HasUpdate)
{
Console.WriteLine("OK");
return;
}
// 是否静默更新,优先级从高到低:
// 1) 命令行 --silent:调用方强制静默
// 2) 云端按版本下发的 silent/v3/update/check 响应里的 silent 字段,可开可关)
// 3) 命令规则 / 更新目标配置中的 silent(对该命令的所有版本生效)
bool silent;
if (HasSilentFlag(args))
silent = true;
else if (summary.HasSilentField)
silent = summary.Silent;
else
silent = cmdMatch.Silent || ConfigManager.ExtractJsonBool(updateConfig, "silent");
if (silent)
{
new SilentUpdater(currentVersion, true, cmdMatch.ProjectId, updateTarget).Run();
}
else
{
Application.Run(new UpdateForm(currentVersion, true, cmdMatch.ProjectId, updateTarget));
}
}
else
{
RunInstallMode(installerConfig);
}
}
else
{
RunInstallMode(installerConfig);
}
}
// 检测命令行中是否带有静默更新标志。支持 --silent / -s / /silent / /s 几种写法,
// 可出现在任意位置(通常作为第三个参数:R0Installer.exe update <version> --silent)。
private static bool HasSilentFlag(string[] args)
{
if (args == null) return false;
foreach (string arg in args)
{
if (string.IsNullOrEmpty(arg)) continue;
switch (arg.Trim().ToLowerInvariant())
{
case "--silent":
case "-s":
case "/silent":
case "/s":
return true;
}
}
return false;
}
private static void RunInstallMode(string installerConfig)
{
string projectId = "r0_arena";
InstallMode mode = InstallMode.Full;
if (installerConfig != null)
{
string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
string exeName = Path.GetFileNameWithoutExtension(exePath);
ConfigManager.MatchResult fileMatch = ConfigManager.MatchFilename(installerConfig, exeName);
if (!string.IsNullOrEmpty(fileMatch.ProjectId))
projectId = fileMatch.ProjectId;
switch (fileMatch.InstallMode)
{
case "client_only": mode = InstallMode.ClientOnly; break;
case "ac_only": mode = InstallMode.AcOnly; break;
default: mode = InstallMode.Full; break;
}
}
else
{
try
{
string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
string exeName = Path.GetFileNameWithoutExtension(exePath);
if (exeName.StartsWith("R0ClientInstaller", StringComparison.OrdinalIgnoreCase))
mode = InstallMode.ClientOnly;
else if (exeName.StartsWith("R0AcInstaller", StringComparison.OrdinalIgnoreCase))
mode = InstallMode.AcOnly;
}
catch { }
}
Application.Run(new MainForm(mode, projectId));
}
}
}