[#bugs] The `AfterPlayerBodyChangeEvent` event handler for `IPlayerPart` appears not to ...

Issue #11276 resolved
Freehold Games Bot Account created an issue

Marked for crossposting by: kernelmethod

Message (jump):

<kernelmethod> The AfterPlayerBodyChangeEvent event handler for IPlayerPart appears not to work correctly; as far as I can tell the check for whether the new body already has the target part typically fails (I believe because IPlayerPart and its ancestors are missing an appropriate override for Equals), and so the part always gets added again.

Proof-of-concept is below. To reproduce:
1. Dominate another creature
2. Clone yourself
3. Return to your original body

Proof-of-concept mod:
```csharp
using System;
using XRL;
using XRL.Core;
using XRL.World;
using XRL.World.Parts;

namespace Kernelmethod.ExampleMod
{
[PlayerMutator]
public class MyPlayerMutator : IPlayerMutator
{
public void mutate(GameObject player)
{
player.AddPart<TestPart>();
}
}

  1. ``` [HasCallAfterGameLoadedAttribute]
    public class AddPartsToPlayerHandler
    {
    [CallAfterGameLoadedAttribute]
    public static void AddPartsCallback()
    {
    GameObject player = XRLCore.Core?.Game?.Player?.Body;
      if (player == null)  
          return;
    
      player.RequirePart<TestPart>();
    

    }
    }

[Serializable]
public class TestPart : IPlayerPart {
public override void Initialize() {
MetricsManager.LogInfo($"TestPart initialized on {ParentObject.DisplayName}");
}

  public override bool WantEvent(int ID, int cascade) {  
      return base.WantEvent(ID, cascade)  
          || ID == AfterPlayerBodyChangeEvent.ID;  
  }

  public override bool HandleEvent(AfterPlayerBodyChangeEvent E) {  
      if (E.NewBody != null)  
          MetricsManager.LogInfo($"contains part: {E.NewBody.PartsList.Contains(this)}");  
      return base.HandleEvent(E);  
  }

} ```

}
```

Comments (4)

  1. kernelmethod
    using System;
    using XRL;
    using XRL.Core;
    using XRL.World;
    using XRL.World.Parts;
    
    namespace Kernelmethod.ExampleMod
    {
        [PlayerMutator]
        public class MyPlayerMutator : IPlayerMutator
        {
            public void mutate(GameObject player)
            {
                player.AddPart<TestPart>();
            }
        }
    
        [HasCallAfterGameLoadedAttribute]
        public class AddPartsToPlayerHandler
        {
            [CallAfterGameLoadedAttribute]
            public static void AddPartsCallback()
            {
                GameObject player = XRLCore.Core?.Game?.Player?.Body;
    
                if (player == null)
                    return;
    
                player.RequirePart<TestPart>();
            }
        }
    
        [Serializable]
        public class TestPart : IPlayerPart {
            public override void Initialize() {
                MetricsManager.LogInfo($"TestPart initialized on {ParentObject.DisplayName}");
            }
    
            public override bool WantEvent(int ID, int cascade) {
                return base.WantEvent(ID, cascade)
                    || ID == AfterPlayerBodyChangeEvent.ID;
            }
    
            public override bool HandleEvent(AfterPlayerBodyChangeEvent E) {
                if (E.NewBody != null)
                    MetricsManager.LogInfo($"contains part: {E.NewBody.PartsList.Contains(this)}");
                return base.HandleEvent(E);
            }
        }
    }
    

    Player.log should reveal that after the third step is completed, the part gets added twice to the original player:

    @kernelmethod ➜ Parts git:(main) grep -E "TestPart" ~/.config/unity3d/Freehold\ Games/CavesOfQud/Player.log 
    INFO - TestPart initialized on Emepichiji Ki
    INFO - TestPart initialized on watervine farmer
    INFO - TestPart initialized on Emepichiji Ki
    INFO - TestPart initialized on Emepichiji Ki
    

  2. Armithaig

    Ah, it just shouldn’t be deep-copied in the first place (it's meant to be a singleton)
    Fixed in 206.75

    public override IPart DeepCopy(GameObject Parent) => null;
    

    in the inheriting part should fix it in the meantime

  3. Log in to comment