Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconnect fix #413

Draft
wants to merge 37 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
88a03d1
Recover mod after disconnect
TheGrayAlien Jul 12, 2022
dd7f525
Fix typo
TheGrayAlien Jul 12, 2022
0f471f7
Oops!
TheGrayAlien Jul 12, 2022
3cc3077
Fix multiple players using HR
TheGrayAlien Jul 13, 2022
f44951e
Notify on ruleset re-activation
TheGrayAlien Jul 13, 2022
6cfe995
Clarifying log reports
TheGrayAlien Jul 13, 2022
26a88c8
Fixed freeze
TheGrayAlien Jul 16, 2022
f4eb92f
Skip this rule when loading OnPreGameCreated
TheGrayAlien Jul 16, 2022
4ad8190
Fix LevelSequence loading
TheGrayAlien Jul 16, 2022
6da4440
Piece adjustments must remain loaded
TheGrayAlien Jul 16, 2022
f84ab00
Fix Play Again
TheGrayAlien Jul 17, 2022
429b6a4
Fix missing variable set
TheGrayAlien Jul 17, 2022
084b0a9
Detect manual disconnects
TheGrayAlien Jul 17, 2022
87c99ad
Detect manual disconnects v2
TheGrayAlien Jul 17, 2022
4687110
Set variable at game over
TheGrayAlien Jul 17, 2022
1389e91
Update functionality
TheGrayAlien Jul 17, 2022
32497bc
Implemented Type
TheGrayAlien Jul 18, 2022
15f3ac8
Fix pregame and postgame loading
TheGrayAlien Jul 18, 2022
ef194ae
Fix OnPostGameCreated
TheGrayAlien Jul 18, 2022
585d25a
Trying again...
TheGrayAlien Jul 18, 2022
948ccb6
Cleanups
TheGrayAlien Jul 18, 2022
c5b4dee
Fix problematic GameHub.GameID
TheGrayAlien Jul 18, 2022
469cca5
Fix active ruleset check
TheGrayAlien Jul 19, 2022
7fba3a5
Fix summary for new Type
TheGrayAlien Jul 19, 2022
20b5b36
Keep up to date with main
TheGrayAlien Jul 19, 2022
90a3bd1
Remove previous commit
TheGrayAlien Jul 19, 2022
cd89eab
Unnecessary - already in main
TheGrayAlien Jul 19, 2022
e1bbbd8
Disable if Host chooses not to reconnect
TheGrayAlien Jul 19, 2022
887ddfd
Added new Type to another rule
TheGrayAlien Jul 20, 2022
b3ea715
Added DeactivateReconnect function
TheGrayAlien Jul 20, 2022
7cd6392
Proper deactivation fix
TheGrayAlien Jul 20, 2022
cd53eb5
Code cleanup
TheGrayAlien Jul 29, 2022
7df968c
Merge branch 'main' into Reconnect_Fix
TheGrayAlien Jul 29, 2022
3b20fe6
Merge branch 'main' into Reconnect_Fix
TheGrayAlien Dec 19, 2022
a508d4e
Allow ruleset re-selection
TheGrayAlien Dec 19, 2022
bdc38c1
Fixes for latest updates to main
TheGrayAlien Dec 19, 2022
d394d95
Merge branch 'main' into Reconnect_Fix
TheGrayAlien Dec 19, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion HouseRules_Core/HR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public static void SelectRuleset(string ruleset)
{
if (IsRulesetActive)
{
throw new InvalidOperationException("May not select a new ruleset while one is currently active.");
LifecycleDirector.DeactivateReconnect();
// throw new InvalidOperationException("May not select a new ruleset while one is currently active.");
}

if (Ruleset.None.Name.Equals(ruleset, StringComparison.OrdinalIgnoreCase))
Expand Down
1 change: 1 addition & 0 deletions HouseRules_Core/HouseRules_Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<Compile Include="CoreMod.cs" />
<Compile Include="Rulebook.cs" />
<Compile Include="Types\IConfigWritable.cs" />
<Compile Include="Types\IDisableOnReconnect.cs" />
<Compile Include="Types\IMultiplayerSafe.cs" />
<Compile Include="Types\IPatchable.cs" />
<Compile Include="Types\ISingular.cs" />
Expand Down
200 changes: 176 additions & 24 deletions HouseRules_Core/LifecycleDirector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,34 @@
{
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using Boardgame;
using Boardgame.BoardgameActions;
using Boardgame.Networking;
using HarmonyLib;
using HouseRules.Types;
using Photon.Pun;
using Photon.Realtime;

internal static class LifecycleDirector
{
private const float WelcomeMessageDurationSeconds = 30f;
private const string ModdedRoomPropertyKey = "modded";

private const float WelcomeMessageDurationSeconds = 30f;
private static GameContext _gameContext;
private static bool _isCreatingGame;
private static bool _isLoadingGame;
private static bool _isReconnect = false;
private static string roomCode;
private static string lastCode;

internal static bool IsRulesetActive { get; private set; }

internal static void Patch(Harmony harmony)
{
harmony.Patch(
original: AccessTools.Method(typeof(GameStateMachine), "OnRoomJoined"),
postfix: new HarmonyMethod(typeof(LifecycleDirector), nameof(GameStateMachine_OnRoomJoined_Postfix)));

harmony.Patch(
original: AccessTools.Method(typeof(GameStartup), "InitializeGame"),
postfix: new HarmonyMethod(typeof(LifecycleDirector), nameof(GameStartup_InitializeGame_Postfix)));
Expand All @@ -35,6 +42,10 @@ internal static void Patch(Harmony harmony)
original: AccessTools.Method(typeof(CreatingGameState), "OnJoinedRoom"),
prefix: new HarmonyMethod(typeof(LifecycleDirector), nameof(CreatingGameState_OnJoinedRoom_Prefix)));

harmony.Patch(
original: AccessTools.Method(typeof(PlayingState), "OnMasterClientChanged"),
prefix: new HarmonyMethod(typeof(LifecycleDirector), nameof(PlayingGameState_OnMasterClientChanged_Prefix)));

harmony.Patch(
original: AccessTools.Method(typeof(GameStateMachine), "GoToPlayingState"),
postfix: new HarmonyMethod(typeof(LifecycleDirector), nameof(GameStateMachine_GoToPlayingState_Postfix)));
Expand All @@ -45,19 +56,19 @@ internal static void Patch(Harmony harmony)

harmony.Patch(
original: AccessTools.Method(typeof(PostGameControllerBase), "OnPlayAgainClicked"),
postfix: new HarmonyMethod(
typeof(LifecycleDirector),
nameof(PostGameControllerBase_OnPlayAgainClicked_Postfix)));
postfix: new HarmonyMethod(typeof(LifecycleDirector), nameof(PostGameControllerBase_OnPlayAgainClicked_Postfix)));

harmony.Patch(
original: AccessTools.Method(typeof(GameStateMachine), "EndGame"),
prefix: new HarmonyMethod(typeof(LifecycleDirector), nameof(GameStateMachine_EndGame_Prefix)));

harmony.Patch(
original: AccessTools.Method(typeof(SerializableEventQueue), "DisconnectLocalPlayer"),
prefix: new HarmonyMethod(
typeof(LifecycleDirector),
nameof(SerializableEventQueue_DisconnectLocalPlayer_Prefix)));
prefix: new HarmonyMethod(typeof(LifecycleDirector), nameof(SerializableEventQueue_DisconnectLocalPlayer_Prefix)));

harmony.Patch(
original: AccessTools.Method(typeof(ReconnectState), "OnClickLeaveGameAfterReconnect"),
postfix: new HarmonyMethod(typeof(LifecycleDirector), nameof(ReconnectState_OnClickLeaveGameAfterReconnect_Postfix)));
}

private static void GameStartup_InitializeGame_Postfix(GameStartup __instance)
Expand All @@ -66,6 +77,26 @@ private static void GameStartup_InitializeGame_Postfix(GameStartup __instance)
_gameContext = gameContext;
}

private static void ReconnectState_OnClickLeaveGameAfterReconnect_Postfix()
{
DeactivateReconnect();
}

private static void GameStateMachine_OnRoomJoined_Postfix()
{
if (!_isReconnect)
{
return;
}

lastCode = PhotonNetwork.CurrentRoom.Name;
if (lastCode != roomCode)
{
CoreMod.Logger.Warning($"Room {lastCode} doesn't match original room {roomCode}. Deactivating reconnection rules!");
DeactivateReconnect();
}
}

private static void CreatingGameState_TryCreateRoom_Prefix()
{
if (HR.SelectedRuleset == Ruleset.None)
Expand Down Expand Up @@ -125,10 +156,47 @@ private static void CreatingGameState_OnJoinedRoom_Prefix()
var levelSequence = Traverse.Create(_gameContext.gameStateMachine).Field<LevelSequence>("levelSequence").Value;
MotherbrainGlobalVars.CurrentConfig = levelSequence.gameConfig;

if (_isReconnect)
{
DeactivateReconnect();
}

roomCode = PhotonNetwork.CurrentRoom.Name;
CoreMod.Logger.Msg($"New game in room {roomCode} started");
ActivateRuleset();
OnPreGameCreated();
}

private static void PlayingGameState_OnMasterClientChanged_Prefix()
{
if (!_isReconnect)
{
return;
}

if (!GameStateMachine.IsMasterClient)
{
return;
}

if (HR.SelectedRuleset == Ruleset.None)
{
return;
}

if (_gameContext.gameStateMachine.goBackToMenuState)
{
return;
}

CoreMod.Logger.Warning($"<--- Resuming ruleset after disconnection from room {roomCode} --->");

ActivateRuleset();
OnPreGameCreated();
OnPostGameCreated();
_isReconnect = false;
}

private static void GameStateMachine_GoToPlayingState_Postfix()
{
if (!_isCreatingGame)
Expand Down Expand Up @@ -172,11 +240,31 @@ private static void GameStateMachine_EndGame_Prefix()
DeactivateRuleset();
}

private static void SerializableEventQueue_DisconnectLocalPlayer_Prefix()
private static void SerializableEventQueue_DisconnectLocalPlayer_Prefix(BoardgameActionOnLocalPlayerDisconnect.DisconnectContext context)
{
DeactivateRuleset();
}
if (HR.SelectedRuleset == Ruleset.None)
{
return;
}

if (!GameStateMachine.IsMasterClient)
{
return;
}

if (context == BoardgameActionOnLocalPlayerDisconnect.DisconnectContext.ReconnectState)
{
CoreMod.Logger.Warning($"<- Disconnected from room {roomCode} ->");
_isReconnect = true;
DeactivateRuleset();
}
else
{
CoreMod.Logger.Msg($"<- MANUALLY disconnected from room {roomCode} ->");
_isReconnect = true;
DeactivateRuleset();
}
}

/// <summary>
/// Add properties to the room to indicate its modded nature.
Expand All @@ -201,13 +289,12 @@ private static void AddModdedRoomProperties(RoomOptions roomOptions)
newOptions[0] = ModdedRoomPropertyKey;
roomOptions.CustomRoomPropertiesForLobby.CopyTo(newOptions, 1);
roomOptions.CustomRoomPropertiesForLobby = newOptions;

roomOptions.CustomRoomProperties.Add(ModdedRoomPropertyKey, true);
}

private static void ActivateRuleset()
{
if (IsRulesetActive)
if (IsRulesetActive && !_isReconnect)
{
CoreMod.Logger.Warning("Ruleset activation was attempted whilst a ruleset was already activated. This should not happen. Please report this to HouseRules developers.");
return;
Expand All @@ -226,13 +313,22 @@ private static void ActivateRuleset()

IsRulesetActive = true;

CoreMod.Logger.Msg($"Activating ruleset: {HR.SelectedRuleset.Name} (with {HR.SelectedRuleset.Rules.Count} rules)");
CoreMod.Logger.Warning($"Activating ruleset: {HR.SelectedRuleset.Name} (with {HR.SelectedRuleset.Rules.Count} rules)");
foreach (var rule in HR.SelectedRuleset.Rules)
{
try
{
CoreMod.Logger.Msg($"Activating rule type: {rule.GetType()}");
rule.OnActivate(_gameContext);
var isDisabled = rule is IDisableOnReconnect;
if (_isReconnect && isDisabled)
{
CoreMod.Logger.Msg($"Skip activating rule type: {rule.GetType()}");
continue;
}
else
{
CoreMod.Logger.Msg($"Activating rule type: {rule.GetType()}");
rule.OnActivate(_gameContext);
}
}
catch (Exception e)
{
Expand All @@ -249,15 +345,27 @@ private static void DeactivateRuleset()
return;
}

IsRulesetActive = false;
if (!_isReconnect)
{
IsRulesetActive = false;
}

CoreMod.Logger.Msg($"Deactivating ruleset: {HR.SelectedRuleset.Name} (with {HR.SelectedRuleset.Rules.Count} rules)");
foreach (var rule in HR.SelectedRuleset.Rules)
{
try
{
CoreMod.Logger.Msg($"Deactivating rule type: {rule.GetType()}");
rule.OnDeactivate(_gameContext);
var isDisabled = rule is IDisableOnReconnect;
if (_isReconnect && isDisabled)
{
CoreMod.Logger.Msg($"Skip deactivating rule type: {rule.GetType()}");
continue;
}
else
{
CoreMod.Logger.Msg($"Deactivating rule type: {rule.GetType()}");
rule.OnDeactivate(_gameContext);
}
}
catch (Exception e)
{
Expand All @@ -267,6 +375,32 @@ private static void DeactivateRuleset()
}
}

public static void DeactivateReconnect()
{
_isReconnect = false;
IsRulesetActive = false;

CoreMod.Logger.Warning($"Deactivating reconnection: {HR.SelectedRuleset.Name} (with {HR.SelectedRuleset.Rules.Count} rules)");

foreach (var rule in HR.SelectedRuleset.Rules)
{
try
{
var isDisabled = rule is IDisableOnReconnect;
if (isDisabled)
{
CoreMod.Logger.Msg($"Deactivating reconnection for rule type: {rule.GetType()}");
rule.OnDeactivate(_gameContext);
}
}
catch (Exception e)
{
// TODO(orendain): Consider rolling back or disable rule.
CoreMod.Logger.Warning($"Failed to deactivate reconnection for rule [{rule.GetType()}]: {e}");
}
}
}

private static void OnPreGameCreated()
{
if (HR.SelectedRuleset == Ruleset.None)
Expand All @@ -283,8 +417,17 @@ private static void OnPreGameCreated()
{
try
{
CoreMod.Logger.Msg($"Calling OnPreGameCreated for rule type: {rule.GetType()}");
rule.OnPreGameCreated(_gameContext);
var isDisabled = rule is IDisableOnReconnect;
if (_isReconnect && isDisabled)
{
CoreMod.Logger.Msg($"Skip OnPreGameCreated for rule type: {rule.GetType()}");
continue;
}
else
{
CoreMod.Logger.Msg($"Calling OnPreGameCreated for rule type: {rule.GetType()}");
rule.OnPreGameCreated(_gameContext);
}
}
catch (Exception e)
{
Expand All @@ -310,8 +453,17 @@ private static void OnPostGameCreated()
{
try
{
CoreMod.Logger.Msg($"Calling OnPostGameCreated for rule type: {rule.GetType()}");
rule.OnPostGameCreated(_gameContext);
var isDisabled = rule is IDisableOnReconnect;
if (_isReconnect && isDisabled)
{
CoreMod.Logger.Msg($"Skip OnPostGameCreated for rule type: {rule.GetType()}");
continue;
}
else
{
CoreMod.Logger.Msg($"Calling OnPostGameCreated for rule type: {rule.GetType()}");
rule.OnPostGameCreated(_gameContext);
}
}
catch (Exception e)
{
Expand Down
9 changes: 9 additions & 0 deletions HouseRules_Core/Types/IDisableOnReconnect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace HouseRules.Types
{
/// <summary>
/// Represents a rule that is not safe to apply after a disconnect in a multiplayer environment.
/// </summary>
public interface IDisableOnReconnect
{
}
}
2 changes: 1 addition & 1 deletion HouseRules_Essentials/Rules/LevelSequenceOverriddenRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using HarmonyLib;
using HouseRules.Types;

public sealed class LevelSequenceOverriddenRule : Rule, IConfigWritable<List<string>>, IPatchable, IMultiplayerSafe
public sealed class LevelSequenceOverriddenRule : Rule, IConfigWritable<List<string>>, IPatchable, IMultiplayerSafe, IDisableOnReconnect
{
public override string Description => "LevelSequence is overridden";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using HouseRules.Types;

public sealed class PieceAbilityListOverriddenRule : Rule,
IConfigWritable<Dictionary<BoardPieceId, List<AbilityKey>>>, IMultiplayerSafe
IConfigWritable<Dictionary<BoardPieceId, List<AbilityKey>>>, IMultiplayerSafe, IDisableOnReconnect
{
public override string Description => "Piece abilities are adjusted";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using Behaviour = DataKeys.Behaviour;

public sealed class PieceBehavioursListOverriddenRule : Rule,
IConfigWritable<Dictionary<BoardPieceId, List<Behaviour>>>, IMultiplayerSafe
IConfigWritable<Dictionary<BoardPieceId, List<Behaviour>>>, IMultiplayerSafe, IDisableOnReconnect
{
public override string Description => "Piece behaviours are adjusted";

Expand Down
2 changes: 1 addition & 1 deletion HouseRules_Essentials/Rules/PieceConfigAdjustedRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using HouseRules.Types;

public sealed class PieceConfigAdjustedRule : Rule, IConfigWritable<List<PieceConfigAdjustedRule.PieceProperty>>,
IMultiplayerSafe
IMultiplayerSafe, IDisableOnReconnect
{
public override string Description => "Piece configuration is adjusted";

Expand Down
Loading