WAReborn / WorldsAdriftReborn

63 stars 15 forks source link

Player inventory broken #28

Closed sp00ktober closed 1 year ago

sp00ktober commented 1 year ago

[Error  : Unity Log] [2022-10-10T17:37:58]..[ERROR] [DEFAULT] [ToolBehaviour] Trying to check if a tool has been unlocked before ToolBehaviour is available. Returning false.
   at ToolBehaviour.IsToolUnlocked(ToolType tool)
   at Travellers.UI.PlayerInventory.HotBarScreen.CheckToolSlot(ToolType type, HotbarSlotType slotType)
   at Travellers.UI.PlayerInventory.HotBarScreen.RefreshToolSlots()
   at Travellers.UI.PlayerInventory.HotBarScreen.RefreshHotbarSlots()
   at Travellers.UI.PlayerInventory.HotBarScreen.ProtectedInit()
   at Travellers.UI.Framework.UIMonoLifetimeController.Initialise()
   at Travellers.UI.Framework.UIObjectFactory.Create(UIElementType elementType, UIFillType fillType, UnityEngine.Transform parent, Boolean isObjectActive, System.String prefabName)
   at Travellers.UI.Framework.HUDState.CreateScreens(System.Collections.Generic.Dictionary`2 screenLookup)
   at Travellers.UI.Framework.WindowState.EnterState()
   at Travellers.UI.Framework.DissociatedStatesStack.PushState(Travellers.UI.Framework.WindowState newState)
   at Travellers.UI.Framework.UIWindowController.PushState(Travellers.UI.Framework.WindowState newState)
   at GameStateMachine.InGameState.OnEnterState()
   at FSM.StateMachine+<SwitchStateWithLoading>c__AnonStorey0.<>m__5(IState o)
   at RSG.Promise`1+<>c__DisplayClass20[[FSM.IState, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].<Then>b__1e(IState v)
   at RSG.Promise`1[[FSM.IState, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].InvokeHandler(System.Action`1 callback, IRejectable rejectable, IState value)
   at RSG.Promise`1[[FSM.IState, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].ActionHandlers(IRejectable resultPromise, System.Action`1 resolveHandler, System.Action`1 rejectHandler)
   at RSG.Promise`1[[FSM.IState, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Then(System.Action`1 onResolved, System.Action`1 onRejected)
   at RSG.Promise`1[[FSM.IState, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Then(System.Action`1 onResolved)
   at FSM.StateMachine.SwitchStateWithLoading(IState nextState)
   at FSM.StateMachine.SwitchState(IState nextState)
   at FSM.StateMachine.ProcessState(IState state)
   at FSM.StateMachine.Update(Single deltaTime)
   at GameStateMachine.Bootstrap.Update()```

The `InventoryVisualizer` does not become active so far and thus we get some errors in `HotBarScreen` once its code gets executed. The `OnEnabled()` method of `InventoryVisualizer` does set some stuff that may be needed by the hotbar.
We need to find out why this happens and how we can solve it.
sp00ktober commented 1 year ago
[HarmonyPatch(typeof(EntityVisualizers))]
    internal class EntityVisualizers_Patch
    {
        [HarmonyPrefix]
        [HarmonyPatch(typeof(EntityVisualizers), "UpdateActivation")]
        public static void UpdateActivation_Prefix(EntityVisualizers __instance, MonoBehaviour visualizer )
        {
            if(visualizer.GetType() == typeof(ToolBehaviour))
            {
                // reflection hell and very broken code incomming but it might make the inventory work
                // however this is brute force and we should find out how the game usually does this
                System.Type tConnectionHandle = AccessTools.TypeByName("ConnectionHandle");
                System.Type tConnection = AccessTools.TypeByName("Connection");

                ToolState.Impl impl1 = new ToolState.Impl((Improbable.Worker.Connection)AccessTools.Constructor(tConnection, new System.Type[] { tConnectionHandle }).Invoke(new object[] { AccessTools.Constructor(tConnectionHandle).Invoke(new object[] { }) }), new EntityId(0), new ToolState.Data(3));
                ToolRequestState.Impl impl2 = new ToolRequestState.Impl((Improbable.Worker.Connection)AccessTools.Constructor(tConnection, new System.Type[] { tConnectionHandle }).Invoke(new object[] { AccessTools.Constructor(tConnectionHandle).Invoke(new object[] { }) }), new EntityId(0), new ToolRequestState.Data());

                IMemberAdapter impl1FieldInfo = VisualizerMetadataLookup.Instance.GetFieldInfo(impl1.GetType(), visualizer.GetType());
                IMemberAdapter impl2FieldInfo = VisualizerMetadataLookup.Instance.GetFieldInfo(impl2.GetType(), visualizer.GetType());

                if(impl1FieldInfo != null && impl2FieldInfo != null)
                {
                    AccessTools.Method(typeof(EntityVisualizers), "InjectField").Invoke(__instance, new object[] { visualizer, impl1FieldInfo, impl1});
                    AccessTools.Method(typeof(EntityVisualizers), "InjectField").Invoke(__instance, new object[] { visualizer, impl2FieldInfo, impl2 });
                }
            }
        }
    }

This patch fixes the above error message by getting past the below check (this.AllFieldReadersAndWritersInjected(visualizer)) which enables the ToolBehaviour on the player and calls its OnEnable() method (which is what fixes the above error):

private void UpdateActivation(MonoBehaviour visualizer)
        {
            if (this.IsMarkedAsDisabled(visualizer))
            {
                this.Deactivate(visualizer);
            }
            else if (VisualizerMetadataLookup.Instance.AreAllRequiredFieldsInjectable(visualizer.GetType()) && this.AllFieldReadersAndWritersInjected(visualizer))
            {
                this.Activate(visualizer);
            }
        }

However this is not the way to go, its only for testing. The game still throws errors later when trying to open the inventory but also just by loading into the game, see below.

NullReferenceException: Object reference not set to an instance of an object
  at Travellers.UI.PlayerInventory.HotBarScreen.RefreshHotbarSlots () [0x00000] in <filename unknown>:0 
  at Travellers.UI.PlayerInventory.HotBarScreen.ProtectedInit () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIMonoLifetimeController.Initialise () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIObjectFactory.Create[HotBarScreen] (UIElementType elementType, UIFillType fillType, UnityEngine.Transform parent, Boolean isObjectActive, System.String prefabName) [0x00000] in <filename unknown>:0 
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
WAUnityLogger:LogToLocal(WALogLevel, Object, String, String, Dictionary`2, Exception, String)
WAUnityLogger:Log(WALogLevel, Object, String, String, Dictionary`2, Exception)
WAUtilities.Logging.WALogger:Exception(Exception, Dictionary`2)
Travellers.UI.Framework.UIErrorHandler:TriggerExceptionHandler(String, Exception, Action, Action)
Travellers.UI.Framework.UIObjectFactory:Create(UIElementType, UIFillType, Transform, Boolean, String)
Travellers.UI.Framework.HUDState:CreateScreens(Dictionary`2)
Travellers.UI.Framework.WindowState:EnterState()
Travellers.UI.Framework.DissociatedStatesStack:PushState(WindowState)
Travellers.UI.Framework.UIWindowController:PushState(WindowState)
GameStateMachine.InGameState:OnEnterState()
FSM.<SwitchStateWithLoading>c__AnonStorey0:<>m__5(IState)
RSG.<>c__DisplayClass20:<Then>b__1e(IState)
RSG.Promise`1:InvokeHandler(Action`1, IRejectable, IState)
RSG.Promise`1:ActionHandlers(IRejectable, Action`1, Action`1)
RSG.Promise`1:Then(Action`1, Action`1)
RSG.Promise`1:Then(Action`1)
FSM.StateMachine:SwitchStateWithLoading(IState)
FSM.StateMachine:SwitchState(IState)
FSM.StateMachine:ProcessState(IState)
FSM.StateMachine:Update(Single)
GameStateMachine.Bootstrap:Update()
[2023-01-23T23:23:15]..[EXCEPTION] [DEFAULT] [Exception] Object reference not set to an instance of an object
exception=System.NullReferenceException: Object reference not set to an instance of an object
  at LorePiecesCollectorVisualizer.GetKnownPieces () [0x00000] in <filename unknown>:0 
  at LoreUI.RefreshLore (Boolean refreshSelected) [0x00000] in <filename unknown>:0 
  at LoreUI.Init () [0x00000] in <filename unknown>:0 
  at Travellers.UI.PlayerInventory.LogbookUI.ProtectedInit () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIMonoLifetimeController.Initialise () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIObjectFactory.Create[LogbookUI] (UIElementType elementType, UIFillType fillType, UnityEngine.Transform parent, Boolean isObjectActive, System.String prefabName) [0x00000] in <filename unknown>:0 
  at LorePiecesCollectorVisualizer.GetKnownPieces () [0x00000] in <filename unknown>:0 
  at LoreUI.RefreshLore (Boolean refreshSelected) [0x00000] in <filename unknown>:0 
  at LoreUI.Init () [0x00000] in <filename unknown>:0 
  at Travellers.UI.PlayerInventory.LogbookUI.ProtectedInit () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIMonoLifetimeController.Initialise () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIObjectFactory.Create[LogbookUI] (UIElementType elementType, UIFillType fillType, UnityEngine.Transform parent, Boolean isObjectActive, System.String prefabName) [0x00000] in <filename unknown>:0
  NullReferenceException: Object reference not set to an instance of an object
  at LorePiecesCollectorVisualizer.GetKnownPieces () [0x00000] in <filename unknown>:0 
  at LoreUI.RefreshLore (Boolean refreshSelected) [0x00000] in <filename unknown>:0 
  at LoreUI.Init () [0x00000] in <filename unknown>:0 
  at Travellers.UI.PlayerInventory.LogbookUI.ProtectedInit () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIMonoLifetimeController.Initialise () [0x00000] in <filename unknown>:0 
  at Travellers.UI.Framework.UIObjectFactory.Create[LogbookUI] (UIElementType elementType, UIFillType fillType, UnityEngine.Transform parent, Boolean isObjectActive, System.String prefabName) [0x00000] in <filename unknown>:0 
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
WAUnityLogger:LogToLocal(WALogLevel, Object, String, String, Dictionary`2, Exception, String)
WAUnityLogger:Log(WALogLevel, Object, String, String, Dictionary`2, Exception)
WAUtilities.Logging.WALogger:Exception(Exception, Dictionary`2)
Travellers.UI.Framework.UIErrorHandler:TriggerExceptionHandler(String, Exception, Action, Action)
Travellers.UI.Framework.UIObjectFactory:Create(UIElementType, UIFillType, Transform, Boolean, String)
Travellers.UI.PlayerInventory.LogbookModule:CreateMainComponent(Transform)
Travellers.UI.PlayerInventory.BaseSheetModule:.ctor(Transform, Transform, Action`1, UIToggleGroup)
Travellers.UI.PlayerInventory.CharacterSheetModule:.ctor(Transform, Transform, HorizontalOrVerticalLayoutGroup, Action`1, UIToggleGroup)
Travellers.UI.PlayerInventory.LogbookModule:.ctor(Transform, Transform, HorizontalOrVerticalLayoutGroup, Action`1, UIToggleGroup)
Travellers.UI.PlayerInventory.CharacterSheetScreen:ProtectedInit()
Travellers.UI.Framework.UIMonoLifetimeController:Initialise()
Travellers.UI.Framework.UIObjectFactory:Create(UIElementType, UIFillType, Transform, Boolean, String)
Travellers.UI.Framework.HUDState:CreateScreens(Dictionary`2)
Travellers.UI.Framework.WindowState:EnterState()
Travellers.UI.Framework.DissociatedStatesStack:PushState(WindowState)
Travellers.UI.Framework.UIWindowController:PushState(WindowState)
GameStateMachine.InGameState:OnEnterState()
FSM.<SwitchStateWithLoading>c__AnonStorey0:<>m__5(IState)
RSG.<>c__DisplayClass20:<Then>b__1e(IState)
RSG.Promise`1:InvokeHandler(Action`1, IRejectable, IState)
RSG.Promise`1:ActionHandlers(IRejectable, Action`1, Action`1)
RSG.Promise`1:Then(Action`1, Action`1)
RSG.Promise`1:Then(Action`1)
FSM.StateMachine:SwitchStateWithLoading(IState)
FSM.StateMachine:SwitchState(IState)
FSM.StateMachine:ProcessState(IState)
FSM.StateMachine:Update(Single)
GameStateMachine.Bootstrap:Update()