229 lines
8.2 KiB
C#
229 lines
8.2 KiB
C#
using System;
|
|||
|
|
using System.Diagnostics;
|
||
|
|
using System.IO;
|
||
|
|
using System.Linq;
|
||
|
|
using PCL.Core.App;
|
||
|
|
using PCL.Core.App.Localization;
|
||
|
|
using PCL.Core.IO.Net;
|
||
|
|
using PCL.Core.Logging;
|
||
|
|
using PCL.Core.Utils;
|
||
|
|
using PCL.Core.Utils.Secret;
|
||
|
|
using static PCL.Core.Link.EasyTier.ETInfoProvider;
|
||
|
|
using static PCL.Core.Link.Lobby.LobbyInfoProvider;
|
||
|
|
using static PCL.Core.Link.Natayark.NatayarkProfileManager;
|
||
|
|
|
||
|
|
namespace PCL.Core.Link.EasyTier;
|
||
|
|
// ReSharper disable InconsistentNaming
|
||
|
|
|
||
|
|
public enum ETState
|
||
|
|
{
|
||
|
|
Stopped,
|
||
|
|
Running,
|
||
|
|
Ready
|
||
|
|
}
|
||
|
|
|
||
|
|
public static class ETController
|
||
|
|
{
|
||
|
|
public static Process? ETProcess { get; private set; }
|
||
|
|
public static int ETRpcPort { get; private set; }
|
||
|
|
public static ETState Status { get; internal set; }
|
||
|
|
|
||
|
|
public static int Precheck()
|
||
|
|
{
|
||
|
|
var existedET = Process.GetProcessesByName("easytier-core");
|
||
|
|
foreach (var p in existedET)
|
||
|
|
{
|
||
|
|
LogWrapper.Warn("Link", $"发现已有的 EasyTier 实例,可能影响与启动器所用的实例通信: {p.Id}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// 检查文件
|
||
|
|
LogWrapper.Info("Link", "EasyTier 路径: " + ETPath);
|
||
|
|
if (!(File.Exists(ETPath + "\\easytier-core.exe") && File.Exists(ETPath + "\\easytier-cli.exe") &&
|
||
|
|
File.Exists(ETPath + "\\Packet.dll")))
|
||
|
|
{
|
||
|
|
LogWrapper.Error("Link", "EasyTier 不存在或不完整");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static int Launch(bool isHost, string? hostname = null)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (TargetLobby is null || Precheck() != 0)
|
||
|
|
{
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
ETProcess = new Process
|
||
|
|
{
|
||
|
|
EnableRaisingEvents = true,
|
||
|
|
StartInfo = new ProcessStartInfo
|
||
|
|
{
|
||
|
|
FileName = $"{ETPath}\\easytier-core.exe", WorkingDirectory = ETPath,
|
||
|
|
WindowStyle = ProcessWindowStyle.Hidden
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
var arguments = new ArgumentsBuilder();
|
||
|
|
|
||
|
|
// 大厅信息
|
||
|
|
var name = TargetLobby.NetworkName;
|
||
|
|
var secret = TargetLobby.NetworkSecret;
|
||
|
|
|
||
|
|
switch (TargetLobby.Type)
|
||
|
|
{
|
||
|
|
case LobbyType.PCLCE:
|
||
|
|
name = ETNetworkNamePrefix + name;
|
||
|
|
secret = ETNetworkSecretPrefix + secret;
|
||
|
|
break;
|
||
|
|
case LobbyType.Terracotta:
|
||
|
|
name = "terracotta-mc-" + name;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
throw new NotSupportedException(Lang.Text("Link.Lobby.UnsupportedType", TargetLobby.Type));
|
||
|
|
}
|
||
|
|
|
||
|
|
arguments.AddFlag("no-tun");
|
||
|
|
arguments.Add("network-name", name);
|
||
|
|
arguments.Add("network-secret", secret);
|
||
|
|
arguments.Add("relay-network-whitelist", name);
|
||
|
|
arguments.Add("private-mode", "true");
|
||
|
|
// 网络参数
|
||
|
|
if (isHost)
|
||
|
|
{
|
||
|
|
LogWrapper.Info("Link", $"本机作为创建者创建大厅,EasyTier 网络名称: {name}");
|
||
|
|
arguments.Add("i", "10.114.51.41");
|
||
|
|
arguments.Add("tcp-whitelist", TargetLobby.Port.ToString());
|
||
|
|
arguments.Add("udp-whitelist", TargetLobby.Port.ToString());
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
LogWrapper.Info("Link", $"本机作为加入者加入大厅,EasyTier 网络名称: {name}");
|
||
|
|
arguments.AddFlag("d");
|
||
|
|
arguments.Add("tcp-whitelist", "0");
|
||
|
|
arguments.Add("udp-whitelist", "0");
|
||
|
|
|
||
|
|
JoinerLocalPort = NetworkHelper.NewTcpPort();
|
||
|
|
LogWrapper.Info("Link", $"ET 端口转发: 远程 {TargetLobby.Port} -> 本地 {JoinerLocalPort}");
|
||
|
|
arguments.Add("port-forward", $"tcp://127.0.0.1:{JoinerLocalPort}/{TargetLobby.Ip}:{TargetLobby.Port}");
|
||
|
|
arguments.Add("port-forward", $"udp://127.0.0.1:{JoinerLocalPort}/{TargetLobby.Ip}:{TargetLobby.Port}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// 节点设置
|
||
|
|
var relays = ETRelay.RelayList;
|
||
|
|
var customNodes = Config.Link.CustomRelayServer;
|
||
|
|
foreach (var node in customNodes.Split([';'], StringSplitOptions.RemoveEmptyEntries))
|
||
|
|
{
|
||
|
|
if (node.Contains("tcp://") || node.Contains("udp://"))
|
||
|
|
{
|
||
|
|
relays.Add(new ETRelay
|
||
|
|
{
|
||
|
|
Url = node,
|
||
|
|
Name = "Custom",
|
||
|
|
Type = ETRelayType.Custom
|
||
|
|
});
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
LogWrapper.Warn("Link", $"无效的自定义节点 URL: {node}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var relay in
|
||
|
|
from relay in relays
|
||
|
|
let serverType = Config.Link.ServerType
|
||
|
|
where (relay.Type == ETRelayType.Selfhosted && serverType != 2) ||
|
||
|
|
(relay.Type == ETRelayType.Community && serverType == 1) || relay.Type == ETRelayType.Custom
|
||
|
|
select relay)
|
||
|
|
{
|
||
|
|
arguments.Add("p", relay.Url);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 中继行为设置
|
||
|
|
if (Config.Link.RelayType == LinkRelayBehavior.ForceRelay)
|
||
|
|
{
|
||
|
|
arguments.AddFlag("disable-p2p");
|
||
|
|
}
|
||
|
|
|
||
|
|
// 数据流代理设置
|
||
|
|
arguments.AddFlag("enable-quic-proxy");
|
||
|
|
arguments.AddFlag("enable-kcp-proxy");
|
||
|
|
arguments.AddFlag("use-smoltcp");
|
||
|
|
arguments.Add("encryption-algorithm", "chacha20");
|
||
|
|
arguments.Add("default-protocol", Config.Link.ProtocolPreference.ToString().ToLower());
|
||
|
|
arguments.AddFlagIf(!Config.Link.TryPunchSym, "disable-sym-hole-punching");
|
||
|
|
arguments.AddFlagIf(!Config.Link.EnableIPv6, "disable-ipv6");
|
||
|
|
|
||
|
|
// 用户名与其他参数
|
||
|
|
arguments.AddFlagIf(Config.Link.UseLatencyFirstMode, "latency-first");
|
||
|
|
arguments.Add("compression", "zstd");
|
||
|
|
arguments.AddFlag("multi-thread");
|
||
|
|
arguments.Add("machine-id", Identify.LauncherId);
|
||
|
|
|
||
|
|
// TODO: 等待玩家档案迁移以获取正在使用的档案名称
|
||
|
|
var showName = "default";
|
||
|
|
if (AllowCustomName && !string.IsNullOrWhiteSpace(Config.Link.Username))
|
||
|
|
{
|
||
|
|
showName = Config.Link.Username;
|
||
|
|
}
|
||
|
|
else if (!string.IsNullOrWhiteSpace(NaidProfile.Username))
|
||
|
|
{
|
||
|
|
showName = NaidProfile.Username;
|
||
|
|
}
|
||
|
|
|
||
|
|
arguments.Add("hostname",
|
||
|
|
(isHost ? "H|" : "J|") + showName + (!string.IsNullOrWhiteSpace(hostname) ? "|" + hostname : ""));
|
||
|
|
|
||
|
|
// 指定 RPC 端口以避免与其他 ET 实例冲突
|
||
|
|
ETRpcPort = NetworkHelper.NewTcpPort();
|
||
|
|
arguments.Add("rpc-portal", $"127.0.0.1:{ETRpcPort}");
|
||
|
|
|
||
|
|
// 启动
|
||
|
|
ETProcess.StartInfo.Arguments = arguments.GetResult();
|
||
|
|
LogWrapper.Info("Link", "启动 EasyTier");
|
||
|
|
// 操作 UI 显示大厅编号(可能写到 XAML 下面 UI 控制那部分去?)
|
||
|
|
ETProcess.Start();
|
||
|
|
Status = ETState.Running;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
LogWrapper.Error(ex, "Link", "尝试启动 EasyTier 时遇到问题");
|
||
|
|
Status = ETState.Stopped;
|
||
|
|
ETProcess = null;
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void Exit()
|
||
|
|
{
|
||
|
|
if (Status == ETState.Stopped || ETProcess is null) return;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
LogWrapper.Info("Link", $"关闭 EasyTier (PID: {ETProcess.Id})");
|
||
|
|
ETProcess.Kill();
|
||
|
|
ETProcess.WaitForExit(200);
|
||
|
|
}
|
||
|
|
catch (InvalidOperationException)
|
||
|
|
{
|
||
|
|
LogWrapper.Warn("Link", "EasyTier 进程不存在,可能已退出");
|
||
|
|
}
|
||
|
|
catch (NullReferenceException)
|
||
|
|
{
|
||
|
|
LogWrapper.Warn("Link", "EasyTier 进程不存在,可能已退出");
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
LogWrapper.Error(ex, "Link", "关闭 EasyTier 时遇到问题");
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
Status = ETState.Stopped;
|
||
|
|
ETProcess = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|