From 2546b5bf8991a96c03e10dfba9420f04f8293480 Mon Sep 17 00:00:00 2001 From: Megghy Date: Sun, 23 Jan 2022 01:59:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbc=E5=91=BD=E4=BB=A4,=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=8F=AF=E7=94=A8?= =?UTF-8?q?=E6=80=A7=E6=B5=8B=E8=AF=95=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MultiSEngine/Core/Adapter/BaseAdapter.cs | 4 +- MultiSEngine/Core/Adapter/ClientAdapter.cs | 6 +- MultiSEngine/Core/Adapter/TestAdapter.cs | 109 ++++++++++++++++++ .../Core/Adapter/VisualPlayerAdapter.cs | 32 ++--- MultiSEngine/Core/Net.cs | 54 +++++++++ MultiSEngine/DataStruct/ClientData.cs | 23 ++-- .../{MSEPlayer.cs => PlayerInfo.cs} | 2 +- MultiSEngine/DataStruct/ServerInfo.cs | 5 +- MultiSEngine/Localization.cs | 4 +- MultiSEngine/Modules/ClientHelper.cs | 24 ++-- MultiSEngine/Modules/Cmds/ConsoleCommand.cs | 40 +++++-- MultiSEngine/Modules/Cmds/InternalCommand.cs | 5 +- MultiSEngine/Modules/Updater.cs | 2 +- MultiSEngine/MultiSEngine.csproj | 4 +- MultiSEngine/Utils.cs | 10 +- 15 files changed, 255 insertions(+), 69 deletions(-) create mode 100644 MultiSEngine/Core/Adapter/TestAdapter.cs rename MultiSEngine/DataStruct/{MSEPlayer.cs => PlayerInfo.cs} (98%) diff --git a/MultiSEngine/Core/Adapter/BaseAdapter.cs b/MultiSEngine/Core/Adapter/BaseAdapter.cs index 1531e3b..8164eb3 100644 --- a/MultiSEngine/Core/Adapter/BaseAdapter.cs +++ b/MultiSEngine/Core/Adapter/BaseAdapter.cs @@ -20,7 +20,6 @@ public BaseAdapter(ClientData client, Socket connection) { Client = client; Connection = connection; - NetReader = new BinaryReader(new NetworkStream(Connection)); } #region 变量 public int ErrorCount { get; protected set; } = 0; @@ -46,6 +45,7 @@ public BaseAdapter(ClientData client, Socket connection) public abstract void SendPacket(Packet packet); public virtual BaseAdapter Start() { + NetReader = new BinaryReader(new NetworkStream(Connection)); Task.Run(RecieveLoop); Task.Run(ProcessPacketLoop); return this; @@ -56,7 +56,7 @@ public virtual void Stop(bool disposeConnection = false) Logs.Warn($"[{GetType()}] <{Connection?.RemoteEndPoint}> Stopped"); #endif ShouldStop = true; - Client.TimeOutTimer?.Stop(); + Client?.TimeOutTimer?.Stop(); if (disposeConnection) { try { Connection?.Shutdown(SocketShutdown.Both); } catch { } diff --git a/MultiSEngine/Core/Adapter/ClientAdapter.cs b/MultiSEngine/Core/Adapter/ClientAdapter.cs index 925c9cb..d0de349 100644 --- a/MultiSEngine/Core/Adapter/ClientAdapter.cs +++ b/MultiSEngine/Core/Adapter/ClientAdapter.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; +using System.Threading.Tasks; using TrProtocol; using TrProtocol.Packets; @@ -14,8 +15,6 @@ public class ClientAdapter : BaseAdapter { public ClientAdapter(ClientData client, Socket connection) : base(client, connection) { - client.IP = (Connection.RemoteEndPoint as IPEndPoint)?.Address.ToString(); - client.Port = (Connection.RemoteEndPoint as IPEndPoint)?.Port ?? -1; } protected override void OnRecieveLoopError(Exception ex) { @@ -34,7 +33,7 @@ public override bool GetPacket(Packet packet) if (Config.Instance.DefaultServerInternal is { }) { Client.ReadVersion(hello); - Client.Join(Config.Instance.DefaultServerInternal); + Task.Run(() => Client.Join(Config.Instance.DefaultServerInternal)); } else Client.Disconnect("No default server is set for the current server."); @@ -83,7 +82,6 @@ public override bool GetPacket(Packet packet) } public override void SendPacket(Packet packet) { - //bool shouldSerializeLikeClient = packet.GetType().GetProperties().Any(p => p.GetCustomAttributes(true)?.Any(a => a.GetType() == typeof(S2COnlyAttribute)) ?? false); if (!ShouldStop) Client.SendDataToServer(packet); } diff --git a/MultiSEngine/Core/Adapter/TestAdapter.cs b/MultiSEngine/Core/Adapter/TestAdapter.cs new file mode 100644 index 0000000..add36b5 --- /dev/null +++ b/MultiSEngine/Core/Adapter/TestAdapter.cs @@ -0,0 +1,109 @@ +using MultiSEngine.DataStruct; +using MultiSEngine.Modules; +using System; +using System.Net.Sockets; +using System.Threading.Tasks; +using TrProtocol; +using TrProtocol.Packets; + +namespace MultiSEngine.Core.Adapter +{ + internal class TestAdapter : ClientAdapter, IDisposable + { + public TestAdapter(ServerInfo server, bool showDetails) : base(null, new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + this.server = server; + ShowDetails = showDetails; + } + private bool ShowDetails { get; init; } + public int State { get; private set; } = 0; + public bool? IsSuccess { get; private set; } + public ServerInfo server { get; private set; } + private void Log(string msg, bool isDetail = true, ConsoleColor color = ConsoleColor.Blue) + { + if (isDetail && !ShowDetails) + return; + Logs.LogAndSave(msg, $"[TEST] <{server.Name}> {(IsSuccess.HasValue ? ((bool)IsSuccess) ? "SUCCESS" : "FAILED" : "TESTING")}: {State} -", color, false); + } + public override BaseAdapter Start() + { + Task.Run(StartTest); + return this; + } + private async Task StartTest() + { + Log($"Start connecting to [{server.Name}]<{server.IP}:{server.Port}>"); + await Connection.ConnectAsync(server.IP, server.Port); + base.Start(); + State = 1; + Log($"Sending [ConnectRequest] packet"); + InternalSendPacket(new ClientHello() + { + Version = $"Terraria{server.VersionNum}" + }); //发起连接请求 + } + protected override void OnRecieveLoopError(Exception ex) + { + throw ex; + } + public override bool GetPacket(Packet packet) + { + switch (packet) + { + case Kick kick: + var reason = kick.Reason.GetText(); + Stop(true); + IsSuccess = false; + Log($"Kicked. Reason: {(string.IsNullOrEmpty(reason) ? "Unkown" : reason)}", false, ConsoleColor.Red); + break; + case LoadPlayer: + State = 2; + Log($"Sending [PlayerInfo] packet"); + InternalSendPacket(new SyncPlayer() + { + Name = "MultiSEngine" + }); + Log($"Sending [UUID] packet"); + InternalSendPacket(new ClientUUID() + { + UUID = "114514" + }); + Log($"Requesting world data"); + InternalSendPacket(new RequestWorldInfo() { }); + State = 3; + break; + case WorldData: + if (!IsSuccess.HasValue) + { + State = 4; + Log($"Requesting map data"); + InternalSendPacket(new RequestTileData() + { + Position = new(-1, -1) + });//请求物块数据 + Log($"Requesting spawn player"); + InternalSendPacket(new SpawnPlayer() + { + Position = new(-1, -1) + }); + } + break; + case RequestPassword: + IsSuccess = false; + Log($"Target server request password", false, ConsoleColor.Red); + break; + case StartPlaying: + State = 10; + IsSuccess = true; + Log($"Server authentication completed, allow client to start running", true, ConsoleColor.Green); + break; + } + return false; + } + public void Dispose() + { + base.Stop(true); + server = null; + } + } +} diff --git a/MultiSEngine/Core/Adapter/VisualPlayerAdapter.cs b/MultiSEngine/Core/Adapter/VisualPlayerAdapter.cs index b3df05e..c335181 100644 --- a/MultiSEngine/Core/Adapter/VisualPlayerAdapter.cs +++ b/MultiSEngine/Core/Adapter/VisualPlayerAdapter.cs @@ -3,6 +3,7 @@ using MultiSEngine.Modules; using System; using System.Net.Sockets; +using System.Threading.Tasks; using TrProtocol; using TrProtocol.Packets; @@ -10,13 +11,15 @@ namespace MultiSEngine.Core.Adapter { public class VisualPlayerAdapter : ServerAdapter, IStatusChangeable { - public VisualPlayerAdapter(ClientData client, Socket connection) : base(client, connection) + public VisualPlayerAdapter(ClientData client, ServerInfo server) : base(client, new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { + TargetServer = server; } - internal MSEPlayer Player => Client.Player; + + internal PlayerInfo Player => Client.Player; internal bool TestConnecting = false; - internal ServerInfo TempServer; - internal Action Callback; + internal ServerInfo TargetServer; + internal Action SuccessCallback; public bool RunningAsNormal { get; set; } = false; public void ChangeProcessState(bool asNormal) { @@ -34,13 +37,14 @@ public override BaseAdapter Start() /// 如果成功连接的话则调用所给的函数 /// /// - public void TryConnect(ServerInfo server, Action successCallback) + public async Task TryConnect(ServerInfo server, Action successCallback) { if (TestConnecting) return; + await Connection.ConnectAsync(server.IP, server.Port); base.Start(); - TempServer = server; - Callback = successCallback; + TargetServer = server; + SuccessCallback = successCallback; TestConnecting = true; InternalSendPacket(new ClientHello() { @@ -58,7 +62,7 @@ public override bool GetPacket(Packet packet) { case Kick kick: Stop(true); - Client.SendErrorMessage(Localization.Instance["Prompt_Disconnect", (Client.Server ?? TempServer)?.Name, kick.Reason.GetText()]); + Client.SendErrorMessage(Localization.Instance["Prompt_Disconnect", (Client.Server ?? TargetServer)?.Name, kick.Reason.GetText()]); Client.State = ClientData.ClientState.Disconnect; break; case LoadPlayer slot: @@ -84,12 +88,12 @@ public override bool GetPacket(Packet packet) Client.SendInfoMessage($"SSC: {worldData.EventInfo1[6]}"); #endif Player.UpdateData(worldData, false); - if (Callback != null) + if (SuccessCallback != null) { TestConnecting = false; - Callback?.Invoke(Client); - Callback = null; - Client.Server = TempServer; + SuccessCallback?.Invoke(Client); + SuccessCallback = null; + Client.Server = TargetServer; } InternalSendPacket(new RequestTileData() { Position = new(Client.SpawnX, Client.SpawnY) });//请求物块数据 InternalSendPacket(new SpawnPlayer() { Position = new(Client.SpawnX, Client.SpawnY) });//请求物块数据 @@ -102,12 +106,12 @@ public override bool GetPacket(Packet packet) return false; if (Client.State == ClientData.ClientState.RequestPassword) { - Client.SendInfoMessage(Localization.Instance["Prompt_WrongPassword", TempServer.Name, Localization.Get("Help_Password")]); + Client.SendInfoMessage(Localization.Instance["Prompt_WrongPassword", TargetServer.Name, Localization.Get("Help_Password")]); } else { Client.State = ClientData.ClientState.RequestPassword; - Client.SendInfoMessage(Localization.Instance["Prompt_NeedPassword", TempServer.Name, Localization.Get("Help_Password")]); + Client.SendInfoMessage(Localization.Instance["Prompt_NeedPassword", TargetServer.Name, Localization.Get("Help_Password")]); } Client.TimeOutTimer.Stop(); Client.TimeOutTimer.Start(); diff --git a/MultiSEngine/Core/Net.cs b/MultiSEngine/Core/Net.cs index a955256..fca96d3 100644 --- a/MultiSEngine/Core/Net.cs +++ b/MultiSEngine/Core/Net.cs @@ -60,6 +60,60 @@ public static void WatchConnecting() } } } + static bool isTesting = false; + internal static void TestAll(bool showDetails = false) + { + Task.Run(() => + { + Logs.Info($"Ready to start testing all server connectivity"); + int successCount = 0; + Config.Instance.Servers.ForEach(s => + { + if (TestConnect(s, showDetails)) + successCount++; + }); + Logs.Info($"Test completed. Number of available servers:{successCount}/{Config.Instance.Servers.Count}"); + }); + } + internal static bool TestConnect(ServerInfo server, bool showDetails = false) + { + return Task.Run(() => + { + if (isTesting) + { + Logs.Warn($"Now testing other servers, please do so when it finished"); + return false; + } + isTesting = true; + try + { + using var tempConnection = new TestAdapter(server, showDetails); + Logs.Info($"Start testing the connectivity of [{server.Name}]"); + tempConnection.Start(); + long waitTime = 0; + while (Config.Instance.SwitchTimeOut > waitTime) + { + if (tempConnection?.IsSuccess ?? false) + { + isTesting = false; + Logs.Success($"Server [{server.Name}] is in good condition :)"); + return true; + } + else + waitTime += 50; + Task.Delay(50).Wait(); + } + if (!tempConnection.IsSuccess.HasValue) + Logs.LogAndSave($"Test FAILED: Time out", $"[TEST] <{server.Name}>", ConsoleColor.Red, false); + } + catch (Exception ex) + { + Logs.LogAndSave($"Test FAILED: Unable to connect to {server.IP}:{server.Port}{Environment.NewLine}{ex}", $"[TEST] <{server.Name}>", ConsoleColor.Red, false); + } + isTesting = false; + return false; + }).Result; + } } } diff --git a/MultiSEngine/DataStruct/ClientData.cs b/MultiSEngine/DataStruct/ClientData.cs index 70a514b..8547abb 100644 --- a/MultiSEngine/DataStruct/ClientData.cs +++ b/MultiSEngine/DataStruct/ClientData.cs @@ -1,12 +1,13 @@ using MultiSEngine.Core.Adapter; using MultiSEngine.Modules; using System; +using System.Net; using System.Net.Sockets; using System.Timers; namespace MultiSEngine.DataStruct { - public class ClientData : IClientAdapter, IServerAdapter, IDisposable + public class ClientData : IClientAdapter, IServerAdapter { public enum ClientState { @@ -30,14 +31,13 @@ public ClientData() } public FakeWorldAdapter CAdapter { get; set; } public VisualPlayerAdapter SAdapter { get; set; } - internal Socket TempConnection { get; set; } internal VisualPlayerAdapter TempAdapter { get; set; } public Timer TimeOutTimer { get; init; } #region 客户端信息 public ClientState State { get; set; } = ClientState.NewConnection; - public string IP { get; internal set; } - public int Port { get; internal set; } + public string IP => (CAdapter?.Connection?.RemoteEndPoint as IPEndPoint)?.Address?.ToString(); + public int Port => (CAdapter?.Connection?.RemoteEndPoint as IPEndPoint)?.Port ?? -1; public string Address => $"{IP}:{Port}"; public bool Syncing { get; internal set; } = false; public bool Disposed { get; private set; } = false; #endregion @@ -48,7 +48,7 @@ public ClientData() public ServerInfo Server { get; set; } public string Name => Player?.Name ?? Address; public byte Index => Player?.Index ?? 0; - public MSEPlayer Player { get; private set; } = new(); + public PlayerInfo Player { get; private set; } = new(); #endregion #region 方法 @@ -57,18 +57,13 @@ protected void OnTimeOut(object sender, ElapsedEventArgs args) if (State == ClientState.RequestPassword) this.SendErrorMessage(Localization.Instance["Prompt_PasswordTimeout"]); else if (State >= ClientState.Switching && State < ClientState.InGame) - this.SendErrorMessage(Localization.Instance["Prompt_CannotConnect", (TempAdapter as VisualPlayerAdapter)?.TempServer?.Name]); + this.SendErrorMessage(Localization.Instance["Prompt_CannotConnect", (TempAdapter as VisualPlayerAdapter)?.TargetServer?.Name]); State = ClientState.ReadyToSwitch; - if (TempAdapter is VisualPlayerAdapter vpa) - { - Logs.Warn($"[{Name}] timeout when request is switch to: {vpa.TempServer.Name}"); - vpa.Callback = null; - } + Logs.Warn($"[{Name}] timeout when request is switch to: {TempAdapter.TargetServer?.Name}"); - TempConnection?.Shutdown(SocketShutdown.Both); - TempConnection?.Dispose(); - TempConnection = null; + TempAdapter.Stop(true); + TempAdapter = null; } public override string ToString() => $"{Address}:{Name}_{Player.UUID}"; diff --git a/MultiSEngine/DataStruct/MSEPlayer.cs b/MultiSEngine/DataStruct/PlayerInfo.cs similarity index 98% rename from MultiSEngine/DataStruct/MSEPlayer.cs rename to MultiSEngine/DataStruct/PlayerInfo.cs index c9e6741..7bab20e 100644 --- a/MultiSEngine/DataStruct/MSEPlayer.cs +++ b/MultiSEngine/DataStruct/PlayerInfo.cs @@ -3,7 +3,7 @@ namespace MultiSEngine.DataStruct { - public class MSEPlayer + public class PlayerInfo { public class PlayerData { diff --git a/MultiSEngine/DataStruct/ServerInfo.cs b/MultiSEngine/DataStruct/ServerInfo.cs index eb5191e..09d497f 100644 --- a/MultiSEngine/DataStruct/ServerInfo.cs +++ b/MultiSEngine/DataStruct/ServerInfo.cs @@ -4,8 +4,9 @@ public class ServerInfo { public bool Visible { get; set; } = true; public string Name { get; set; } - public string IP { get; set; } - public int Port { get; set; } + public string ShortName { get; set; } = ""; + public string IP { get; set; } = "127.0.0.1"; + public int Port { get; set; } = 7777; public short SpawnX { get; set; } = -1; public short SpawnY { get; set; } = -1; public int VersionNum { get; set; } = -1; diff --git a/MultiSEngine/Localization.cs b/MultiSEngine/Localization.cs index 003d00b..44a4ea2 100644 --- a/MultiSEngine/Localization.cs +++ b/MultiSEngine/Localization.cs @@ -29,10 +29,8 @@ public static string Get(string key, object[] obj = null) { return obj is null ? Instance.JsonData?.RootElement.GetProperty(key).GetString() : string.Format(Instance.JsonData?.RootElement.GetProperty(key).GetString(), obj); } - catch (JsonException) { return key; } - catch (Exception ex) + catch { - Logs.Error(ex); return key; } } diff --git a/MultiSEngine/Modules/ClientHelper.cs b/MultiSEngine/Modules/ClientHelper.cs index 53b28eb..52d523d 100644 --- a/MultiSEngine/Modules/ClientHelper.cs +++ b/MultiSEngine/Modules/ClientHelper.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net.Sockets; +using System.Threading.Tasks; using TrProtocol; using TrProtocol.Models; using TrProtocol.Packets; @@ -20,7 +21,7 @@ public static partial class ClientHelper /// /// /// - public static async void Join(this ClientData client, ServerInfo server) + public static async Task Join(this ClientData client, ServerInfo server) { if (Core.Hooks.OnPreSwitch(client, server, out _)) return; @@ -41,28 +42,25 @@ public static async void Join(this ClientData client, ServerInfo server) client.State = ClientData.ClientState.Switching; client.TimeOutTimer.Start(); - client.TempConnection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - await client.TempConnection.ConnectAsync(ip, server.Port); //新建与服务器的连接 + client.TempAdapter = new(client, server);//新建与服务器的连接 - client.CAdapter.ChangeProcessState(true); //切换至正常的客户端处理 - - var tempAdapter = new VisualPlayerAdapter(client, client.TempConnection); - client.TempAdapter = tempAdapter; - tempAdapter.TryConnect(server, (client) => + await client.TempAdapter.TryConnect(server, (client) => { client.State = ClientData.ClientState.InGame; client.SAdapter?.Stop(true); client.SAdapter = client.TempAdapter; - client.TempConnection = null; + client.CAdapter.ChangeProcessState(true); //切换至正常的客户端处理 client.TempAdapter = null; client.TimeOutTimer.Stop(); client.Sync(server); }); } - catch + catch(Exception ex) { client.State = ClientData.ClientState.ReadyToSwitch; - Logs.Error($"Unable to connect to server {server.IP}:{server.Port}"); + if (ex is SocketException se && se.SocketErrorCode == SocketError.OperationAborted) + return; + Logs.Error($"Unable to connect to server {server.IP}:{server.Port}{Environment.NewLine}{ex}"); client.SendErrorMessage(Localization.Instance["Prompt_CannotConnect", server.Name]); } } @@ -83,9 +81,9 @@ public static void Back(this ClientData client) client.CAdapter?.BackToThere(); } else if (client.Server is null) - client.SendErrorMessage(Localization.Instance["Prompt_CannotConnect", client.TempAdapter?.TempServer?.Name]); + client.SendErrorMessage(Localization.Instance["Prompt_CannotConnect", client.TempAdapter?.TargetServer?.Name]); else - client.Join(Config.Instance.DefaultServerInternal); + Task.Run(() => client.Join(Config.Instance.DefaultServerInternal)); } public static void Sync(this ClientData client, ServerInfo targetServer) { diff --git a/MultiSEngine/Modules/Cmds/ConsoleCommand.cs b/MultiSEngine/Modules/Cmds/ConsoleCommand.cs index c0ab66d..d07021e 100644 --- a/MultiSEngine/Modules/Cmds/ConsoleCommand.cs +++ b/MultiSEngine/Modules/Cmds/ConsoleCommand.cs @@ -1,6 +1,7 @@ using MultiSEngine.DataStruct; using System; using System.Linq; +using System.Threading.Tasks; namespace MultiSEngine.Modules.Cmds { @@ -13,15 +14,18 @@ public override bool Execute(ClientData client, string cmdName, string[] parma) var internalCommand = Data.Commands.FirstOrDefault(c => c.Name == "mse"); switch (cmdName) { + case "l": case "list": Logs.Info($"{Localization.Instance["Command_AviliableServer"]}{Environment.NewLine + "- "}{string.Join(Environment.NewLine + "- ", from server in Config.Instance.Servers let text = $"{server.Name} <{server.Online().Length}>" select text)}", false); break; + case "ol": case "online": Logs.Info($"{Data.Clients.Count} Player(s) Online:{Environment.NewLine}{string.Join(", ", from c in Data.Clients let text = $"{c.Name} <{c.Server?.Name ?? "FakeWorld"}>" select text)}", false); break; case "stop": case "exit": return false; + case "k": case "kick": if (parma.Any()) { @@ -33,6 +37,7 @@ public override bool Execute(ClientData client, string cmdName, string[] parma) else Logs.Error(Localization.Instance["Prompt_InvalidFormat"]); break; + case "rl": case "reload": Config._instance = null; Localization._instance = null; @@ -46,24 +51,41 @@ public override bool Execute(ClientData client, string cmdName, string[] parma) case "bc": if (parma.Length > 1) { - if (Utils.GetServerInfoByName(parma[1]).FirstOrDefault() is { } server) + if (Utils.GetServersInfoByName(parma[1]).FirstOrDefault() is { } server) Data.Clients.Where(c => c.Server == server).ForEach(c => c.SendMessage($"[Broadcast] {parma[0]}", false)); else Logs.Error(string.Format(Localization.Get("Command_ServerNotFound"), parma[1])); } else - ClientHelper.Broadcast(null, parma.FirstOrDefault()); + ClientHelper.Broadcast(null, $"[Broadcast] {parma.FirstOrDefault()}"); + Logs.Info($"Broadcast: {(parma.Length > 1 ? parma[1] : parma[0])}"); + break; + case "t": + case "test": + if (parma.Any()) + { + bool showDetail = parma.Length > 1 && parma[1].ToLower() == "-detail"; + if(parma[0].ToLower() == "all") + Core.Net.TestAll(showDetail); + else if (Utils.GetSingleServerInfoByName(parma[0]) is { } testServer) + Task.Run(() => Core.Net.TestConnect(testServer, showDetail)); + else + Logs.Error($"The server named [{parma[0]}] was not found"); + } + else + Logs.Error(Localization.Instance["Prompt_InvalidFormat"]); break; case "help": default: Logs.Info($"Avaliable console commands:{Environment.NewLine}" + - $"- stop(exit){Environment.NewLine}" + - $"- kick (reason){Environment.NewLine}" + - $"- broadcase(bc) (target server){Environment.NewLine}" + - $"- list{Environment.NewLine}" + - $"- online{Environment.NewLine}" + - $"- reload{Environment.NewLine}" + - $"- reloadplugin(rp){Environment.NewLine}", false); + $"- stop(exit) -- Exit MultiSEngine.{Environment.NewLine}" + + $"- kick(k) (reason) -- Kick out the specified player.{Environment.NewLine}" + + $"- broadcase(bc) (target server) -- Send a message to all players.{Environment.NewLine}" + + $"- list(l) -- List all servers in the config.{Environment.NewLine}" + + $"- online(ol) -- List all online players.{Environment.NewLine}" + + $"- test(t) / (-detail) -- Test if the server can connect. If you add the detail parameter at the end, it will show the details of the connection{Environment.NewLine}" + + $"- reload(rl) -- Overloading most of the config file content.{Environment.NewLine}" + + $"- reloadplugin(rp) -- Reload Plugin.{Environment.NewLine}", false); break; } Data.Commands.FirstOrDefault(c => c.Name == "mce")?.Execute(client, cmdName, parma); diff --git a/MultiSEngine/Modules/Cmds/InternalCommand.cs b/MultiSEngine/Modules/Cmds/InternalCommand.cs index bb45684..092a591 100644 --- a/MultiSEngine/Modules/Cmds/InternalCommand.cs +++ b/MultiSEngine/Modules/Cmds/InternalCommand.cs @@ -1,6 +1,7 @@ using MultiSEngine.DataStruct; using System; using System.Linq; +using System.Threading.Tasks; namespace MultiSEngine.Modules.Cmds { @@ -34,7 +35,7 @@ public override bool Execute(ClientData client, string cmdName, string[] parma) break; case "list": case "l": - client.SendSuccessMessage($"{Localization.Get("Command_AviliableServer")}{Environment.NewLine + "- "}{string.Join(Environment.NewLine + "- ", (from server in Config.Instance.Servers let text = $"{server.Name} <{server.Online().Length}>" select text))}"); + client.SendSuccessMessage($"{Localization.Get("Command_AviliableServer")}{Environment.NewLine + "- "}{string.Join(Environment.NewLine + "- ", (from server in Config.Instance.Servers let text = $"{server.Name} {(string.IsNullOrEmpty(server.ShortName) ? "" : $"[{server.ShortName}]")} <{server.Online().Length}>" select text))}"); break; case "password": case "pass": @@ -85,7 +86,7 @@ private static void SwitchServer(ClientData client, string serverName) client.SendErrorMessage(Localization.Get("Command_IsSwitching")); return; } - if (Utils.GetServerInfoByName(serverName).FirstOrDefault() is { } server) + if (Utils.GetServersInfoByName(serverName).FirstOrDefault() is { } server) { if (client.Server == server) client.SendErrorMessage(string.Format(Localization.Get("Command_AlreadyIn"), server.Name)); diff --git a/MultiSEngine/Modules/Updater.cs b/MultiSEngine/Modules/Updater.cs index 10af26f..880a103 100644 --- a/MultiSEngine/Modules/Updater.cs +++ b/MultiSEngine/Modules/Updater.cs @@ -32,7 +32,7 @@ private static async void CheckUpdate(object sender, ElapsedEventArgs e) { var version = await GetNewestVersion(); if (version > Assembly.GetExecutingAssembly().GetName().Version) - Logs.LogAndSave($"New version found: {version}, please go to [https://github.com/Megghy/MultiSEngine/releases] to download.", "[Updater]", ConsoleColor.DarkYellow); + Logs.LogAndSave($"New version found: {version}, please download at [https://github.com/Megghy/MultiSEngine/releases] or [https://github.com/Megghy/MultiSEngine/actions].", "[Updater]", ConsoleColor.DarkYellow); } catch { } } diff --git a/MultiSEngine/MultiSEngine.csproj b/MultiSEngine/MultiSEngine.csproj index d6ffab3..6028c8d 100644 --- a/MultiSEngine/MultiSEngine.csproj +++ b/MultiSEngine/MultiSEngine.csproj @@ -4,8 +4,8 @@ preview Exe net6.0 - 1.0.5.3 - 1.0.5.3 + 1.0.5.4 + 1.0.5.4 True latest true diff --git a/MultiSEngine/Utils.cs b/MultiSEngine/Utils.cs index 4c058c5..0248f1e 100644 --- a/MultiSEngine/Utils.cs +++ b/MultiSEngine/Utils.cs @@ -38,9 +38,15 @@ public static bool TryParseAddress(string address, out string ip) catch { } return false; } - public static ServerInfo[] GetServerInfoByName(string name) + public static ServerInfo[] GetServersInfoByName(string name) { - return Config.Instance.Servers.Where(s => s.Name.ToLower().StartsWith(name.ToLower()) || s.Name.ToLower().Contains(name.ToLower())).ToArray(); + return Config.Instance.Servers.Where(s => s.Name.ToLower().StartsWith(name.ToLower()) || s.Name.ToLower().Contains(name.ToLower()) || s.ShortName == name).ToArray(); + } + public static ServerInfo GetSingleServerInfoByName(string name) + { + if (GetServersInfoByName(name) is { } temp && temp.Any()) + return temp.First(); + return null; } public static void ForEach(this IEnumerable source, Action action) {