Fixed IdCollision HandshakeResponse will not send

Added ConnectToHost auto try NetID feature
This commit is contained in:
kmyuhkyuk
2026-03-08 23:27:41 +08:00
parent b4403b4d16
commit ac1c274b02
3 changed files with 115 additions and 20 deletions
@@ -22,27 +22,9 @@ namespace SlayTheSpire2.LAN.Multiplayer.Components
public bool IsIPAndPort => IPAndPortRegex().IsMatch(Text);
public string? IPAddress
{
get
{
if (IsOnlyIP)
return Text;
public string? IPAddress => IsOnlyIP ? Text : IsIPAndPort ? Text.Split(':').First() : null;
return IsIPAndPort ? Text.Split(':').First() : null;
}
}
public ushort? Port
{
get
{
if (IsIPAndPort)
return Convert.ToUInt16(Text.Split(':').Last());
return null;
}
}
public ushort? Port => IsIPAndPort ? Convert.ToUInt16(Text.Split(':').Last()) : null;
public override void _Ready()
{
@@ -0,0 +1,73 @@
using HarmonyLib;
using MegaCrit.Sts2.Core.Entities.Multiplayer;
using MegaCrit.Sts2.Core.Helpers;
using MegaCrit.Sts2.Core.Logging;
using MegaCrit.Sts2.Core.Multiplayer;
using MegaCrit.Sts2.Core.Multiplayer.Connection;
using MegaCrit.Sts2.Core.Multiplayer.Transport.ENet;
using MegaCrit.Sts2.Core.Platform;
// ReSharper disable UnusedMember.Global
// ReSharper disable UnusedType.Global
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
{
[HarmonyPatch(typeof(ENetClientConnectionInitializer), "Connect")]
internal class ENetClientConnectionInitializerPatch
{
private static bool Prefix(NetClientGameService gameService,
CancellationToken cancelToken, ulong ____netId, string ____ip, ushort ____port, ref Task __result)
{
__result = TaskHelper.RunSafely(Connect(gameService, cancelToken, ____netId, ____ip, ____port));
return false;
}
private static async Task<NetErrorInfo?> Connect(NetClientGameService gameService,
CancellationToken cancelToken, ulong netId, string ip, ushort port)
{
if (gameService.IsConnected)
{
throw new InvalidOperationException(
"NetClientGameService must not be connected when passed to ENetClientConnectionInitializer!");
}
var eNetClient = new ENetClient(gameService);
gameService.Initialize(eNetClient, PlatformType.None);
var count = 0;
const int tryCount = 10;
NetErrorInfo? netErrorInfo = null;
while (count < tryCount)
{
if (cancelToken.IsCancellationRequested)
return netErrorInfo;
netErrorInfo = await eNetClient.ConnectToHost(netId, ip, port, cancelToken);
if (!netErrorInfo.HasValue)
{
Log.Info($"Connect {ip}:{port} Host Game NetID:{netId}");
return null;
}
if (netErrorInfo.Value.GetReason() != NetError.Kicked)
return netErrorInfo;
var nextNetId = netId + 1000u;
if (count < tryCount - 1)
{
Log.Warn($"{ip}:{port} Host Game NetID:{netId} already occupied, next try NetID:{nextNetId}");
}
netId = nextNetId;
count++;
}
return netErrorInfo;
}
}
}
@@ -0,0 +1,40 @@
using System.Reflection;
using System.Reflection.Emit;
using Godot;
using HarmonyLib;
using MegaCrit.Sts2.Core.Multiplayer.Transport.ENet;
// ReSharper disable UnusedMember.Global
// ReSharper disable UnusedType.Global
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
{
[HarmonyPatch]
internal class ENetHostDoClientHandshakePatch
{
private static MethodInfo TargetMethod()
{
return AccessTools.AsyncMoveNext(typeof(ENetHost).GetMethod("DoClientHandshake",
BindingFlags.Instance | BindingFlags.NonPublic));
}
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//Fixed IdCollision HandshakeResponse will not send
var peerDisconnectMethod = AccessTools.Method(typeof(ENetPacketPeer), "PeerDisconnect");
var peerDisconnectLaterMethod = AccessTools.Method(typeof(ENetPacketPeer), "PeerDisconnectLater");
foreach (var instruction in instructions)
{
if (instruction.opcode == OpCodes.Callvirt && instruction.operand as MethodInfo == peerDisconnectMethod)
{
yield return new CodeInstruction(OpCodes.Callvirt, peerDisconnectLaterMethod);
continue;
}
yield return instruction;
}
}
}
}