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;
}
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)
{
@@ -34,7 +35,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
var peer = connection.ConnectToHost(ip, port);
Traverse.Create(eNetClient).Field("_peer").SetValue(peer);
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);
if (cancelToken.IsCancellationRequested)
@@ -60,7 +62,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
}
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)
{
peer.PeerDisconnect();
@@ -1,6 +1,7 @@
using System.Collections;
using Godot;
using HarmonyLib;
using MegaCrit.Sts2.Core.Entities.Multiplayer;
using MegaCrit.Sts2.Core.Helpers;
using MegaCrit.Sts2.Core.Multiplayer.Transport;
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;
}
var ipAddressLineEdit = new IPAddressLineEdit { Name = "IPInput" };
var addressLineEdit = new AddressLineEdit { Name = "AddressInput" };
vBoxContainer.AddChild(ipAddressLineEdit);
vBoxContainer.AddChild(addressLineEdit);
ipAddressLineEdit.Text = SettingsHelper.Instance.SettingsModel.IPAddress;
ipAddressLineEdit.Alignment = HorizontalAlignment.Center;
ipAddressLineEdit.CustomMinimumSize = new Vector2(300, 50);
ipAddressLineEdit.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter;
addressLineEdit.Text = SettingsHelper.Instance.SettingsModel.IPAddress;
addressLineEdit.Alignment = HorizontalAlignment.Center;
addressLineEdit.CustomMinimumSize = new Vector2(300, 50);
addressLineEdit.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter;
if (__instance.GetNode<NJoinFriendRefreshButton>("RefreshButton").Duplicate() is NJoinFriendRefreshButton
joinButton)
@@ -83,28 +83,27 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
joinButton.Connect(NClickableControl.SignalName.Released, Callable.From<NClickableControl>(_ =>
{
if (!ipAddressLineEdit.IsOnlyIP &&
(!ipAddressLineEdit.IsIPAndPort || !ipAddressLineEdit.Port.HasValue))
var addressInfo = addressLineEdit.GetAddressInfo();
if (!addressInfo.IsValid)
return;
SettingsHelper.Instance.SettingsModel.IPAddress = ipAddressLineEdit.Text;
SettingsHelper.Instance.SettingsModel.IPAddress = addressLineEdit.Text;
SettingsHelper.Instance.WriteSettings();
var ipAddress = ipAddressLineEdit.IPAddress;
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)");
if (ipAddress != null)
if (addressInfo.Address != null)
{
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">
<HintPath>V:\Slay.the.Spire.2\game\data_sts2_windows_x86_64\GodotSharp.dll</HintPath>
</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">
<HintPath>V:\Slay.the.Spire.2\game\data_sts2_windows_x86_64\sts2.dll</HintPath>
</Reference>