Files
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

114 lines
3.8 KiB
C#

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 timeoutCts = new CancellationTokenSource(_timeout);
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
linkedCts.Token.Register(() =>
{
try
{
if (so.Connected) so.Close();
}
catch (ObjectDisposedException)
{
/* Ignore */
}
});
await so.ConnectAsync(_endpoint, linkedCts.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), linkedCts.Token);
var ms = new MemoryStream();
await stream.CopyToAsync(ms, linkedCts.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);
}
}