Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace PCL.Core.Link;

public class Broadcast(string description, int localPort) : IDisposable
public class BroadcastLocal(string description, int localPort) : IDisposable
{
private Socket? _broadcastSocket;
private CancellationTokenSource? _cts;
Expand Down Expand Up @@ -80,6 +80,7 @@ public void Dispose()
{
Stop();
_cts?.Dispose();
_broadcastSocket.SafeClose();
GC.SuppressFinalize(this);
}
}
5 changes: 3 additions & 2 deletions PCL.Core/Link/Lobby/LobbyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using static PCL.Core.Link.Lobby.LobbyInfoProvider;
using static PCL.Core.Link.Natayark.NatayarkProfileManager;
using LobbyType = PCL.Core.Link.Scaffolding.Client.Models.LobbyType;
using PCL.Core.Link.McPing;

namespace PCL.Core.Link.Lobby;

Expand Down Expand Up @@ -88,7 +89,7 @@ public sealed class LobbyController

var tcpPortForForward = NetworkHelper.NewTcpPort();
McForward = new TcpForward(IPAddress.Loopback, tcpPortForForward, IPAddress.Loopback, localPort);
McBroadcast = new Broadcast($"§ePCL CE 大厅{desc}", tcpPortForForward);
McBroadcast = new BroadcastLocal($"§ePCL CE 大厅{desc}", tcpPortForForward);
McForward.Start();
McBroadcast.Start();

Expand Down Expand Up @@ -159,7 +160,7 @@ public sealed class LobbyController
/// </summary>
public static async Task<bool> IsHostInstanceAvailableAsync(int port)
{
var ping = new McPing("127.0.0.1", port);
using var ping = McPingServiceFactory.CreateService("127.0.0.1", port);
var info = await ping.PingAsync().ConfigureAwait(false);

if (info != null) return true;
Expand Down
2 changes: 1 addition & 1 deletion PCL.Core/Link/Lobby/LobbyInfoProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static class LobbyInfoProvider
public static bool RequiresRealName { get; set; } = true;
public static int ProtocolVersion { get; set; } = 6;

public static Broadcast? McBroadcast { get; internal set; }
public static BroadcastLocal? McBroadcast { get; internal set; }
public static TcpForward? McForward { get; internal set; }

public class LobbyInfo
Expand Down
3 changes: 2 additions & 1 deletion PCL.Core/Link/Lobby/LobbyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System.Windows;
using PCL.Core.App.IoC;
using PCL.Core.UI;
using PCL.Core.Link.McPing;

namespace PCL.Core.Link.Lobby;

Expand Down Expand Up @@ -204,7 +205,7 @@ public static async Task DiscoverWorldAsync()
{
if (!recordedPorts.TryAdd(info.Address.Port)) return;

using var pinger = new McPing(new IPEndPoint(IPAddress.Loopback, info.Address.Port));
using var pinger = McPingServiceFactory.CreateService(new IPEndPoint(IPAddress.Loopback, info.Address.Port));
using var cts = new CancellationTokenSource(2000);

try
Expand Down
35 changes: 35 additions & 0 deletions PCL.Core/Link/McPing/IMcPingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using PCL.Core.Link.McPing.Model;
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace PCL.Core.Link.McPing;

/// <summary>
/// Minecraft服务器探测服务接口
/// </summary>
public interface IMcPingService : IDisposable
{
/// <summary>
/// 异步探测Minecraft服务器信息
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>服务器探测结果,如果探测失败则返回null</returns>
Task<McPingResult?> PingAsync(CancellationToken cancellationToken = default);

/// <summary>
/// 获取服务端点信息
/// </summary>
IPEndPoint Endpoint { get; }

/// <summary>
/// 获取主机地址
/// </summary>
string Host { get; }

/// <summary>
/// 获取超时时间(毫秒)
/// </summary>
int Timeout { get; }
}
113 changes: 113 additions & 0 deletions PCL.Core/Link/McPing/LegacyMcPingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using PCL.Core.Link.McPing.Model;
using PCL.Core.Logging;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PCL.Core.Link.McPing;

/// <summary>
/// 旧版Minecraft协议服务器探测服务实现
/// 支持1.6及以下版本的服务器信息查询协议
/// </summary>
public class LegacyMcPingService : IMcPingService
{
private readonly IPEndPoint _endpoint;
private readonly string _host;
private const int DefaultTimeout = 10000;
private readonly int _timeout;
private bool _disposed;

public IPEndPoint Endpoint => _endpoint;
public string Host => _host;
public int Timeout => _timeout;

public LegacyMcPingService(IPEndPoint endpoint, int timeout = DefaultTimeout)
{
_endpoint = endpoint;
_host = _endpoint.Address.ToString();
_timeout = timeout;
}

public LegacyMcPingService(string ip, int port = 25565, int timeout = DefaultTimeout)
{
_endpoint = IPAddress.TryParse(ip, out var ipAddress)
? new IPEndPoint(ipAddress, port)
: new IPEndPoint(Dns.GetHostAddresses(ip).First(), port);
_host = ip;
_timeout = timeout;
}

/// <summary>
/// 执行旧版Minecraft协议的服务器探测
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<McPingResult?> PingAsync(CancellationToken cancellationToken = default)
{
// TODO: 实现旧版协议的探测逻辑
// 这里需要迁移原来McPing类中的PingOldAsync方法逻辑

using var so = new Socket(SocketType.Stream, ProtocolType.Tcp);
using var cts = new CancellationTokenSource();
cts.CancelAfter(_timeout);
cts.Token.Register(() =>
{
try
{
if (so.Connected) so.Close();
}
catch (ObjectDisposedException)
{
/* Ignore */
}
});

await so.ConnectAsync(_endpoint, cts.Token);
LogWrapper.Debug("LegacyMcPing", $"Connected to {_endpoint}");
await using var stream = new NetworkStream(so, false);

var queryPack = new byte[] { 0xfe, 0x01 };
await stream.WriteAsync(queryPack.AsMemory(0, queryPack.Length), cts.Token);
var ms = new MemoryStream();
await stream.CopyToAsync(ms, cts.Token);
so.Close();
var retData = ms.ToArray();
if (retData.Length < 21 || (retData.Length >= 21 && retData[0] != 0xff))
{
LogWrapper.Info("McPing", $"Unknown response from {_endpoint}, ignore");
return null;
}

var retRep = Encoding.UTF8.GetString(retData);
try
{
var retPart = retRep.Split(["\0\0\0"], StringSplitOptions.None);
retPart = retPart
.Select(s => new string([.. s.Where((_, index) => index % 2 == 0)]))
.ToArray();
if (retPart.Length < 6)
return null;
return new McPingResult(new McPingVersionResult(retPart[2], int.Parse(retPart[1])),
new McPingPlayerResult(int.Parse(retPart[5]), int.Parse(retPart[4]), []), retPart[3], string.Empty, 0,
new McPingModInfoResult(string.Empty, []), null);
}
catch (Exception e)
{
LogWrapper.Error(e, "McPing", $"Unable to serialize response from {_endpoint}");
return null;
}
}

public void Dispose()
{
if (_disposed) return;
_disposed = true;
GC.SuppressFinalize(this);
}
}
Loading