Compare commits
11
Commits
36aea85870
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3283b66044 | ||
|
|
c0d9e66fc8 | ||
|
|
79876ed68b | ||
|
|
047318373a | ||
|
|
32b55c42be | ||
|
|
a2b2351398 | ||
|
|
301b0aecf3 | ||
|
|
1bf1e32b3d | ||
|
|
309087a8f4 | ||
|
|
2fbfa5a6d6 | ||
|
|
ddb56fd903 |
@@ -1,3 +1,4 @@
|
|||||||
|
./sts2_src
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Components
|
|||||||
{
|
{
|
||||||
var locString = new LocString("main_menu_ui", LocKeyPrefix);
|
var locString = new LocString("main_menu_ui", LocKeyPrefix);
|
||||||
SetTextAutoSize(locString.GetFormattedText());
|
SetTextAutoSize(locString.GetFormattedText());
|
||||||
this.ApplyLocaleFontSubstitution(FontType.Regular, ThemeConstants.Label.font);
|
this.ApplyLocaleFontSubstitution(FontType.Regular, ThemeConstants.Label.Font);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Components
|
|||||||
{
|
{
|
||||||
var locString = new LocString("main_menu_ui", _locKeyPrefix);
|
var locString = new LocString("main_menu_ui", _locKeyPrefix);
|
||||||
SetTextAutoSize(locString.GetFormattedText());
|
SetTextAutoSize(locString.GetFormattedText());
|
||||||
this.ApplyLocaleFontSubstitution(FontType.Regular, ThemeConstants.Label.font);
|
this.ApplyLocaleFontSubstitution(FontType.Regular, ThemeConstants.Label.Font);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Godot;
|
using System.Reflection;
|
||||||
|
using Godot;
|
||||||
|
using HarmonyLib;
|
||||||
using MegaCrit.Sts2.Core.Daily;
|
using MegaCrit.Sts2.Core.Daily;
|
||||||
using MegaCrit.Sts2.Core.Entities.Multiplayer;
|
using MegaCrit.Sts2.Core.Entities.Multiplayer;
|
||||||
using MegaCrit.Sts2.Core.Helpers;
|
using MegaCrit.Sts2.Core.Helpers;
|
||||||
@@ -24,6 +26,15 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
|||||||
{
|
{
|
||||||
internal class LanHostHelper
|
internal class LanHostHelper
|
||||||
{
|
{
|
||||||
|
private static readonly Action<NMultiplayerSubmenu> UpdateButtons;
|
||||||
|
|
||||||
|
static LanHostHelper()
|
||||||
|
{
|
||||||
|
UpdateButtons = AccessTools.MethodDelegate<Action<NMultiplayerSubmenu>>(
|
||||||
|
typeof(NMultiplayerSubmenu).GetMethod("UpdateButtons",
|
||||||
|
BindingFlags.Instance | BindingFlags.NonPublic)!);
|
||||||
|
}
|
||||||
|
|
||||||
public static void StartHost(GameMode gameMode, Control loadingOverlay, NSubmenuStack stack, ushort port,
|
public static void StartHost(GameMode gameMode, Control loadingOverlay, NSubmenuStack stack, ushort port,
|
||||||
int maxPlayers)
|
int maxPlayers)
|
||||||
{
|
{
|
||||||
@@ -163,7 +174,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task TryAbandonMultiplayerRun(Action updateButtons)
|
public static async Task TryAbandonMultiplayerRun(NMultiplayerSubmenu instance)
|
||||||
{
|
{
|
||||||
var header = new LocString("main_menu_ui", "ABANDON_RUN_CONFIRMATION.header");
|
var header = new LocString("main_menu_ui", "ABANDON_RUN_CONFIRMATION.header");
|
||||||
var body = new LocString("main_menu_ui", "ABANDON_RUN_CONFIRMATION.body");
|
var body = new LocString("main_menu_ui", "ABANDON_RUN_CONFIRMATION.body");
|
||||||
@@ -209,7 +220,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
LanRunSaveManagerService.Instance.DeleteCurrentMultiplayerRun();
|
LanRunSaveManagerService.Instance.DeleteCurrentMultiplayerRun();
|
||||||
updateButtons();
|
UpdateButtons(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using MegaCrit.Sts2.Core.Debug;
|
||||||
|
using MegaCrit.Sts2.Core.Logging;
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Helpers
|
||||||
|
{
|
||||||
|
internal static class TaskGenericHelper
|
||||||
|
{
|
||||||
|
public static Task<T> RunSafely<T>(Task<T> task)
|
||||||
|
{
|
||||||
|
return LogTaskExceptions(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<T> LogTaskExceptions<T>(Task<T> task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await task;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (ex is not TaskCanceledException)
|
||||||
|
{
|
||||||
|
Log.Error(ex.ToString());
|
||||||
|
SentryService.CaptureException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Models
|
|||||||
public string playerName;
|
public string playerName;
|
||||||
|
|
||||||
public bool ShouldBroadcast => false;
|
public bool ShouldBroadcast => false;
|
||||||
|
public bool ShouldBuffer => false;
|
||||||
public NetTransferMode Mode => NetTransferMode.Reliable;
|
public NetTransferMode Mode => NetTransferMode.Reliable;
|
||||||
public LogLevel LogLevel => LogLevel.Info;
|
public LogLevel LogLevel => LogLevel.Info;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using MegaCrit.Sts2.Core.Logging;
|
using MegaCrit.Sts2.Core.Logging;
|
||||||
using MegaCrit.Sts2.Core.Multiplayer.Serialization;
|
using MegaCrit.Sts2.Core.Multiplayer.Serialization;
|
||||||
using MegaCrit.Sts2.Core.Multiplayer.Transport;
|
using MegaCrit.Sts2.Core.Multiplayer.Transport;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Models
|
namespace SlayTheSpire2.LAN.Multiplayer.Models
|
||||||
{
|
{
|
||||||
@@ -9,12 +10,13 @@ namespace SlayTheSpire2.LAN.Multiplayer.Models
|
|||||||
public PlayerNames playerNames;
|
public PlayerNames playerNames;
|
||||||
|
|
||||||
public bool ShouldBroadcast => false;
|
public bool ShouldBroadcast => false;
|
||||||
|
public bool ShouldBuffer => false;
|
||||||
public NetTransferMode Mode => NetTransferMode.Reliable;
|
public NetTransferMode Mode => NetTransferMode.Reliable;
|
||||||
public LogLevel LogLevel => LogLevel.Info;
|
public LogLevel LogLevel => LogLevel.Info;
|
||||||
|
|
||||||
public void Serialize(PacketWriter writer)
|
public void Serialize(PacketWriter writer)
|
||||||
{
|
{
|
||||||
writer.WriteInt(playerNames.Count);
|
PacketHelper.WriteVarInt(writer, (uint)playerNames.Count);
|
||||||
foreach (var keyValue in playerNames)
|
foreach (var keyValue in playerNames)
|
||||||
{
|
{
|
||||||
writer.WriteULong(keyValue.Key);
|
writer.WriteULong(keyValue.Key);
|
||||||
@@ -24,7 +26,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Models
|
|||||||
|
|
||||||
public void Deserialize(PacketReader reader)
|
public void Deserialize(PacketReader reader)
|
||||||
{
|
{
|
||||||
var count = reader.ReadInt();
|
var count = PacketHelper.ReadVarInt(reader);
|
||||||
playerNames = new PlayerNames();
|
playerNames = new PlayerNames();
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Context;
|
||||||
|
using MegaCrit.Sts2.Core.Helpers;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game.Lobby;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Messages.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Runs;
|
||||||
|
using MegaCrit.Sts2.Core.Saves.Runs;
|
||||||
|
using Logger = MegaCrit.Sts2.Core.Logging.Logger;
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
|
||||||
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||||
|
{
|
||||||
|
[HarmonyPatch(typeof(CombatStateSynchronizer), "WaitForSync")]
|
||||||
|
internal class CombatStateSynchronizerWaitForSyncPatch
|
||||||
|
{
|
||||||
|
private static bool Prefix(CombatStateSynchronizer __instance, Logger ____logger,
|
||||||
|
INetGameService ____netService, TaskCompletionSource? ____syncCompletionSource,
|
||||||
|
Dictionary<ulong, SerializablePlayer> ____syncData, RunState ____runState, RunLobby? ____runLobby,
|
||||||
|
SerializableRunRngSet? ____rngSet, SerializableRelicGrabBag? ____sharedRelicGrabBag, ref Task __result)
|
||||||
|
{
|
||||||
|
//Whether is LAN game was not checked, because the sync issue may also occur when connect via Steam
|
||||||
|
|
||||||
|
__result = TaskHelper.RunSafely(WaitForSync(__instance, ____logger, ____netService,
|
||||||
|
____syncCompletionSource, ____syncData, ____runState, ____runLobby, ____rngSet,
|
||||||
|
____sharedRelicGrabBag));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task WaitForSync(CombatStateSynchronizer instance, Logger logger,
|
||||||
|
INetGameService netService, TaskCompletionSource? syncCompletionSource,
|
||||||
|
Dictionary<ulong, SerializablePlayer> syncData, RunState runState, RunLobby? runLobby,
|
||||||
|
SerializableRunRngSet? rngSet, SerializableRelicGrabBag? sharedRelicGrabBag)
|
||||||
|
{
|
||||||
|
logger.Debug("Waiting to receive all sync messages from all clients");
|
||||||
|
if (netService.Type == NetGameType.Singleplayer || instance.IsDisabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (syncCompletionSource == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("StartSync must be called before WaitForSync!");
|
||||||
|
}
|
||||||
|
|
||||||
|
var startTime = DateTime.Now;
|
||||||
|
var lastResendTick = DateTime.Now;
|
||||||
|
|
||||||
|
const int timeoutSeconds = 30;
|
||||||
|
const int resendIntervalSeconds = 5;
|
||||||
|
|
||||||
|
while (!syncCompletionSource.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
if ((DateTime.Now - startTime).TotalSeconds > timeoutSeconds)
|
||||||
|
{
|
||||||
|
logger.Warn("Receive all sync messages timeout, skip waiting for all clients");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (netService.Type == NetGameType.Host &&
|
||||||
|
(DateTime.Now - lastResendTick).TotalSeconds > resendIntervalSeconds && rngSet != null &&
|
||||||
|
sharedRelicGrabBag != null)
|
||||||
|
{
|
||||||
|
logger.Debug("Resend rng sync message");
|
||||||
|
|
||||||
|
var message = new SyncRngMessage
|
||||||
|
{
|
||||||
|
rng = rngSet,
|
||||||
|
sharedRelicGrabBag = sharedRelicGrabBag
|
||||||
|
};
|
||||||
|
|
||||||
|
netService.SendMessage(message);
|
||||||
|
|
||||||
|
lastResendTick = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var syncDatum in syncData)
|
||||||
|
{
|
||||||
|
if (runLobby != null && !runLobby.ConnectedPlayerIds.Contains(syncDatum.Key))
|
||||||
|
{
|
||||||
|
logger.Debug($"Skipping sync for disconnected player {syncDatum.Key}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = runState.GetPlayer(syncDatum.Key);
|
||||||
|
if (!LocalContext.IsMe(player))
|
||||||
|
{
|
||||||
|
player?.SyncWithSerializedPlayer(syncDatum.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (netService.Type != NetGameType.Host)
|
||||||
|
{
|
||||||
|
if (rngSet != null)
|
||||||
|
{
|
||||||
|
runState.Rng.LoadFromSerializable(rngSet);
|
||||||
|
}
|
||||||
|
else if (runState.Players.Count > 1)
|
||||||
|
{
|
||||||
|
logger.Error(
|
||||||
|
"There are two or more players and we are a client, but we never received the RNG set!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedRelicGrabBag != null)
|
||||||
|
{
|
||||||
|
runState.SharedRelicGrabBag.LoadFromSerializable(sharedRelicGrabBag);
|
||||||
|
}
|
||||||
|
else if (runState.Players.Count > 1)
|
||||||
|
{
|
||||||
|
logger.Error(
|
||||||
|
"There are two or more players and we are a client, but we never received the shared relic grab bag!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncData.Clear();
|
||||||
|
var traverse = Traverse.Create(instance);
|
||||||
|
traverse.Field("_rngSet").SetValue(null);
|
||||||
|
traverse.Field("_sharedRelicGrabBag").SetValue(null);
|
||||||
|
traverse.Field("_syncCompletionSource").SetValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using Godot;
|
using Godot;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using MegaCrit.Sts2.Core.Entities.Multiplayer;
|
using MegaCrit.Sts2.Core.Entities.Multiplayer;
|
||||||
using MegaCrit.Sts2.Core.Helpers;
|
|
||||||
using MegaCrit.Sts2.Core.Multiplayer.Transport;
|
using MegaCrit.Sts2.Core.Multiplayer.Transport;
|
||||||
using MegaCrit.Sts2.Core.Multiplayer.Transport.ENet;
|
using MegaCrit.Sts2.Core.Multiplayer.Transport.ENet;
|
||||||
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
@@ -23,9 +22,10 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs.ENet
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool Prefix(ENetClient __instance, ulong netId, string ip, ushort port,
|
private static bool Prefix(ENetClient __instance, ulong netId, string ip, ushort port,
|
||||||
CancellationToken cancelToken, Logger ____logger, INetClientHandler ____handler, ref Task __result)
|
CancellationToken cancelToken, Logger ____logger, INetClientHandler ____handler,
|
||||||
|
ref Task<NetErrorInfo?> __result)
|
||||||
{
|
{
|
||||||
__result = TaskHelper.RunSafely(ConnectToHost(__instance, ____logger, ____handler, netId, ip, port,
|
__result = TaskGenericHelper.RunSafely(ConnectToHost(__instance, ____logger, ____handler, netId, ip, port,
|
||||||
cancelToken));
|
cancelToken));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using MegaCrit.Sts2.Core.Multiplayer.Connection;
|
|||||||
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
using MegaCrit.Sts2.Core.Multiplayer.Messages.Lobby;
|
using MegaCrit.Sts2.Core.Multiplayer.Messages.Lobby;
|
||||||
using MegaCrit.Sts2.Core.Platform;
|
using MegaCrit.Sts2.Core.Platform;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
using SlayTheSpire2.LAN.Multiplayer.Models;
|
using SlayTheSpire2.LAN.Multiplayer.Models;
|
||||||
using SlayTheSpire2.LAN.Multiplayer.Services;
|
using SlayTheSpire2.LAN.Multiplayer.Services;
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
{
|
{
|
||||||
private static void Postfix(JoinFlow __instance, ref Task<ClientLobbyJoinResponseMessage> __result)
|
private static void Postfix(JoinFlow __instance, ref Task<ClientLobbyJoinResponseMessage> __result)
|
||||||
{
|
{
|
||||||
__result = AttemptJoin(__instance, __result);
|
__result = TaskGenericHelper.RunSafely(AttemptJoin(__instance, __result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ClientLobbyJoinResponseMessage> AttemptJoin(JoinFlow joinFlow,
|
private static async Task<ClientLobbyJoinResponseMessage> AttemptJoin(JoinFlow joinFlow,
|
||||||
@@ -44,7 +45,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
{
|
{
|
||||||
private static void Postfix(JoinFlow __instance, ref Task<ClientLoadJoinResponseMessage> __result)
|
private static void Postfix(JoinFlow __instance, ref Task<ClientLoadJoinResponseMessage> __result)
|
||||||
{
|
{
|
||||||
__result = AttemptLoadJoin(__instance, __result);
|
__result = TaskGenericHelper.RunSafely(AttemptLoadJoin(__instance, __result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ClientLoadJoinResponseMessage> AttemptLoadJoin(JoinFlow joinFlow,
|
private static async Task<ClientLoadJoinResponseMessage> AttemptLoadJoin(JoinFlow joinFlow,
|
||||||
@@ -71,7 +72,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
{
|
{
|
||||||
private static void Postfix(JoinFlow __instance, ref Task<ClientRejoinResponseMessage> __result)
|
private static void Postfix(JoinFlow __instance, ref Task<ClientRejoinResponseMessage> __result)
|
||||||
{
|
{
|
||||||
__result = AttemptRejoin(__instance, __result);
|
__result = TaskGenericHelper.RunSafely(AttemptRejoin(__instance, __result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ClientRejoinResponseMessage> AttemptRejoin(JoinFlow joinFlow,
|
private static async Task<ClientRejoinResponseMessage> AttemptRejoin(JoinFlow joinFlow,
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Achievements;
|
||||||
|
using MegaCrit.Sts2.Core.Context;
|
||||||
|
using MegaCrit.Sts2.Core.Daily;
|
||||||
|
using MegaCrit.Sts2.Core.Entities.Players;
|
||||||
|
using MegaCrit.Sts2.Core.Helpers;
|
||||||
|
using MegaCrit.Sts2.Core.Models;
|
||||||
|
using MegaCrit.Sts2.Core.Multiplayer.Game;
|
||||||
|
using MegaCrit.Sts2.Core.Platform;
|
||||||
|
using MegaCrit.Sts2.Core.Rooms;
|
||||||
using MegaCrit.Sts2.Core.Runs;
|
using MegaCrit.Sts2.Core.Runs;
|
||||||
|
using MegaCrit.Sts2.Core.Runs.Metrics;
|
||||||
|
using MegaCrit.Sts2.Core.Saves;
|
||||||
using SlayTheSpire2.LAN.Multiplayer.Services;
|
using SlayTheSpire2.LAN.Multiplayer.Services;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
@@ -16,4 +27,137 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
|||||||
LanPlayerNameService.Instance.SetDefaultPlayerNames();
|
LanPlayerNameService.Instance.SetDefaultPlayerNames();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(RunManager), "OnEnded")]
|
||||||
|
internal class RunManagerOnEndedPatch
|
||||||
|
{
|
||||||
|
[HarmonyReversePatch]
|
||||||
|
[HarmonyPatch(typeof(RunManager), "UpdatePlayerStatsInMapPointHistory")]
|
||||||
|
private static void UpdatePlayerStatsInMapPointHistory(RunManager instance)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyReversePatch]
|
||||||
|
[HarmonyPatch(typeof(RunManager), "CheckUpdateEnemyDiscoveryAfterLoss")]
|
||||||
|
private static void CheckUpdateEnemyDiscoveryAfterLoss(Player player, ModelId monster)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool Prefix(RunManager __instance, bool isVictory, ref bool ____runHistoryWasUploaded,
|
||||||
|
ref SerializableRun __result)
|
||||||
|
{
|
||||||
|
if (__instance.NetService.Platform == PlatformType.None)
|
||||||
|
{
|
||||||
|
UpdatePlayerStatsInMapPointHistory(__instance);
|
||||||
|
var state = Traverse.Create(__instance).Property("State").GetValue<RunState?>();
|
||||||
|
if (state == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"{nameof(state)} is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
var me = LocalContext.GetMe(state);
|
||||||
|
if (me == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"{nameof(me)} is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state is { CurrentRoom: CombatRoom combatRoom, CurrentMapPointHistoryEntry: not null })
|
||||||
|
{
|
||||||
|
state.CurrentMapPointHistoryEntry.Rooms.Last().TurnsTaken = combatRoom.CombatState.RoundNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
var serializableRun = __instance.ToSave(null);
|
||||||
|
var me2 = LocalContext.GetMe(serializableRun);
|
||||||
|
if (me2 == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"{nameof(me2)} is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (____runHistoryWasUploaded)
|
||||||
|
{
|
||||||
|
__result = serializableRun;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
____runHistoryWasUploaded = true;
|
||||||
|
if (!isVictory && state.CurrentRoom is CombatRoom combatRoom2)
|
||||||
|
{
|
||||||
|
foreach (var monstersWithSlot in combatRoom2.Encounter.MonstersWithSlots)
|
||||||
|
{
|
||||||
|
var item = monstersWithSlot.Item1;
|
||||||
|
CheckUpdateEnemyDiscoveryAfterLoss(me, item.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__instance.ShouldSave)
|
||||||
|
{
|
||||||
|
using (SaveManager.Instance.BeginSaveBatch())
|
||||||
|
{
|
||||||
|
SaveManager.Instance.UpdateProgressWithRunData(serializableRun, isVictory);
|
||||||
|
foreach (var discoveredEpoch in me2.DiscoveredEpochs)
|
||||||
|
{
|
||||||
|
if (!me.DiscoveredEpochs.Contains(discoveredEpoch))
|
||||||
|
{
|
||||||
|
me.DiscoveredEpochs.Add(discoveredEpoch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AchievementsHelper.AfterRunEnded(state, me, isVictory);
|
||||||
|
RunHistoryUtilities.CreateRunHistoryEntry(serializableRun, isVictory, __instance.IsAbandoned,
|
||||||
|
__instance.NetService.Platform);
|
||||||
|
MetricUtilities.UploadRunMetrics(serializableRun, isVictory, __instance.NetService.NetId);
|
||||||
|
if (SaveManager.Instance.Progress.NumberOfRuns == 5)
|
||||||
|
{
|
||||||
|
MetricUtilities.UploadSettingsMetric();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (__instance.NetService.Type)
|
||||||
|
{
|
||||||
|
case NetGameType.Singleplayer:
|
||||||
|
SaveManager.Instance.DeleteCurrentRun();
|
||||||
|
break;
|
||||||
|
case NetGameType.Host:
|
||||||
|
LanRunSaveManagerService.Instance.DeleteCurrentMultiplayerRun();
|
||||||
|
break;
|
||||||
|
case NetGameType.None:
|
||||||
|
case NetGameType.Client:
|
||||||
|
case NetGameType.Replay:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVictory)
|
||||||
|
{
|
||||||
|
var score = ScoreUtility.CalculateScore(serializableRun, isVictory);
|
||||||
|
StatsManager.IncrementArchitectDamage(score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__instance.DailyTime.HasValue)
|
||||||
|
{
|
||||||
|
var type = __instance.NetService.Type;
|
||||||
|
if ((uint)(type - 1) <= 1u)
|
||||||
|
{
|
||||||
|
var score2 = ScoreUtility.CalculateScore(serializableRun, isVictory);
|
||||||
|
TaskHelper.RunSafely(DailyRunUtility.UploadScore(__instance.DailyTime.Value, score2,
|
||||||
|
serializableRun.Players));
|
||||||
|
}
|
||||||
|
else if (__instance.NetService.Type == NetGameType.Client)
|
||||||
|
{
|
||||||
|
TaskHelper.RunSafely(DailyRunUtility.UploadScore(__instance.DailyTime.Value, -99999,
|
||||||
|
serializableRun.Players));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__result = serializableRun;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ using SlayTheSpire2.LAN.Multiplayer.Services;
|
|||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs
|
||||||
{
|
{
|
||||||
[HarmonyPatch(typeof(RunSaveManager), "SaveRun")]
|
[HarmonyPatch(typeof(RunSaveManager), "SaveRun", new[] { typeof(AbstractRoom) })]
|
||||||
internal class RunSaveManagerSaveRunPatch
|
internal class RunSaveManagerSaveRunPatch
|
||||||
{
|
{
|
||||||
private static bool Prefix(RunSaveManager __instance, AbstractRoom? preFinishedRoom, bool ____forceSynchronous,
|
private static bool Prefix(RunSaveManager __instance, AbstractRoom? preFinishedRoom, bool ____forceSynchronous,
|
||||||
|
|||||||
+6
-3
@@ -3,6 +3,7 @@ using HarmonyLib;
|
|||||||
using MegaCrit.Sts2.Core.Assets;
|
using MegaCrit.Sts2.Core.Assets;
|
||||||
using MegaCrit.Sts2.Core.Entities.Players;
|
using MegaCrit.Sts2.Core.Entities.Players;
|
||||||
using MegaCrit.Sts2.Core.Helpers;
|
using MegaCrit.Sts2.Core.Helpers;
|
||||||
|
using MegaCrit.Sts2.Core.Localization;
|
||||||
using MegaCrit.Sts2.Core.Nodes.Multiplayer;
|
using MegaCrit.Sts2.Core.Nodes.Multiplayer;
|
||||||
using MegaCrit.Sts2.Core.Nodes.Screens.CardLibrary;
|
using MegaCrit.Sts2.Core.Nodes.Screens.CardLibrary;
|
||||||
using MegaCrit.Sts2.Core.Nodes.Screens.Map;
|
using MegaCrit.Sts2.Core.Nodes.Screens.Map;
|
||||||
@@ -36,16 +37,18 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
|||||||
|
|
||||||
disableDrawing.Name = "DisableDrawing";
|
disableDrawing.Name = "DisableDrawing";
|
||||||
disableDrawing.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter;
|
disableDrawing.SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter;
|
||||||
disableDrawing.SetLabel("Disable drawing");
|
|
||||||
|
|
||||||
var marginContainer = (MarginContainer)container.GetNode("MarginContainer");
|
var marginContainer = (MarginContainer)container.GetNode("MarginContainer");
|
||||||
marginContainer.RemoveThemeConstantOverride("margin_top");
|
marginContainer.RemoveThemeConstantOverride("margin_top");
|
||||||
|
|
||||||
var lanMapDrawingsService = LanMapDrawingsService.Instance;
|
|
||||||
|
|
||||||
container.AddChildSafely(disableDrawing);
|
container.AddChildSafely(disableDrawing);
|
||||||
container.MoveChild(disableDrawing, 0);
|
container.MoveChild(disableDrawing, 0);
|
||||||
|
|
||||||
|
disableDrawing.SetLabel(new LocString("gameplay_ui", "SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING")
|
||||||
|
.GetFormattedText());
|
||||||
|
|
||||||
|
var lanMapDrawingsService = LanMapDrawingsService.Instance;
|
||||||
|
|
||||||
disableDrawing.IsTicked =
|
disableDrawing.IsTicked =
|
||||||
lanMapDrawingsService.DisableDrawingHashSet.Contains(____player.NetId);
|
lanMapDrawingsService.DisableDrawingHashSet.Contains(____player.NetId);
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,6 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
|||||||
[HarmonyPatch(typeof(NMultiplayerSubmenu), "_Ready")]
|
[HarmonyPatch(typeof(NMultiplayerSubmenu), "_Ready")]
|
||||||
internal class NMultiplayerSubmenuReadyPatch
|
internal class NMultiplayerSubmenuReadyPatch
|
||||||
{
|
{
|
||||||
[HarmonyReversePatch(HarmonyReversePatchType.Snapshot)]
|
|
||||||
[HarmonyPatch(typeof(NMultiplayerSubmenu), "UpdateButtons")]
|
|
||||||
private static void UpdateButtons(NMultiplayerSubmenu instance)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Prefix(NMultiplayerSubmenu __instance)
|
private static void Prefix(NMultiplayerSubmenu __instance)
|
||||||
{
|
{
|
||||||
var buttonContainerNode = __instance.GetNode("ButtonContainer");
|
var buttonContainerNode = __instance.GetNode("ButtonContainer");
|
||||||
@@ -105,8 +98,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
|||||||
lanAbandonButton.Connect(NClickableControl.SignalName.Released,
|
lanAbandonButton.Connect(NClickableControl.SignalName.Released,
|
||||||
Callable.From<NButton>(_ =>
|
Callable.From<NButton>(_ =>
|
||||||
{
|
{
|
||||||
TaskHelper.RunSafely(
|
TaskHelper.RunSafely(LanHostHelper.TryAbandonMultiplayerRun(__instance));
|
||||||
LanHostHelper.TryAbandonMultiplayerRun(() => UpdateButtons(__instance)));
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
LanMultiplayerSubmenuButtonService.Instance.LanAbandonButton = lanAbandonButton;
|
LanMultiplayerSubmenuButtonService.Instance.LanAbandonButton = lanAbandonButton;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Godot;
|
using Godot;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
using MegaCrit.Sts2.Core.Logging;
|
||||||
using MegaCrit.Sts2.Core.Nodes.Screens.Overlays;
|
using MegaCrit.Sts2.Core.Nodes.Screens.Overlays;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
@@ -7,23 +8,21 @@ using MegaCrit.Sts2.Core.Nodes.Screens.Overlays;
|
|||||||
|
|
||||||
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
||||||
{
|
{
|
||||||
[HarmonyPatch("MegaCrit.Sts2.Core.Nodes.Screens.Overlays.NOverlayStack", "Remove")]
|
[HarmonyPatch(typeof(NOverlayStack), "Remove")]
|
||||||
internal class NOverlayStackRemovePatch
|
internal class NOverlayStackRemovePatch
|
||||||
{
|
{
|
||||||
private static bool Prefix(object __instance, object screen)
|
private static bool Prefix(NOverlayStack __instance, IOverlayScreen screen)
|
||||||
{
|
|
||||||
var overlay = screen as GodotObject;
|
|
||||||
if (overlay == null || !GodotObject.IsInstanceValid(overlay))
|
|
||||||
{
|
{
|
||||||
|
if (screen is not GodotObject overlay || !GodotObject.IsInstanceValid(overlay))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
var connections = overlay.GetSignalConnectionList("Completed");
|
var connections = overlay.GetSignalConnectionList("Completed");
|
||||||
|
|
||||||
foreach (var connection in connections)
|
foreach (var connection in connections)
|
||||||
{
|
{
|
||||||
if (connection["callable"].AsCallable().Target == __instance)
|
if (connection["callable"].AsCallable().Target == __instance)
|
||||||
{
|
{
|
||||||
GD.Print("NOverlayStack.Remove called twice, preventing crash.");
|
Log.Debug("NOverlayStack.Remove called twice, preventing crash.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using MegaCrit.Sts2.Core.Nodes.Screens.CustomRun;
|
|||||||
using MegaCrit.Sts2.Core.Nodes.Screens.DailyRun;
|
using MegaCrit.Sts2.Core.Nodes.Screens.DailyRun;
|
||||||
using MegaCrit.Sts2.Core.Nodes.Screens.MainMenu;
|
using MegaCrit.Sts2.Core.Nodes.Screens.MainMenu;
|
||||||
using MegaCrit.Sts2.Core.Platform;
|
using MegaCrit.Sts2.Core.Platform;
|
||||||
|
using SlayTheSpire2.LAN.Multiplayer.Helpers;
|
||||||
using SlayTheSpire2.LAN.Multiplayer.Services;
|
using SlayTheSpire2.LAN.Multiplayer.Services;
|
||||||
|
|
||||||
// ReSharper disable UnusedMember.Global
|
// ReSharper disable UnusedMember.Global
|
||||||
@@ -97,7 +98,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
|||||||
{
|
{
|
||||||
if (____lobby.NetService.Platform == PlatformType.None)
|
if (____lobby.NetService.Platform == PlatformType.None)
|
||||||
{
|
{
|
||||||
__result = RunScreenService.ShouldAllowRunToBegin(____lobby);
|
__result = TaskGenericHelper.RunSafely(RunScreenService.ShouldAllowRunToBegin(____lobby));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +113,7 @@ namespace SlayTheSpire2.LAN.Multiplayer.Patchs.Screens
|
|||||||
{
|
{
|
||||||
if (____runLobby.NetService.Platform == PlatformType.None)
|
if (____runLobby.NetService.Platform == PlatformType.None)
|
||||||
{
|
{
|
||||||
__result = RunScreenService.ShouldAllowRunToBegin(____runLobby);
|
__result = TaskGenericHelper.RunSafely(RunScreenService.ShouldAllowRunToBegin(____runLobby));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AssemblyVersion>1.6.1.0</AssemblyVersion>
|
<AssemblyVersion>1.6.3.0</AssemblyVersion>
|
||||||
<FileVersion>1.6.1.0</FileVersion>
|
<FileVersion>1.6.3.0</FileVersion>
|
||||||
|
<GodotProjectDir>$(MSBuildProjectDirectory)</GodotProjectDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -14,16 +15,16 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="0Harmony">
|
<Reference Include="0Harmony">
|
||||||
<HintPath>V:\SteamLibrary\steamapps\common\Slay the Spire 2\data_sts2_windows_x86_64\0Harmony.dll</HintPath>
|
<HintPath>$(HOME)/.steam/steam/steamapps/common/Slay the Spire 2/data_sts2_linuxbsd_x86_64/0Harmony.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="GodotSharp">
|
<Reference Include="GodotSharp">
|
||||||
<HintPath>V:\SteamLibrary\steamapps\common\Slay the Spire 2\data_sts2_windows_x86_64\GodotSharp.dll</HintPath>
|
<HintPath>$(HOME)/.steam/steam/steamapps/common/Slay the Spire 2/data_sts2_linuxbsd_x86_64/GodotSharp.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Steamworks.NET">
|
<Reference Include="Steamworks.NET">
|
||||||
<HintPath>V:\SteamLibrary\steamapps\common\Slay the Spire 2\data_sts2_windows_x86_64\Steamworks.NET.dll</HintPath>
|
<HintPath>$(HOME)/.steam/steam/steamapps/common/Slay the Spire 2/data_sts2_linuxbsd_x86_64/Steamworks.NET.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="sts2">
|
<Reference Include="sts2">
|
||||||
<HintPath>V:\SteamLibrary\steamapps\common\Slay the Spire 2\data_sts2_windows_x86_64\sts2.dll</HintPath>
|
<HintPath>$(HOME)/.steam/steam/steamapps/common/Slay the Spire 2/data_sts2_linuxbsd_x86_64/sts2.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Zeichnen deaktivieren"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Disable drawing"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Deshabilitar dibujo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Désactiver le dessin"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Disabilita il disegno"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "描画を無効にする"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "그리기 비활성화"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Wyłącz rysowanie"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Desativar desenho"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Отключить рисование"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Deshabilitar dibujo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "ปิดใช้งานการวาดภาพ"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "Çizimi devre dışı bırak"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"SlayTheSpire2.LAN.Multiplayer.DISABLE_DRAWING": "禁用绘图"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user