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()
|
||||
{
|
||||
TextSubmitted += OnTextSubmitted;
|
||||
TextChanged += OnTextChanged;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
addressInfo.IsValid = true;
|
||||
addressInfo.Address = ipEndPoint.Address.ToString();
|
||||
addressInfo.Port = (ushort)ipEndPoint.Port;
|
||||
}
|
||||
|
||||
return addressInfo;
|
||||
}
|
||||
|
||||
private void OnTextSubmitted(string newText)
|
||||
private void OnTextChanged(string newText)
|
||||
{
|
||||
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.Runs;
|
||||
using MegaCrit.Sts2.Core.Saves;
|
||||
using MegaCrit.Sts2.Core.Saves.Managers;
|
||||
using MegaCrit.Sts2.Core.Saves.Migrations;
|
||||
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
@@ -23,6 +25,9 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
||||
public static string CurrentMultiplayerRunSavePath =>
|
||||
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 ReadSaveResult<SerializableRun> LoadAndCanonicalizeMultiplayerRunSave(ulong localPlayerId)
|
||||
@@ -33,6 +38,14 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
catch (Exception value)
|
||||
@@ -76,6 +89,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
||||
public static void DeleteCurrentMultiplayerRun()
|
||||
{
|
||||
SaveStore.DeleteFile(CurrentMultiplayerRunSavePath);
|
||||
SaveStore.DeleteFile(CurrentMultiplayerRunPlayerNamesPath);
|
||||
}
|
||||
|
||||
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 MegaCrit.Sts2.Core.Saves;
|
||||
using Steamworks;
|
||||
|
||||
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("ip_address")] public string IPAddress { get; set; } = "127.0.0.1";
|
||||
[JsonPropertyName("net_id")] public ulong NetId { get; set; } = 1000u;
|
||||
[JsonPropertyName("player_name")] public string PlayerName { get; set; } = SteamFriends.GetPersonaName();
|
||||
|
||||
public int SchemaVersion { get; set; }
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ using Logger = MegaCrit.Sts2.Core.Logging.Logger;
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.ENet
|
||||
{
|
||||
[HarmonyPatch(typeof(ENetClient), "ConnectToHost")]
|
||||
internal class ENetClientConnectToHostPatch
|
||||
@@ -132,7 +132,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
if (eNetHandshakeResponse.status == ENetHandshakeStatus.IdCollision)
|
||||
{
|
||||
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),
|
||||
eNetHandshakeResponse.newNetId);
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ using Logger = MegaCrit.Sts2.Core.Logging.Logger;
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.ENet
|
||||
{
|
||||
[HarmonyPatch(typeof(ENetHost), "DoClientHandshake")]
|
||||
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
|
||||
{
|
||||
[HarmonyPatch(typeof(RunManager), "CleanUp")]
|
||||
internal class RunManagerPatch
|
||||
internal class RunManagerCleanUpPatch
|
||||
{
|
||||
private static void Postfix()
|
||||
{
|
||||
LanMapDrawingsHelper.DisableDrawingHashSet.Clear();
|
||||
LanPlayerNameHelper.SetDefaultPlayerNameDictionary();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using MegaCrit.Sts2.Core.Runs;
|
||||
using MegaCrit.Sts2.Core.Saves;
|
||||
using MegaCrit.Sts2.Core.Saves.Managers;
|
||||
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
@@ -35,8 +36,12 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
return;
|
||||
|
||||
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
|
||||
: Traverse.Create(runSaveManager).Property("CurrentMultiplayerRunSavePath").GetValue<string>()
|
||||
: Traverse.Create(runSaveManager).Property("CurrentRunSavePath").GetValue<string>();
|
||||
@@ -55,6 +60,25 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
stream.Seek(0L, SeekOrigin.Begin);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||
{
|
||||
[HarmonyPatch(typeof(NJoinFriendScreen), "_Ready")]
|
||||
internal class NJoinFriendScreenPatch
|
||||
internal class NJoinFriendScreenReadyPatch
|
||||
{
|
||||
private static void Prefix(NJoinFriendScreen __instance)
|
||||
{
|
||||
@@ -28,11 +28,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
lanPanel.PatchMarginLeft = 12;
|
||||
lanPanel.PatchMarginRight = 12;
|
||||
|
||||
lanPanel.AnchorLeft = 0.5f;
|
||||
lanPanel.AnchorTop = 0.5f;
|
||||
lanPanel.AnchorRight = 0.5f;
|
||||
lanPanel.AnchorBottom = 0.5f;
|
||||
|
||||
lanPanel.SetAnchorsPreset(Control.LayoutPreset.Center);
|
||||
lanPanel.OffsetLeft = 450;
|
||||
lanPanel.OffsetTop = -338;
|
||||
lanPanel.OffsetRight = 790;
|
||||
@@ -49,8 +45,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
lanPanel.AddChild(vBoxContainer);
|
||||
|
||||
vBoxContainer.Alignment = BoxContainer.AlignmentMode.Center;
|
||||
vBoxContainer.SetAnchorsPreset(Control.LayoutPreset.FullRect);
|
||||
vBoxContainer.Size = new Vector2(350, 676);
|
||||
vBoxContainer.SetAnchorsAndOffsetsPreset(Control.LayoutPreset.FullRect);
|
||||
vBoxContainer.AddThemeConstantOverride("separation", 24);
|
||||
|
||||
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 UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||
{
|
||||
[HarmonyPatch(typeof(NMapDrawings), "CreateLineForPlayer")]
|
||||
internal class NMapDrawingsPatch
|
||||
internal class NMapDrawingsCreateLineForPlayerPatch
|
||||
{
|
||||
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 UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||
{
|
||||
[HarmonyPatch(typeof(NMultiplayerPlayerExpandedState), "_Ready")]
|
||||
internal class NMultiplayerPlayerExpandedStatePatch
|
||||
internal class NMultiplayerPlayerExpandedStateReadyPatch
|
||||
{
|
||||
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 UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||
{
|
||||
[HarmonyPatch(typeof(NMultiplayerSubmenu), "_Ready")]
|
||||
internal class NMultiplayerSubmenuReadyPatch
|
||||
+2
-2
@@ -6,10 +6,10 @@ using MegaCrit.Sts2.Core.Runs;
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||
{
|
||||
[HarmonyPatch(typeof(NRestSiteRoom), "_Ready")]
|
||||
internal class NRestSiteRoomPatch
|
||||
internal class NRestSiteRoomReadyPatch
|
||||
{
|
||||
private static void Prefix(NRestSiteRoom __instance, IRunState ____runState,
|
||||
List<Control> ____characterContainers)
|
||||
+65
-15
@@ -1,21 +1,23 @@
|
||||
using Godot;
|
||||
using HarmonyLib;
|
||||
using MegaCrit.Sts2.Core.Nodes.Screens.Settings;
|
||||
using SlayTheSpire2.LAN.Multiplayer.Components;
|
||||
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable UnusedType.Global
|
||||
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||
{
|
||||
[HarmonyPatch(typeof(NSettingsScreen), "_Ready")]
|
||||
internal class NSettingsScreenPatch
|
||||
internal class NSettingsScreenReadyPatch
|
||||
{
|
||||
private static void Prefix(NSettingsScreen __instance)
|
||||
{
|
||||
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 &&
|
||||
moddingNode.Duplicate() is MarginContainer hostPort &&
|
||||
@@ -23,8 +25,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
{
|
||||
hostPortDivider.Name = "HostPortDivider";
|
||||
|
||||
vBoxContainer.AddChild(hostPortDivider);
|
||||
vBoxContainer.MoveChild(hostPortDivider, moddingNode.GetIndex() + 1);
|
||||
vBoxContainerNode.AddChild(hostPortDivider);
|
||||
vBoxContainerNode.MoveChild(hostPortDivider, moddingNode.GetIndex() + 1);
|
||||
|
||||
hostPortDivider.Show();
|
||||
|
||||
@@ -32,8 +34,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
|
||||
hostPort.RemoveChild(hostPort.GetNode("ModdingButton"));
|
||||
|
||||
vBoxContainer.AddChild(hostPort);
|
||||
vBoxContainer.MoveChild(hostPort, hostPortDivider.GetIndex() + 1);
|
||||
vBoxContainerNode.AddChild(hostPort);
|
||||
vBoxContainerNode.MoveChild(hostPort, hostPortDivider.GetIndex() + 1);
|
||||
|
||||
hostPort.Show();
|
||||
|
||||
@@ -64,8 +66,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
{
|
||||
hostMaxPlayersDivider.Name = "HostMaxPlayersDivider";
|
||||
|
||||
vBoxContainer.AddChild(hostMaxPlayersDivider);
|
||||
vBoxContainer.MoveChild(hostMaxPlayersDivider, moddingNode.GetIndex() + 1);
|
||||
vBoxContainerNode.AddChild(hostMaxPlayersDivider);
|
||||
vBoxContainerNode.MoveChild(hostMaxPlayersDivider, moddingNode.GetIndex() + 1);
|
||||
|
||||
hostMaxPlayersDivider.Show();
|
||||
|
||||
@@ -73,8 +75,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
|
||||
hostMaxPlayers.RemoveChild(hostMaxPlayers.GetNode("ModdingButton"));
|
||||
|
||||
vBoxContainer.AddChild(hostMaxPlayers);
|
||||
vBoxContainer.MoveChild(hostMaxPlayers, hostMaxPlayersDivider.GetIndex() + 1);
|
||||
vBoxContainerNode.AddChild(hostMaxPlayers);
|
||||
vBoxContainerNode.MoveChild(hostMaxPlayers, hostMaxPlayersDivider.GetIndex() + 1);
|
||||
|
||||
hostMaxPlayers.Show();
|
||||
|
||||
@@ -98,14 +100,57 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
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 &&
|
||||
moddingNode.Duplicate() is MarginContainer netId &&
|
||||
netId.GetNode("Label") is RichTextLabel netIdLabel)
|
||||
{
|
||||
netIdDivider.Name = "NetIDDivider";
|
||||
|
||||
vBoxContainer.AddChild(netIdDivider);
|
||||
vBoxContainer.MoveChild(netIdDivider, moddingNode.GetIndex() + 1);
|
||||
vBoxContainerNode.AddChild(netIdDivider);
|
||||
vBoxContainerNode.MoveChild(netIdDivider, moddingNode.GetIndex() + 1);
|
||||
|
||||
netIdDivider.Show();
|
||||
|
||||
@@ -113,8 +158,8 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
|
||||
netId.RemoveChild(netId.GetNode("ModdingButton"));
|
||||
|
||||
vBoxContainer.AddChild(netId);
|
||||
vBoxContainer.MoveChild(netId, netIdDivider.GetIndex() + 1);
|
||||
vBoxContainerNode.AddChild(netId);
|
||||
vBoxContainerNode.MoveChild(netId, netIdDivider.GetIndex() + 1);
|
||||
|
||||
netId.Show();
|
||||
|
||||
@@ -138,6 +183,11 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||
|
||||
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