Fixed inability to connect via port
Added Scrollbar to choose Relic with more than 4 players Added custom player name feature, Now host will synchronize client names
This commit is contained in:
@@ -8,7 +8,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Components
|
|||||||
{
|
{
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
TextSubmitted += OnTextSubmitted;
|
TextChanged += OnTextChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct AddressInfo(bool isValid, string? address, ushort? port)
|
public struct AddressInfo(bool isValid, string? address, ushort? port)
|
||||||
@@ -50,13 +50,14 @@ namespace SlayTheSpire2.LAN.Multiplayer.Components
|
|||||||
else if (IPEndPoint.TryParse(Text, out var ipEndPoint))
|
else if (IPEndPoint.TryParse(Text, out var ipEndPoint))
|
||||||
{
|
{
|
||||||
addressInfo.IsValid = true;
|
addressInfo.IsValid = true;
|
||||||
|
addressInfo.Address = ipEndPoint.Address.ToString();
|
||||||
addressInfo.Port = (ushort)ipEndPoint.Port;
|
addressInfo.Port = (ushort)ipEndPoint.Port;
|
||||||
}
|
}
|
||||||
|
|
||||||
return addressInfo;
|
return addressInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTextSubmitted(string newText)
|
private void OnTextChanged(string newText)
|
||||||
{
|
{
|
||||||
Modulate = Colors.White;
|
Modulate = Colors.White;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Godot;
|
||||||
|
using MegaCrit.Sts2.Core.Nodes.GodotExtensions;
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Components
|
||||||
|
{
|
||||||
|
internal partial class PlayerNameLineEdit : NMegaLineEdit
|
||||||
|
{
|
||||||
|
[GeneratedRegex(@"[\x00-\x1F\x7F<>:""/\\|?*\x0B\x0C\x0D&;`#$%^+={}]")]
|
||||||
|
private static partial Regex InvalidCharsRegex();
|
||||||
|
|
||||||
|
public bool IsInvalid => !GetPlayerNameIsInvalid(Text);
|
||||||
|
|
||||||
|
public bool IsEmpty => string.IsNullOrEmpty(Text);
|
||||||
|
|
||||||
|
public PlayerNameLineEdit()
|
||||||
|
{
|
||||||
|
TextChanged += OnTextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTextChanged(string newText)
|
||||||
|
{
|
||||||
|
Modulate = Colors.White;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(newText) || !GetPlayerNameIsInvalid(newText))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Modulate = Colors.Red;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GetPlayerNameIsInvalid(string playerName)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(playerName) || playerName.StartsWith(' ') ||
|
||||||
|
InvalidCharsRegex().IsMatch(playerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
using Godot;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.addons.mega_text;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Nodes.Multiplayer;
|
||||||
|
using MegaCrit.Sts2.Core.Nodes.Screens.CharacterSelect;
|
||||||
|
using MegaCrit.Sts2.Core.Platform;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Components;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||||
|
|
||||||
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
||||||
|
{
|
||||||
|
internal class LanPlayerNameHelper
|
||||||
|
{
|
||||||
|
public static LanPlayerNames PlayerNameDictionary = DefaultPlayerNameDictionary;
|
||||||
|
|
||||||
|
public static INetGameService? NetService;
|
||||||
|
|
||||||
|
public static NCharacterSelectScreen? CharacterSelectScreen;
|
||||||
|
|
||||||
|
public static NMultiplayerLoadGameScreen? MultiplayerLoadGameScreen;
|
||||||
|
|
||||||
|
public static TaskCompletionSource<LanPlayerNameResponseMessage>? LanPlayerNameCompletion;
|
||||||
|
|
||||||
|
private static LanPlayerNames DefaultPlayerNameDictionary =>
|
||||||
|
!PlayerNameLineEdit.GetPlayerNameIsInvalid(SettingsHelper.Instance.SettingsModel.PlayerName)
|
||||||
|
? new LanPlayerNames
|
||||||
|
{
|
||||||
|
{ 1u, SettingsHelper.Instance.SettingsModel.PlayerName }
|
||||||
|
}
|
||||||
|
: new LanPlayerNames();
|
||||||
|
|
||||||
|
public static void SetHostPlayerName()
|
||||||
|
{
|
||||||
|
if (!PlayerNameLineEdit.GetPlayerNameIsInvalid(SettingsHelper.Instance.SettingsModel.PlayerName))
|
||||||
|
{
|
||||||
|
PlayerNameDictionary[1u] = SettingsHelper.Instance.SettingsModel.PlayerName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PlayerNameDictionary.Remove(1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetDefaultPlayerNameDictionary()
|
||||||
|
{
|
||||||
|
PlayerNameDictionary = DefaultPlayerNameDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleLanPlayerNameResponseMessage(LanPlayerNameResponseMessage lanPlayerNameResponseMessage,
|
||||||
|
ulong senderId)
|
||||||
|
{
|
||||||
|
PlayerNameDictionary = lanPlayerNameResponseMessage.playerNameDictionary;
|
||||||
|
|
||||||
|
UpdatePlayerName();
|
||||||
|
|
||||||
|
LanPlayerNameCompletion?.SetResult(lanPlayerNameResponseMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleLanPlayerNameRequestMessage(LanPlayerNameRequestMessage lanPlayerNameRequestMessage,
|
||||||
|
ulong senderId)
|
||||||
|
{
|
||||||
|
if (PlayerNameLineEdit.GetPlayerNameIsInvalid(lanPlayerNameRequestMessage.playerName))
|
||||||
|
{
|
||||||
|
NetService?.SendMessage(
|
||||||
|
new LanPlayerNameResponseMessage { playerNameDictionary = PlayerNameDictionary }, senderId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerNameDictionary[senderId] = lanPlayerNameRequestMessage.playerName;
|
||||||
|
|
||||||
|
UpdatePlayerName();
|
||||||
|
|
||||||
|
NetService?.SendMessage(new LanPlayerNameResponseMessage { playerNameDictionary = PlayerNameDictionary });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task AttemptPlayerName(NetClientGameService gameService)
|
||||||
|
{
|
||||||
|
LanPlayerNameCompletion = new TaskCompletionSource<LanPlayerNameResponseMessage>();
|
||||||
|
var message = new LanPlayerNameRequestMessage
|
||||||
|
{ playerName = SettingsHelper.Instance.SettingsModel.PlayerName };
|
||||||
|
gameService.SendMessage(message);
|
||||||
|
await LanPlayerNameCompletion.Task;
|
||||||
|
LanPlayerNameCompletion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdatePlayerName()
|
||||||
|
{
|
||||||
|
if (CharacterSelectScreen != null)
|
||||||
|
{
|
||||||
|
UpdateNameplateLabel(CharacterSelectScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MultiplayerLoadGameScreen != null)
|
||||||
|
{
|
||||||
|
UpdateNameplateLabel(MultiplayerLoadGameScreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateNameplateLabel(Control container)
|
||||||
|
{
|
||||||
|
var nodes = Traverse.Create(container).Field("_remotePlayerContainer")
|
||||||
|
.Field("_nodes").GetValue<List<NRemoteLobbyPlayer>>();
|
||||||
|
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
var megaLabel = Traverse.Create(node).Field("_nameplateLabel").GetValue<MegaLabel>();
|
||||||
|
|
||||||
|
if (!GodotObject.IsInstanceValid(megaLabel))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
megaLabel.SetTextAutoSize(PlatformUtil.GetPlayerName(PlatformType.None, node.PlayerId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
using HarmonyLib;
|
using System.Text.Json;
|
||||||
|
using HarmonyLib;
|
||||||
using MegaCrit.Sts2.Core.Logging;
|
using MegaCrit.Sts2.Core.Logging;
|
||||||
using MegaCrit.Sts2.Core.Runs;
|
using MegaCrit.Sts2.Core.Runs;
|
||||||
using MegaCrit.Sts2.Core.Saves;
|
using MegaCrit.Sts2.Core.Saves;
|
||||||
using MegaCrit.Sts2.Core.Saves.Managers;
|
using MegaCrit.Sts2.Core.Saves.Managers;
|
||||||
using MegaCrit.Sts2.Core.Saves.Migrations;
|
using MegaCrit.Sts2.Core.Saves.Migrations;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||||
|
|
||||||
// ReSharper disable MemberCanBePrivate.Global
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
// ReSharper disable ClassNeverInstantiated.Global
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
@@ -23,6 +25,9 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
|||||||
public static string CurrentMultiplayerRunSavePath =>
|
public static string CurrentMultiplayerRunSavePath =>
|
||||||
RunSaveManager.GetRunSavePath(ProfileIdProvider.CurrentProfileId, "current_lan_run_mp.save");
|
RunSaveManager.GetRunSavePath(ProfileIdProvider.CurrentProfileId, "current_lan_run_mp.save");
|
||||||
|
|
||||||
|
public static string CurrentMultiplayerRunPlayerNamesPath =>
|
||||||
|
RunSaveManager.GetRunSavePath(ProfileIdProvider.CurrentProfileId, "current_lan_run_mp_player_names.json");
|
||||||
|
|
||||||
public static bool HasMultiplayerRunSave => SaveStore.FileExists(CurrentMultiplayerRunSavePath);
|
public static bool HasMultiplayerRunSave => SaveStore.FileExists(CurrentMultiplayerRunSavePath);
|
||||||
|
|
||||||
public static ReadSaveResult<SerializableRun> LoadAndCanonicalizeMultiplayerRunSave(ulong localPlayerId)
|
public static ReadSaveResult<SerializableRun> LoadAndCanonicalizeMultiplayerRunSave(ulong localPlayerId)
|
||||||
@@ -33,6 +38,14 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = RunManager.CanonicalizeSave(readSaveResult.SaveData, localPlayerId);
|
var data = RunManager.CanonicalizeSave(readSaveResult.SaveData, localPlayerId);
|
||||||
|
var playerNamesJson = SaveStore.ReadFile(CurrentMultiplayerRunPlayerNamesPath);
|
||||||
|
if (!string.IsNullOrEmpty(playerNamesJson))
|
||||||
|
{
|
||||||
|
LanPlayerNameHelper.PlayerNameDictionary =
|
||||||
|
JsonSerializer.Deserialize<LanPlayerNames>(playerNamesJson) ?? new LanPlayerNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
LanPlayerNameHelper.SetHostPlayerName();
|
||||||
return new ReadSaveResult<SerializableRun>(data, ReadSaveStatus.Success);
|
return new ReadSaveResult<SerializableRun>(data, ReadSaveStatus.Success);
|
||||||
}
|
}
|
||||||
catch (Exception value)
|
catch (Exception value)
|
||||||
@@ -76,6 +89,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
|||||||
public static void DeleteCurrentMultiplayerRun()
|
public static void DeleteCurrentMultiplayerRun()
|
||||||
{
|
{
|
||||||
SaveStore.DeleteFile(CurrentMultiplayerRunSavePath);
|
SaveStore.DeleteFile(CurrentMultiplayerRunSavePath);
|
||||||
|
SaveStore.DeleteFile(CurrentMultiplayerRunPlayerNamesPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RenameBrokenMultiplayerRunSave(ReadSaveStatus status)
|
public static void RenameBrokenMultiplayerRunSave(ReadSaveStatus status)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using MegaCrit.Sts2.Core.Logging;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Serialization;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Transport;
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Models
|
||||||
|
{
|
||||||
|
public struct LanPlayerNameRequestMessage : INetMessage
|
||||||
|
{
|
||||||
|
public string playerName;
|
||||||
|
|
||||||
|
public bool ShouldBroadcast => false;
|
||||||
|
public NetTransferMode Mode => NetTransferMode.Reliable;
|
||||||
|
public LogLevel LogLevel => LogLevel.Info;
|
||||||
|
|
||||||
|
public void Serialize(PacketWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteString(playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(PacketReader reader)
|
||||||
|
{
|
||||||
|
playerName = reader.ReadString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using MegaCrit.Sts2.Core.Logging;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Serialization;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Transport;
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Models
|
||||||
|
{
|
||||||
|
public struct LanPlayerNameResponseMessage : INetMessage
|
||||||
|
{
|
||||||
|
public LanPlayerNames playerNameDictionary;
|
||||||
|
|
||||||
|
public bool ShouldBroadcast => false;
|
||||||
|
public NetTransferMode Mode => NetTransferMode.Reliable;
|
||||||
|
public LogLevel LogLevel => LogLevel.Info;
|
||||||
|
|
||||||
|
public void Serialize(PacketWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteInt(playerNameDictionary.Count);
|
||||||
|
foreach (var keyValue in playerNameDictionary)
|
||||||
|
{
|
||||||
|
writer.WriteULong(keyValue.Key);
|
||||||
|
writer.WriteString(keyValue.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(PacketReader reader)
|
||||||
|
{
|
||||||
|
var count = reader.ReadInt();
|
||||||
|
playerNameDictionary = new LanPlayerNames();
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
playerNameDictionary.Add(reader.ReadULong(), reader.ReadString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Models
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(LanPlayerNames))]
|
||||||
|
public partial class LanPlayerNamesContext : JsonSerializerContext;
|
||||||
|
|
||||||
|
public class LanPlayerNames : Dictionary<ulong, string>;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using MegaCrit.Sts2.Core.Saves;
|
using MegaCrit.Sts2.Core.Saves;
|
||||||
|
using Steamworks;
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Models
|
namespace SlayTheSpire2.LAN.Multiplayer.Models
|
||||||
{
|
{
|
||||||
@@ -12,6 +13,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Models
|
|||||||
[JsonPropertyName("host_max_players")] public int HostMaxPlayers { get; set; } = 4;
|
[JsonPropertyName("host_max_players")] public int HostMaxPlayers { get; set; } = 4;
|
||||||
[JsonPropertyName("ip_address")] public string IPAddress { get; set; } = "127.0.0.1";
|
[JsonPropertyName("ip_address")] public string IPAddress { get; set; } = "127.0.0.1";
|
||||||
[JsonPropertyName("net_id")] public ulong NetId { get; set; } = 1000u;
|
[JsonPropertyName("net_id")] public ulong NetId { get; set; } = 1000u;
|
||||||
|
[JsonPropertyName("player_name")] public string PlayerName { get; set; } = SteamFriends.GetPersonaName();
|
||||||
|
|
||||||
public int SchemaVersion { get; set; }
|
public int SchemaVersion { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ using Logger = MegaCrit.Sts2.Core.Logging.Logger;
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.ENet
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(ENetClient), "ConnectToHost")]
|
[HarmonyPatch(typeof(ENetClient), "ConnectToHost")]
|
||||||
internal class ENetClientConnectToHostPatch
|
internal class ENetClientConnectToHostPatch
|
||||||
@@ -132,7 +132,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
if (eNetHandshakeResponse.status == ENetHandshakeStatus.IdCollision)
|
if (eNetHandshakeResponse.status == ENetHandshakeStatus.IdCollision)
|
||||||
{
|
{
|
||||||
logger.Warn(
|
logger.Warn(
|
||||||
$"NetID:{netId} already occupied, Next try server send new NetID:{eNetHandshakeResponse.newNetId}");
|
$"NetID:{netId} already occupied, Next try host send new NetID:{eNetHandshakeResponse.newNetId}");
|
||||||
return (new NetErrorInfo(NetError.Kicked, selfInitiated: false),
|
return (new NetErrorInfo(NetError.Kicked, selfInitiated: false),
|
||||||
eNetHandshakeResponse.newNetId);
|
eNetHandshakeResponse.newNetId);
|
||||||
}
|
}
|
||||||
+1
-1
@@ -12,7 +12,7 @@ using Logger = MegaCrit.Sts2.Core.Logging.Logger;
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.ENet
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(ENetHost), "DoClientHandshake")]
|
[HarmonyPatch(typeof(ENetHost), "DoClientHandshake")]
|
||||||
internal class ENetHostDoClientHandshakePatch
|
internal class ENetHostDoClientHandshakePatch
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Entities.Multiplayer;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Connection;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Messages.Lobby;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||||
|
{
|
||||||
|
[HarmonyPatch(typeof(JoinFlow), "AttemptJoin")]
|
||||||
|
internal class JoinFlowAttemptJoinPatch
|
||||||
|
{
|
||||||
|
private static void Postfix(JoinFlow __instance, ref Task<ClientLobbyJoinResponseMessage> __result)
|
||||||
|
{
|
||||||
|
__result = AttemptJoin(__instance, __result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ClientLobbyJoinResponseMessage> AttemptJoin(JoinFlow joinFlow,
|
||||||
|
Task<ClientLobbyJoinResponseMessage> clientLobbyJoinResponseMessage)
|
||||||
|
{
|
||||||
|
var result = await clientLobbyJoinResponseMessage;
|
||||||
|
|
||||||
|
if (joinFlow.NetService == null)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
joinFlow.NetService.RegisterMessageHandler<LanPlayerNameResponseMessage>(LanPlayerNameHelper
|
||||||
|
.HandleLanPlayerNameResponseMessage);
|
||||||
|
|
||||||
|
await LanPlayerNameHelper.AttemptPlayerName(joinFlow.NetService);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(JoinFlow), "AttemptLoadJoin")]
|
||||||
|
internal class JoinFlowAttemptLoadJoinPatch
|
||||||
|
{
|
||||||
|
private static void Postfix(JoinFlow __instance, ref Task<ClientLoadJoinResponseMessage> __result)
|
||||||
|
{
|
||||||
|
__result = AttemptLoadJoin(__instance, __result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ClientLoadJoinResponseMessage> AttemptLoadJoin(JoinFlow joinFlow,
|
||||||
|
Task<ClientLoadJoinResponseMessage> clientLoadJoinResponseMessage)
|
||||||
|
{
|
||||||
|
var result = await clientLoadJoinResponseMessage;
|
||||||
|
|
||||||
|
if (joinFlow.NetService == null)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
joinFlow.NetService.RegisterMessageHandler<LanPlayerNameResponseMessage>(LanPlayerNameHelper
|
||||||
|
.HandleLanPlayerNameResponseMessage);
|
||||||
|
|
||||||
|
await LanPlayerNameHelper.AttemptPlayerName(joinFlow.NetService);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(JoinFlow), "AttemptRejoin")]
|
||||||
|
internal class JoinFlowAttemptRejoinPatch
|
||||||
|
{
|
||||||
|
private static void Postfix(JoinFlow __instance, ref Task<ClientRejoinResponseMessage> __result)
|
||||||
|
{
|
||||||
|
__result = AttemptRejoin(__instance, __result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ClientRejoinResponseMessage> AttemptRejoin(JoinFlow joinFlow,
|
||||||
|
Task<ClientRejoinResponseMessage> clientRejoinResponseMessage)
|
||||||
|
{
|
||||||
|
var result = await clientRejoinResponseMessage;
|
||||||
|
|
||||||
|
if (joinFlow.NetService == null)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
joinFlow.NetService.RegisterMessageHandler<LanPlayerNameResponseMessage>(LanPlayerNameHelper
|
||||||
|
.HandleLanPlayerNameResponseMessage);
|
||||||
|
|
||||||
|
await LanPlayerNameHelper.AttemptPlayerName(joinFlow.NetService);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(JoinFlow), "OnDisconnected")]
|
||||||
|
internal class JoinFlowOnDisconnectedPatch
|
||||||
|
{
|
||||||
|
private static void Postfix(NetErrorInfo info)
|
||||||
|
{
|
||||||
|
var exception =
|
||||||
|
new ClientConnectionFailedException(
|
||||||
|
$"Unexpectedly disconnected from host while joining. Reason: {info.GetReason()}", info);
|
||||||
|
|
||||||
|
var lanPlayerNameCompletion = LanPlayerNameHelper.LanPlayerNameCompletion;
|
||||||
|
|
||||||
|
if (lanPlayerNameCompletion?.Task is { IsCompleted: false })
|
||||||
|
{
|
||||||
|
lanPlayerNameCompletion.SetException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(JoinFlow), "Cancel")]
|
||||||
|
internal class JoinFlowCancelPatch
|
||||||
|
{
|
||||||
|
private static void Postfix()
|
||||||
|
{
|
||||||
|
var lanPlayerNameCompletion = LanPlayerNameHelper.LanPlayerNameCompletion;
|
||||||
|
|
||||||
|
if (lanPlayerNameCompletion?.Task is { IsCompleted: false })
|
||||||
|
{
|
||||||
|
lanPlayerNameCompletion.SetCanceled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
using Godot;
|
|
||||||
using HarmonyLib;
|
|
||||||
using MegaCrit.Sts2.Core.Nodes.Screens.TreasureRoomRelic;
|
|
||||||
using MegaCrit.Sts2.Core.Runs;
|
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global
|
|
||||||
// ReSharper disable UnusedType.Global
|
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|
||||||
{
|
|
||||||
[HarmonyPatch(typeof(NTreasureRoomRelicCollection), "_Ready")]
|
|
||||||
internal class NTreasureRoomRelicCollectionPatch
|
|
||||||
{
|
|
||||||
private static void Prefix(NTreasureRoomRelicCollection __instance)
|
|
||||||
{
|
|
||||||
var runState = Traverse.Create(RunManager.Instance).Property("State").GetValue<RunState?>();
|
|
||||||
|
|
||||||
if (runState is { Players.Count: > 4 })
|
|
||||||
{
|
|
||||||
var container = __instance.GetNode("Container");
|
|
||||||
var multiplayerRelicHolder = container.GetNode("MultiplayerRelicHolder4");
|
|
||||||
|
|
||||||
for (var i = 4; i < runState.Players.Count; i++)
|
|
||||||
{
|
|
||||||
var newMultiplayerRelicHolder = multiplayerRelicHolder.Duplicate();
|
|
||||||
container.AddChild(newMultiplayerRelicHolder);
|
|
||||||
|
|
||||||
if (newMultiplayerRelicHolder is NTreasureRoomRelicHolder nTreasureRoomRelicHolder)
|
|
||||||
{
|
|
||||||
nTreasureRoomRelicHolder.Name = $"MultiplayerRelicHolder{i + 1}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Postfix(List<NTreasureRoomRelicHolder> ____multiplayerHolders)
|
|
||||||
{
|
|
||||||
if (____multiplayerHolders.Count > 4)
|
|
||||||
{
|
|
||||||
var more16People = ____multiplayerHolders.Count > 16;
|
|
||||||
|
|
||||||
var position = new Vector2(more16People ? 30 : 62, 110);
|
|
||||||
|
|
||||||
for (var i = 0; i < ____multiplayerHolders.Count; i++)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
if (more16People)
|
|
||||||
{
|
|
||||||
position = i % 6 == 0
|
|
||||||
? new Vector2(30, position.Y + 140)
|
|
||||||
: new Vector2(position.X + 140, position.Y);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
position = i % 4 == 0
|
|
||||||
? new Vector2(62, position.Y + 140)
|
|
||||||
: new Vector2(position.X + 240, position.Y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
____multiplayerHolders[i].Position = position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Platform.Null;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||||
|
{
|
||||||
|
[HarmonyPatch(typeof(NullPlatformUtilStrategy), "GetPlayerName")]
|
||||||
|
internal class NullPlatformUtilStrategyGetPlayerNamePatch
|
||||||
|
{
|
||||||
|
private static bool Prefix(ulong playerId, List<NullMultiplayerName>? ____mpNames, ref string __result)
|
||||||
|
{
|
||||||
|
if (____mpNames != null)
|
||||||
|
{
|
||||||
|
foreach (var mpName in ____mpNames)
|
||||||
|
{
|
||||||
|
if (mpName.netId == playerId)
|
||||||
|
{
|
||||||
|
__result = mpName.name;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LanPlayerNameHelper.PlayerNameDictionary.TryGetValue(playerId, out var playerName))
|
||||||
|
{
|
||||||
|
__result = playerName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
__result = playerId switch
|
||||||
|
{
|
||||||
|
1uL => "Test Host",
|
||||||
|
1000uL => "Test Client 1",
|
||||||
|
2000uL => "Test Client 2",
|
||||||
|
3000uL => "Test Client 3",
|
||||||
|
_ => playerId.ToString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game.Lobby;
|
||||||
|
using MegaCrit.Sts2.Core.Platform;
|
||||||
|
using MegaCrit.Sts2.Core.Runs;
|
||||||
|
using MegaCrit.Sts2.Core.Saves;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||||
|
{
|
||||||
|
[HarmonyPatch]
|
||||||
|
internal class RunLobbyConstructorPatchs
|
||||||
|
{
|
||||||
|
private static IEnumerable<MethodBase> TargetMethods()
|
||||||
|
{
|
||||||
|
yield return typeof(StartRunLobby).GetConstructor([
|
||||||
|
typeof(GameMode), typeof(INetGameService), typeof(IStartRunLobbyListener), typeof(int)
|
||||||
|
])!;
|
||||||
|
yield return typeof(RunLobby).GetConstructor([
|
||||||
|
typeof(GameMode), typeof(INetGameService), typeof(IRunLobbyListener), typeof(IPlayerCollection),
|
||||||
|
typeof(IEnumerable<ulong>)
|
||||||
|
])!;
|
||||||
|
yield return typeof(LoadRunLobby).GetConstructor([
|
||||||
|
typeof(INetGameService), typeof(ILoadRunLobbyListener), typeof(SerializableRun)
|
||||||
|
])!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Prefix(INetGameService netService)
|
||||||
|
{
|
||||||
|
if (netService.Platform == PlatformType.None)
|
||||||
|
{
|
||||||
|
LanPlayerNameHelper.NetService = netService;
|
||||||
|
|
||||||
|
netService.RegisterMessageHandler<LanPlayerNameResponseMessage>(LanPlayerNameHelper
|
||||||
|
.HandleLanPlayerNameResponseMessage);
|
||||||
|
|
||||||
|
if (netService.Type == NetGameType.Host)
|
||||||
|
{
|
||||||
|
netService.RegisterMessageHandler<LanPlayerNameRequestMessage>(LanPlayerNameHelper
|
||||||
|
.HandleLanPlayerNameRequestMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch]
|
||||||
|
internal class RunLobbyCleanUpPatchs
|
||||||
|
{
|
||||||
|
private static IEnumerable<MethodBase> TargetMethods()
|
||||||
|
{
|
||||||
|
yield return typeof(StartRunLobby).GetMethod("CleanUp", BindingFlags.Instance | BindingFlags.Public)!;
|
||||||
|
yield return typeof(RunLobby).GetMethod("Dispose", BindingFlags.Instance | BindingFlags.Public)!;
|
||||||
|
yield return typeof(LoadRunLobby).GetMethod("CleanUp", BindingFlags.Instance | BindingFlags.Public)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Prefix(object __instance)
|
||||||
|
{
|
||||||
|
var netService = __instance is StartRunLobby or LoadRunLobby
|
||||||
|
? Traverse.Create(__instance).Property("NetService").GetValue<INetGameService>()
|
||||||
|
: Traverse.Create(__instance).Field("_netService").GetValue<INetGameService>();
|
||||||
|
|
||||||
|
if (netService.Platform == PlatformType.None)
|
||||||
|
{
|
||||||
|
LanPlayerNameHelper.SetDefaultPlayerNameDictionary();
|
||||||
|
|
||||||
|
netService.UnregisterMessageHandler<LanPlayerNameResponseMessage>(LanPlayerNameHelper
|
||||||
|
.HandleLanPlayerNameResponseMessage);
|
||||||
|
|
||||||
|
if (netService.Type == NetGameType.Host)
|
||||||
|
{
|
||||||
|
netService.UnregisterMessageHandler<LanPlayerNameRequestMessage>(LanPlayerNameHelper
|
||||||
|
.HandleLanPlayerNameRequestMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,11 +8,12 @@ using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
|||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(RunManager), "CleanUp")]
|
[HarmonyPatch(typeof(RunManager), "CleanUp")]
|
||||||
internal class RunManagerPatch
|
internal class RunManagerCleanUpPatch
|
||||||
{
|
{
|
||||||
private static void Postfix()
|
private static void Postfix()
|
||||||
{
|
{
|
||||||
LanMapDrawingsHelper.DisableDrawingHashSet.Clear();
|
LanMapDrawingsHelper.DisableDrawingHashSet.Clear();
|
||||||
|
LanPlayerNameHelper.SetDefaultPlayerNameDictionary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ using MegaCrit.Sts2.Core.Runs;
|
|||||||
using MegaCrit.Sts2.Core.Saves;
|
using MegaCrit.Sts2.Core.Saves;
|
||||||
using MegaCrit.Sts2.Core.Saves.Managers;
|
using MegaCrit.Sts2.Core.Saves.Managers;
|
||||||
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
@@ -35,8 +36,12 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var value = RunManager.Instance.ToSave(preFinishedRoom);
|
var value = RunManager.Instance.ToSave(preFinishedRoom);
|
||||||
var savePath = RunManager.Instance.NetService.Type.IsMultiplayer()
|
|
||||||
? RunManager.Instance.NetService.Platform == PlatformType.None
|
var isMultiplayer = RunManager.Instance.NetService.Type.IsMultiplayer();
|
||||||
|
var isNonePlatform = RunManager.Instance.NetService.Platform == PlatformType.None;
|
||||||
|
|
||||||
|
var savePath = isMultiplayer
|
||||||
|
? isNonePlatform
|
||||||
? LanRunSaveManagerHelper.CurrentMultiplayerRunSavePath
|
? LanRunSaveManagerHelper.CurrentMultiplayerRunSavePath
|
||||||
: Traverse.Create(runSaveManager).Property("CurrentMultiplayerRunSavePath").GetValue<string>()
|
: Traverse.Create(runSaveManager).Property("CurrentMultiplayerRunSavePath").GetValue<string>()
|
||||||
: Traverse.Create(runSaveManager).Property("CurrentRunSavePath").GetValue<string>();
|
: Traverse.Create(runSaveManager).Property("CurrentRunSavePath").GetValue<string>();
|
||||||
@@ -55,6 +60,25 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
stream.Seek(0L, SeekOrigin.Begin);
|
stream.Seek(0L, SeekOrigin.Begin);
|
||||||
await saveStore.WriteFileAsync(savePath, stream.ToArray());
|
await saveStore.WriteFileAsync(savePath, stream.ToArray());
|
||||||
|
|
||||||
|
if (isMultiplayer && isNonePlatform)
|
||||||
|
{
|
||||||
|
using var playerNamesStream = new MemoryStream();
|
||||||
|
if (!forceSynchronous)
|
||||||
|
{
|
||||||
|
await JsonSerializer.SerializeAsync(playerNamesStream, LanPlayerNameHelper.PlayerNameDictionary,
|
||||||
|
LanPlayerNamesContext.Default.LanPlayerNames, CancellationToken.None);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await JsonSerializer.SerializeAsync(playerNamesStream, LanPlayerNameHelper.PlayerNameDictionary,
|
||||||
|
LanPlayerNamesContext.Default.LanPlayerNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
playerNamesStream.Seek(0L, SeekOrigin.Begin);
|
||||||
|
await saveStore.WriteFileAsync(LanRunSaveManagerHelper.CurrentMultiplayerRunPlayerNamesPath,
|
||||||
|
playerNamesStream.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
saved?.Invoke();
|
saved?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Nodes.Screens.CharacterSelect;
|
||||||
|
using MegaCrit.Sts2.Core.Platform;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
|
{
|
||||||
|
[HarmonyPatch]
|
||||||
|
internal class NCharacterSelectScreenInitializeMultiplayerPatchs
|
||||||
|
{
|
||||||
|
private static IEnumerable<MethodInfo> TargetMethods()
|
||||||
|
{
|
||||||
|
yield return typeof(NCharacterSelectScreen).GetMethod("InitializeMultiplayerAsClient",
|
||||||
|
BindingFlags.Instance | BindingFlags.Public)!;
|
||||||
|
yield return typeof(NCharacterSelectScreen).GetMethod("InitializeMultiplayerAsHost",
|
||||||
|
BindingFlags.Instance | BindingFlags.Public)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Prefix(NCharacterSelectScreen __instance, INetGameService gameService)
|
||||||
|
{
|
||||||
|
if (gameService.Platform == PlatformType.None)
|
||||||
|
{
|
||||||
|
LanPlayerNameHelper.CharacterSelectScreen = __instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-9
@@ -12,10 +12,10 @@ using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(NJoinFriendScreen), "_Ready")]
|
[HarmonyPatch(typeof(NJoinFriendScreen), "_Ready")]
|
||||||
internal class NJoinFriendScreenPatch
|
internal class NJoinFriendScreenReadyPatch
|
||||||
{
|
{
|
||||||
private static void Prefix(NJoinFriendScreen __instance)
|
private static void Prefix(NJoinFriendScreen __instance)
|
||||||
{
|
{
|
||||||
@@ -28,11 +28,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
lanPanel.PatchMarginLeft = 12;
|
lanPanel.PatchMarginLeft = 12;
|
||||||
lanPanel.PatchMarginRight = 12;
|
lanPanel.PatchMarginRight = 12;
|
||||||
|
|
||||||
lanPanel.AnchorLeft = 0.5f;
|
lanPanel.SetAnchorsPreset(Control.LayoutPreset.Center);
|
||||||
lanPanel.AnchorTop = 0.5f;
|
|
||||||
lanPanel.AnchorRight = 0.5f;
|
|
||||||
lanPanel.AnchorBottom = 0.5f;
|
|
||||||
|
|
||||||
lanPanel.OffsetLeft = 450;
|
lanPanel.OffsetLeft = 450;
|
||||||
lanPanel.OffsetTop = -338;
|
lanPanel.OffsetTop = -338;
|
||||||
lanPanel.OffsetRight = 790;
|
lanPanel.OffsetRight = 790;
|
||||||
@@ -49,8 +45,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
lanPanel.AddChild(vBoxContainer);
|
lanPanel.AddChild(vBoxContainer);
|
||||||
|
|
||||||
vBoxContainer.Alignment = BoxContainer.AlignmentMode.Center;
|
vBoxContainer.Alignment = BoxContainer.AlignmentMode.Center;
|
||||||
vBoxContainer.SetAnchorsPreset(Control.LayoutPreset.FullRect);
|
vBoxContainer.SetAnchorsAndOffsetsPreset(Control.LayoutPreset.FullRect);
|
||||||
vBoxContainer.Size = new Vector2(350, 676);
|
|
||||||
vBoxContainer.AddThemeConstantOverride("separation", 24);
|
vBoxContainer.AddThemeConstantOverride("separation", 24);
|
||||||
|
|
||||||
if (__instance.GetNode("TitleLabel").Duplicate() is MegaLabel ipAddressLabel)
|
if (__instance.GetNode("TitleLabel").Duplicate() is MegaLabel ipAddressLabel)
|
||||||
+2
-2
@@ -7,10 +7,10 @@ using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(NMapDrawings), "CreateLineForPlayer")]
|
[HarmonyPatch(typeof(NMapDrawings), "CreateLineForPlayer")]
|
||||||
internal class NMapDrawingsPatch
|
internal class NMapDrawingsCreateLineForPlayerPatch
|
||||||
{
|
{
|
||||||
private static void Postfix(Player player, Line2D __result)
|
private static void Postfix(Player player, Line2D __result)
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Nodes.Screens.CharacterSelect;
|
||||||
|
using MegaCrit.Sts2.Core.Platform;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
|
{
|
||||||
|
[HarmonyPatch]
|
||||||
|
internal class NMultiplayerLoadGameScreenInitializePatchs
|
||||||
|
{
|
||||||
|
private static IEnumerable<MethodInfo> TargetMethods()
|
||||||
|
{
|
||||||
|
yield return typeof(NMultiplayerLoadGameScreen).GetMethod("InitializeAsHost",
|
||||||
|
BindingFlags.Instance | BindingFlags.Public)!;
|
||||||
|
yield return typeof(NMultiplayerLoadGameScreen).GetMethod("InitializeAsClient",
|
||||||
|
BindingFlags.Instance | BindingFlags.Public)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Prefix(NMultiplayerLoadGameScreen __instance, INetGameService gameService)
|
||||||
|
{
|
||||||
|
if (gameService.Platform == PlatformType.None)
|
||||||
|
{
|
||||||
|
LanPlayerNameHelper.MultiplayerLoadGameScreen = __instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -12,10 +12,10 @@ using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(NMultiplayerPlayerExpandedState), "_Ready")]
|
[HarmonyPatch(typeof(NMultiplayerPlayerExpandedState), "_Ready")]
|
||||||
internal class NMultiplayerPlayerExpandedStatePatch
|
internal class NMultiplayerPlayerExpandedStateReadyPatch
|
||||||
{
|
{
|
||||||
private static void Prefix(NMultiplayerPlayerExpandedState __instance, Player ____player)
|
private static void Prefix(NMultiplayerPlayerExpandedState __instance, Player ____player)
|
||||||
{
|
{
|
||||||
+1
-1
@@ -10,7 +10,7 @@ using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(NMultiplayerSubmenu), "_Ready")]
|
[HarmonyPatch(typeof(NMultiplayerSubmenu), "_Ready")]
|
||||||
internal class NMultiplayerSubmenuReadyPatch
|
internal class NMultiplayerSubmenuReadyPatch
|
||||||
+2
-2
@@ -6,10 +6,10 @@ using MegaCrit.Sts2.Core.Runs;
|
|||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(NRestSiteRoom), "_Ready")]
|
[HarmonyPatch(typeof(NRestSiteRoom), "_Ready")]
|
||||||
internal class NRestSiteRoomPatch
|
internal class NRestSiteRoomReadyPatch
|
||||||
{
|
{
|
||||||
private static void Prefix(NRestSiteRoom __instance, IRunState ____runState,
|
private static void Prefix(NRestSiteRoom __instance, IRunState ____runState,
|
||||||
List<Control> ____characterContainers)
|
List<Control> ____characterContainers)
|
||||||
+65
-15
@@ -1,21 +1,23 @@
|
|||||||
using Godot;
|
using Godot;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using MegaCrit.Sts2.Core.Nodes.Screens.Settings;
|
using MegaCrit.Sts2.Core.Nodes.Screens.Settings;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Components;
|
||||||
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
// ReSharper disable UnusedType.Global
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(NSettingsScreen), "_Ready")]
|
[HarmonyPatch(typeof(NSettingsScreen), "_Ready")]
|
||||||
internal class NSettingsScreenPatch
|
internal class NSettingsScreenReadyPatch
|
||||||
{
|
{
|
||||||
private static void Prefix(NSettingsScreen __instance)
|
private static void Prefix(NSettingsScreen __instance)
|
||||||
{
|
{
|
||||||
var moddingNode = __instance.GetNode("%Modding");
|
var moddingNode = __instance.GetNode("%Modding");
|
||||||
|
|
||||||
var vBoxContainer = moddingNode.GetParent();
|
var vBoxContainerNode = moddingNode.GetParent();
|
||||||
|
var generalSettings = vBoxContainerNode.GetParent();
|
||||||
|
|
||||||
if (__instance.GetNode("%ModdingDivider").Duplicate() is ColorRect hostPortDivider &&
|
if (__instance.GetNode("%ModdingDivider").Duplicate() is ColorRect hostPortDivider &&
|
||||||
moddingNode.Duplicate() is MarginContainer hostPort &&
|
moddingNode.Duplicate() is MarginContainer hostPort &&
|
||||||
@@ -23,8 +25,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
{
|
{
|
||||||
hostPortDivider.Name = "HostPortDivider";
|
hostPortDivider.Name = "HostPortDivider";
|
||||||
|
|
||||||
vBoxContainer.AddChild(hostPortDivider);
|
vBoxContainerNode.AddChild(hostPortDivider);
|
||||||
vBoxContainer.MoveChild(hostPortDivider, moddingNode.GetIndex() + 1);
|
vBoxContainerNode.MoveChild(hostPortDivider, moddingNode.GetIndex() + 1);
|
||||||
|
|
||||||
hostPortDivider.Show();
|
hostPortDivider.Show();
|
||||||
|
|
||||||
@@ -32,8 +34,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
|
|
||||||
hostPort.RemoveChild(hostPort.GetNode("ModdingButton"));
|
hostPort.RemoveChild(hostPort.GetNode("ModdingButton"));
|
||||||
|
|
||||||
vBoxContainer.AddChild(hostPort);
|
vBoxContainerNode.AddChild(hostPort);
|
||||||
vBoxContainer.MoveChild(hostPort, hostPortDivider.GetIndex() + 1);
|
vBoxContainerNode.MoveChild(hostPort, hostPortDivider.GetIndex() + 1);
|
||||||
|
|
||||||
hostPort.Show();
|
hostPort.Show();
|
||||||
|
|
||||||
@@ -64,8 +66,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
{
|
{
|
||||||
hostMaxPlayersDivider.Name = "HostMaxPlayersDivider";
|
hostMaxPlayersDivider.Name = "HostMaxPlayersDivider";
|
||||||
|
|
||||||
vBoxContainer.AddChild(hostMaxPlayersDivider);
|
vBoxContainerNode.AddChild(hostMaxPlayersDivider);
|
||||||
vBoxContainer.MoveChild(hostMaxPlayersDivider, moddingNode.GetIndex() + 1);
|
vBoxContainerNode.MoveChild(hostMaxPlayersDivider, moddingNode.GetIndex() + 1);
|
||||||
|
|
||||||
hostMaxPlayersDivider.Show();
|
hostMaxPlayersDivider.Show();
|
||||||
|
|
||||||
@@ -73,8 +75,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
|
|
||||||
hostMaxPlayers.RemoveChild(hostMaxPlayers.GetNode("ModdingButton"));
|
hostMaxPlayers.RemoveChild(hostMaxPlayers.GetNode("ModdingButton"));
|
||||||
|
|
||||||
vBoxContainer.AddChild(hostMaxPlayers);
|
vBoxContainerNode.AddChild(hostMaxPlayers);
|
||||||
vBoxContainer.MoveChild(hostMaxPlayers, hostMaxPlayersDivider.GetIndex() + 1);
|
vBoxContainerNode.MoveChild(hostMaxPlayers, hostMaxPlayersDivider.GetIndex() + 1);
|
||||||
|
|
||||||
hostMaxPlayers.Show();
|
hostMaxPlayers.Show();
|
||||||
|
|
||||||
@@ -98,14 +100,57 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
hostMaxPlayersLabel.Text = "Host Max Players";
|
hostMaxPlayersLabel.Text = "Host Max Players";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__instance.GetNode("%ModdingDivider").Duplicate() is ColorRect playerNameDivider &&
|
||||||
|
moddingNode.Duplicate() is MarginContainer playerName &&
|
||||||
|
playerName.GetNode("Label") is RichTextLabel playerNameLabel)
|
||||||
|
{
|
||||||
|
playerNameDivider.Name = "PlayerNameDivider";
|
||||||
|
|
||||||
|
vBoxContainerNode.AddChild(playerNameDivider);
|
||||||
|
vBoxContainerNode.MoveChild(playerNameDivider, moddingNode.GetIndex() + 1);
|
||||||
|
|
||||||
|
playerNameDivider.Show();
|
||||||
|
|
||||||
|
playerName.Name = "PlayerName";
|
||||||
|
|
||||||
|
playerName.RemoveChild(playerName.GetNode("ModdingButton"));
|
||||||
|
|
||||||
|
vBoxContainerNode.AddChild(playerName);
|
||||||
|
vBoxContainerNode.MoveChild(playerName, playerNameDivider.GetIndex() + 1);
|
||||||
|
|
||||||
|
playerName.Show();
|
||||||
|
|
||||||
|
var playerNameInput = new PlayerNameLineEdit { Name = "PlayerNameInput" };
|
||||||
|
|
||||||
|
playerName.AddChild(playerNameInput);
|
||||||
|
|
||||||
|
playerNameInput.CustomMinimumSize = new Vector2(324, 64);
|
||||||
|
playerNameInput.SizeFlagsHorizontal = Control.SizeFlags.ShrinkEnd;
|
||||||
|
playerNameInput.Alignment = HorizontalAlignment.Center;
|
||||||
|
|
||||||
|
playerNameInput.MaxLength = 16;
|
||||||
|
playerNameInput.Text = SettingsHelper.Instance.SettingsModel.PlayerName;
|
||||||
|
playerNameInput.TextChanged += value =>
|
||||||
|
{
|
||||||
|
if (playerNameInput.IsEmpty || !playerNameInput.IsInvalid)
|
||||||
|
{
|
||||||
|
SettingsHelper.Instance.SettingsModel.PlayerName = value;
|
||||||
|
LanPlayerNameHelper.SetHostPlayerName();
|
||||||
|
SettingsHelper.Instance.WriteSettings();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
playerNameLabel.Text = "Player Name";
|
||||||
|
}
|
||||||
|
|
||||||
if (__instance.GetNode("%ModdingDivider").Duplicate() is ColorRect netIdDivider &&
|
if (__instance.GetNode("%ModdingDivider").Duplicate() is ColorRect netIdDivider &&
|
||||||
moddingNode.Duplicate() is MarginContainer netId &&
|
moddingNode.Duplicate() is MarginContainer netId &&
|
||||||
netId.GetNode("Label") is RichTextLabel netIdLabel)
|
netId.GetNode("Label") is RichTextLabel netIdLabel)
|
||||||
{
|
{
|
||||||
netIdDivider.Name = "NetIDDivider";
|
netIdDivider.Name = "NetIDDivider";
|
||||||
|
|
||||||
vBoxContainer.AddChild(netIdDivider);
|
vBoxContainerNode.AddChild(netIdDivider);
|
||||||
vBoxContainer.MoveChild(netIdDivider, moddingNode.GetIndex() + 1);
|
vBoxContainerNode.MoveChild(netIdDivider, moddingNode.GetIndex() + 1);
|
||||||
|
|
||||||
netIdDivider.Show();
|
netIdDivider.Show();
|
||||||
|
|
||||||
@@ -113,8 +158,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
|
|
||||||
netId.RemoveChild(netId.GetNode("ModdingButton"));
|
netId.RemoveChild(netId.GetNode("ModdingButton"));
|
||||||
|
|
||||||
vBoxContainer.AddChild(netId);
|
vBoxContainerNode.AddChild(netId);
|
||||||
vBoxContainer.MoveChild(netId, netIdDivider.GetIndex() + 1);
|
vBoxContainerNode.MoveChild(netId, netIdDivider.GetIndex() + 1);
|
||||||
|
|
||||||
netId.Show();
|
netId.Show();
|
||||||
|
|
||||||
@@ -138,6 +183,11 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
|
|
||||||
netIdLabel.Text = "NetID";
|
netIdLabel.Text = "NetID";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (generalSettings is NSettingsPanel nSettingsPanel)
|
||||||
|
{
|
||||||
|
Traverse.Create(nSettingsPanel).Method("RefreshSize").GetValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using Godot;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Assets;
|
||||||
|
using MegaCrit.Sts2.Core.Helpers;
|
||||||
|
using MegaCrit.Sts2.Core.Nodes.GodotExtensions;
|
||||||
|
using MegaCrit.Sts2.Core.Nodes.Screens.TreasureRoomRelic;
|
||||||
|
using MegaCrit.Sts2.Core.Runs;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
|
{
|
||||||
|
[HarmonyPatch(typeof(NTreasureRoomRelicCollection), "_Ready")]
|
||||||
|
internal class NTreasureRoomRelicCollectionReadyPatch
|
||||||
|
{
|
||||||
|
private static void Prefix(NTreasureRoomRelicCollection __instance)
|
||||||
|
{
|
||||||
|
var runState = Traverse.Create(RunManager.Instance).Property("State").GetValue<RunState?>();
|
||||||
|
|
||||||
|
if (runState is { Players.Count: > 4 })
|
||||||
|
{
|
||||||
|
var container = __instance.GetNode("Container");
|
||||||
|
var multiplayerRelicHolder = container.GetNode("MultiplayerRelicHolder4");
|
||||||
|
|
||||||
|
for (var i = 4; i < runState.Players.Count; i++)
|
||||||
|
{
|
||||||
|
var newMultiplayerRelicHolder = multiplayerRelicHolder.Duplicate();
|
||||||
|
container.AddChild(newMultiplayerRelicHolder);
|
||||||
|
|
||||||
|
if (newMultiplayerRelicHolder is NTreasureRoomRelicHolder nTreasureRoomRelicHolder)
|
||||||
|
{
|
||||||
|
nTreasureRoomRelicHolder.Name = $"MultiplayerRelicHolder{i + 1}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Postfix(NTreasureRoomRelicCollection __instance,
|
||||||
|
List<NTreasureRoomRelicHolder> ____multiplayerHolders)
|
||||||
|
{
|
||||||
|
if (____multiplayerHolders.Count > 4)
|
||||||
|
{
|
||||||
|
var container = __instance.GetNode("Container");
|
||||||
|
|
||||||
|
var scrollContainer = new NScrollableContainer { Name = "ScrollableContainer" };
|
||||||
|
|
||||||
|
var mask = new TextureRect { Name = "Mask" };
|
||||||
|
scrollContainer.AddChild(mask);
|
||||||
|
|
||||||
|
mask.ClipContents = true;
|
||||||
|
|
||||||
|
var viewport = new Control { Name = "Viewport" };
|
||||||
|
mask.AddChild(viewport);
|
||||||
|
|
||||||
|
var gridContainer = new GridContainer { Name = "RelicContainer" };
|
||||||
|
viewport.AddChild(gridContainer);
|
||||||
|
|
||||||
|
var scrollbar = PreloadManager.Cache.GetScene(SceneHelper.GetScenePath("ui/scrollbar"))
|
||||||
|
.Instantiate<NScrollbar>();
|
||||||
|
scrollContainer.AddChild(scrollbar);
|
||||||
|
|
||||||
|
container.AddChild(scrollContainer);
|
||||||
|
|
||||||
|
scrollContainer.SetAnchorsPreset(Control.LayoutPreset.FullRect);
|
||||||
|
scrollContainer.OffsetLeft = -510;
|
||||||
|
scrollContainer.OffsetTop = -250;
|
||||||
|
scrollContainer.OffsetRight = 510;
|
||||||
|
scrollContainer.OffsetBottom = 250;
|
||||||
|
|
||||||
|
mask.SetAnchorsPreset(Control.LayoutPreset.CenterTop);
|
||||||
|
mask.OffsetLeft = -450;
|
||||||
|
mask.OffsetTop = 250;
|
||||||
|
mask.OffsetRight = 450;
|
||||||
|
mask.OffsetBottom = 830;
|
||||||
|
|
||||||
|
viewport.SetAnchorsPreset(Control.LayoutPreset.TopWide);
|
||||||
|
viewport.OffsetLeft = 110;
|
||||||
|
viewport.OffsetTop = 110;
|
||||||
|
viewport.OffsetRight = -120;
|
||||||
|
viewport.OffsetBottom = 580;
|
||||||
|
|
||||||
|
scrollbar.AnchorLeft = 0.794f;
|
||||||
|
scrollbar.AnchorTop = 0.187f;
|
||||||
|
scrollbar.AnchorRight = 0.818f;
|
||||||
|
scrollbar.AnchorBottom = 0.947f;
|
||||||
|
scrollbar.OffsetLeft = -200;
|
||||||
|
scrollbar.OffsetTop = 158;
|
||||||
|
scrollbar.OffsetRight = -199;
|
||||||
|
scrollbar.OffsetBottom = -163;
|
||||||
|
|
||||||
|
gridContainer.SetAnchorsAndOffsetsPreset(Control.LayoutPreset.TopWide);
|
||||||
|
|
||||||
|
gridContainer.Columns = 4;
|
||||||
|
gridContainer.CustomMinimumSize = new Vector2(0, 469);
|
||||||
|
|
||||||
|
gridContainer.AddThemeConstantOverride("h_separation", 42);
|
||||||
|
gridContainer.AddThemeConstantOverride("v_separation", 42);
|
||||||
|
|
||||||
|
scrollContainer.SetContent(gridContainer, 20, 30);
|
||||||
|
|
||||||
|
//The animation will change MultiplayerHolder position. Manually fix the position.
|
||||||
|
|
||||||
|
var position = new Vector2(0, 110);
|
||||||
|
|
||||||
|
for (var i = 0; i < ____multiplayerHolders.Count; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
position = i % 4 == 0
|
||||||
|
? new Vector2(0, position.Y + 178)
|
||||||
|
: new Vector2(position.X + 178, position.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
var multiplayerHolder = ____multiplayerHolders[i];
|
||||||
|
multiplayerHolder.Position = position;
|
||||||
|
multiplayerHolder.Reparent(gridContainer, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user