Supports url address and ipv6 connecting

This commit is contained in:
kmyuhkyuk
2026-03-11 01:38:50 +08:00
parent e52fbfdefc
commit ff0c021a12
6 changed files with 115 additions and 80 deletions
@@ -0,0 +1,69 @@
using System.Net;
using Godot;
using MegaCrit.Sts2.Core.Nodes.GodotExtensions;
namespace SlayTheSpire2.LAN.Multiplayer.Components
{
internal class AddressLineEdit : NMegaLineEdit
{
public override void _Ready()
{
TextSubmitted += OnTextSubmitted;
}
public struct AddressInfo(bool isValid, string? address, ushort? port)
{
public bool IsValid = isValid;
public string? Address = address;
public ushort? Port = port;
}
public AddressInfo GetAddressInfo()
{
var addressInfo = new AddressInfo();
var uriString = Text;
if (uriString == "localhost")
{
uriString = $"http://{uriString}";
}
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri) && uri.Scheme is "http" or "https")
{
addressInfo.IsValid = true;
addressInfo.Address = uri.Host;
if (uri is { Port: > -1, IsDefaultPort: false })
{
addressInfo.Port = (ushort)uri.Port;
}
return addressInfo;
}
if (IPAddress.TryParse(Text, out _))
{
addressInfo.IsValid = true;
addressInfo.Address = Text;
}
else if (IPEndPoint.TryParse(Text, out var ipEndPoint))
{
addressInfo.IsValid = true;
addressInfo.Port = (ushort)ipEndPoint.Port;
}
return addressInfo;
}
private void OnTextSubmitted(string newText)
{
Modulate = Colors.White;
if (GetAddressInfo().IsValid)
return;
Modulate = Colors.Red;
}
}
}
@@ -1,61 +0,0 @@
using System.Text.RegularExpressions;
using Godot;
using MegaCrit.Sts2.Core.Nodes.GodotExtensions;
namespace SlayTheSpire2.LAN.Multiplayer.Components
{
internal partial class IPAddressLineEdit : NMegaLineEdit
{
[GeneratedRegex(@"^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$")]
private static partial Regex IPRegex();
[GeneratedRegex(
@"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d):((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([1-9][0-9]{0,3})|[0-9])$")]
private static partial Regex IPAndPortRegex();
[GeneratedRegex("^[0-9.:]*$")]
private static partial Regex IPAllowRegex();
private string _oldText = "";
public bool IsOnlyIP => IPRegex().IsMatch(Text);
public bool IsIPAndPort => IPAndPortRegex().IsMatch(Text);
public string? IPAddress => IsOnlyIP ? Text : IsIPAndPort ? Text.Split(':').First() : null;
public ushort? Port => IsIPAndPort ? Convert.ToUInt16(Text.Split(':').Last()) : null;
public override void _Ready()
{
TextChanged += OnTextChanged;
TextSubmitted += OnTextSubmitted;
}
private void OnTextChanged(string newText)
{
if (IPAllowRegex().IsMatch(newText))
{
_oldText = newText;
}
else
{
Text = _oldText;
CaretColumn = Text.Length;
}
}
private void OnTextSubmitted(string newText)
{
Modulate = Colors.White;
if (IPRegex().IsMatch(newText))
return;
if (IPAndPortRegex().IsMatch(newText))
return;
Modulate = Colors.Red;
}
}
}
@@ -24,7 +24,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
return false; return false;
} }
private static async Task<NetErrorInfo?> ConnectToHost(ENetClient eNetClient, Logger logger, INetClientHandler handler, ulong netId, string ip, ushort port, CancellationToken cancelToken) private static async Task<NetErrorInfo?> ConnectToHost(ENetClient eNetClient, Logger logger,
INetClientHandler handler, ulong netId, string ip, ushort port, CancellationToken cancelToken)
{ {
while (true) while (true)
{ {
@@ -34,7 +35,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
var peer = connection.ConnectToHost(ip, port); var peer = connection.ConnectToHost(ip, port);
Traverse.Create(eNetClient).Field("_peer").SetValue(peer); Traverse.Create(eNetClient).Field("_peer").SetValue(peer);
var timeoutTimer = 0; var timeoutTimer = 0;
while (!connection.TryService(out var output) || output is not { type: ENetConnection.EventType.Connect }) while (!connection.TryService(out var output) ||
output is not { type: ENetConnection.EventType.Connect })
{ {
await Task.Delay(100, cancelToken); await Task.Delay(100, cancelToken);
if (cancelToken.IsCancellationRequested) if (cancelToken.IsCancellationRequested)
@@ -60,7 +62,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
} }
var bufferedPackets = new List<ENetServiceData>(); var bufferedPackets = new List<ENetServiceData>();
var (result, newNetId) = await SendAndWaitForNetIdAck(eNetClient, logger, peer, connection, netId, bufferedPackets, cancelToken); var (result, newNetId) = await SendAndWaitForNetIdAck(eNetClient, logger, peer, connection, netId,
bufferedPackets, cancelToken);
if (result.HasValue) if (result.HasValue)
{ {
peer.PeerDisconnect(); peer.PeerDisconnect();
@@ -1,6 +1,7 @@
using System.Collections; using System.Collections;
using Godot; using Godot;
using HarmonyLib; using HarmonyLib;
using MegaCrit.Sts2.Core.Entities.Multiplayer;
using MegaCrit.Sts2.Core.Helpers; using MegaCrit.Sts2.Core.Helpers;
using MegaCrit.Sts2.Core.Multiplayer.Transport; using MegaCrit.Sts2.Core.Multiplayer.Transport;
using MegaCrit.Sts2.Core.Multiplayer.Transport.ENet; using MegaCrit.Sts2.Core.Multiplayer.Transport.ENet;
@@ -97,4 +98,25 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
} }
} }
} }
[HarmonyPatch(typeof(ENetHost), "StartHost")]
internal class ENetHostStartHostPatch
{
private static bool Prefix(ushort port, int maxClients, Logger ____logger, ref ENetConnection? ____connection,
ref bool ____isConnected, ref NetErrorInfo? __result)
{
____connection = new ENetConnection();
var error = ____connection.CreateHostBound("*", port, maxClients);
if (error != Error.Ok)
{
____logger.Error($"Failed to create host! {error}");
__result = new NetErrorInfo(error);
return false;
}
____isConnected = true;
return false;
}
}
} }
@@ -62,14 +62,14 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
ipAddressLabel.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter; ipAddressLabel.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter;
} }
var ipAddressLineEdit = new IPAddressLineEdit { Name = "IPInput" }; var addressLineEdit = new AddressLineEdit { Name = "AddressInput" };
vBoxContainer.AddChild(ipAddressLineEdit); vBoxContainer.AddChild(addressLineEdit);
ipAddressLineEdit.Text = SettingsHelper.Instance.SettingsModel.IPAddress; addressLineEdit.Text = SettingsHelper.Instance.SettingsModel.IPAddress;
ipAddressLineEdit.Alignment = HorizontalAlignment.Center; addressLineEdit.Alignment = HorizontalAlignment.Center;
ipAddressLineEdit.CustomMinimumSize = new Vector2(300, 50); addressLineEdit.CustomMinimumSize = new Vector2(300, 50);
ipAddressLineEdit.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter; addressLineEdit.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter;
if (__instance.GetNode<NJoinFriendRefreshButton>("RefreshButton").Duplicate() is NJoinFriendRefreshButton if (__instance.GetNode<NJoinFriendRefreshButton>("RefreshButton").Duplicate() is NJoinFriendRefreshButton
joinButton) joinButton)
@@ -83,28 +83,27 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
joinButton.Connect(NClickableControl.SignalName.Released, Callable.From<NClickableControl>(_ => joinButton.Connect(NClickableControl.SignalName.Released, Callable.From<NClickableControl>(_ =>
{ {
if (!ipAddressLineEdit.IsOnlyIP && var addressInfo = addressLineEdit.GetAddressInfo();
(!ipAddressLineEdit.IsIPAndPort || !ipAddressLineEdit.Port.HasValue))
if (!addressInfo.IsValid)
return; return;
SettingsHelper.Instance.SettingsModel.IPAddress = ipAddressLineEdit.Text; SettingsHelper.Instance.SettingsModel.IPAddress = addressLineEdit.Text;
SettingsHelper.Instance.WriteSettings(); SettingsHelper.Instance.WriteSettings();
var ipAddress = ipAddressLineEdit.IPAddress;
ushort port = 33771; ushort port = 33771;
if (ipAddressLineEdit.Port.HasValue) if (addressInfo.Port.HasValue)
{ {
port = ipAddressLineEdit.Port.Value; port = addressInfo.Port.Value;
} }
var netId = SettingsHelper.Instance.SettingsModel.NetId;
DisplayServer.WindowSetTitle("Slay The Spire 2 (Client)"); DisplayServer.WindowSetTitle("Slay The Spire 2 (Client)");
if (ipAddress != null) if (addressInfo.Address != null)
{ {
TaskHelper.RunSafely( TaskHelper.RunSafely(
__instance.JoinGameAsync(new ENetClientConnectionInitializer(netId, ipAddress, port))); __instance.JoinGameAsync(new ENetClientConnectionInitializer(
SettingsHelper.Instance.SettingsModel.NetId, addressInfo.Address, port)));
} }
})); }));
@@ -15,6 +15,9 @@
<Reference Include="GodotSharp"> <Reference Include="GodotSharp">
<HintPath>V:\Slay.the.Spire.2\game\data_sts2_windows_x86_64\GodotSharp.dll</HintPath> <HintPath>V:\Slay.the.Spire.2\game\data_sts2_windows_x86_64\GodotSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="Steamworks.NET">
<HintPath>V:\Slay.the.Spire.2\game\data_sts2_windows_x86_64\Steamworks.NET.dll</HintPath>
</Reference>
<Reference Include="sts2"> <Reference Include="sts2">
<HintPath>V:\Slay.the.Spire.2\game\data_sts2_windows_x86_64\sts2.dll</HintPath> <HintPath>V:\Slay.the.Spire.2\game\data_sts2_windows_x86_64\sts2.dll</HintPath>
</Reference> </Reference>