Search Unity

Using Singletons throws Exceptions which are lies

Discussion in 'Entity Component System' started by Wobbers, Feb 16, 2020.

  1. Wobbers

    Wobbers

    Joined:
    Dec 31, 2017
    Posts:
    55
    So I have this weird problem, where after some time in the game, the editor will throw Exceptions about singletons, like
    , when the Entity with that exact component is actually still there, as it can be seen in the Entity Debugger and as some systems relying on these singletons are still working.

    For Example, this code
    Code (CSharp):
    1. protected override void OnUpdate()
    2. {
    3.     // ...
    4.     if (m_Input.DownThisFrame(GameInputs.Interact))
    5.     {
    6.         Logger.Log("Has Camera Anchor: " + HasSingleton<CameraAnchor>());
    7.         var target = EntityManager.GetComponentData<Target>(GetSingletonEntity<CameraAnchor>()).Value;
    8.         // ...
    9.     }
    10.     // ...
    11. }
    will log False and throw the above exception while other Code in a different system which controls the camera with that singleton just continues to work as expected:
    Code (CSharp):
    1. protected override void OnUpdate()
    2. {
    3.     Entities.ForEach((ref CameraAnchor anchor, ref Rotation rotation, ref Translation translation, ref LocalToWorld ltw) =>
    4.     {
    5.         // ...
    6.         Camera.main.transform.position = translation.Value;
    7.         Camera.main.transform.rotation = rotation.Value;
    8.     }
    9. });
    and the Entity Debugger will show the entity with that component as well and update its values.

    I got this issue with
    GetSingletonEntity<NetworkStreamConnection>()
    from the NetCode Package as well. But aside from checking the singleton, the connection is stable, and Rpcs and commands are sent properly.

    Also, there is sometimes another error thrown from the return line of this code:
    Code (CSharp):
    1. public static T GetOrCreateSingleton<T>(this ComponentSystem system) where T : struct, IComponentData
    2. {
    3.     if (!system.HasSingleton<T>())
    4.         system.EntityManager.CreateEntity(typeof(T));
    5.     return system.GetSingleton<T>();
    6. }
    Anyone has experienced this as well or knows what this might be caused by?
     
    Last edited: Feb 16, 2020
  2. BrendonSmuts

    BrendonSmuts

    Joined:
    Jun 12, 2017
    Posts:
    86
    Have you checked that the system looking for the Singleton is in the same ECS world as the working system?
     
  3. Wobbers

    Wobbers

    Joined:
    Dec 31, 2017
    Posts:
    55
    This is all happening in the same world (there is only one Client World and these systems don't run in the Server World). And it works as intended for some time but there seems to be a random point at which it starts happening.
    I can't reliably reproduce to get to this point. This is the kind of thing that never occurs when I try to reproduce it, but it seems to happen whenever I don't expect it.
    The affected entities are only created at the start of the game and never destroyed, only the values inside their components are modified (like translation/rotation on the CameraAnchor singleton).
     
  4. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,853
    I tried to use a Singleton entity and I ran into issues. Didn't know if it was my code or the singleton but looking at your issues I think it was the Singleton.
     
  5. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    have you tried something like

    Code (CSharp):
    1.  
    2. if(!HasSingleton<MySingleton>())
    3.     return inputDeps;
    4. var singleton = GetSingleton<MySingleton>();
    5.  
    also remembered you can use "RequireSingletonForUpdate" to a system to negate the need for the above code
     
  6. Wobbers

    Wobbers

    Joined:
    Dec 31, 2017
    Posts:
    55
    I forgot to mention that. The system in my first example actually has a

    RequireSingletonForUpdate<CameraAnchor>()
    defined in its OnCreate. It still runs all the time (as it should actually, since the Singleton is always there), and Has/GetSingleton will still return false/exception. This makes this behaviour even weirder.

    RequireSingletonForUpdate seems to get the singleton correctly, while Has/GetSingleton start to mess up at some point.

    Here is the full System:
    Code (CSharp):
    1.     [UpdateInGroup(typeof(ClientSimulationSystemGroup)), UpdateAfter(typeof(CalculateTargetSystem))]
    2.     public class CharacterControllerSystem : ComponentSystem, ISend<StartTalkingIntent>, ISend<InteractIntent>, ISend<ToggleWeaponIntent>
    3.     {
    4.  
    5.         InputSystem m_Input;
    6.  
    7.         protected override void OnCreate()
    8.         {
    9.             RequireSingletonForUpdate<LocalPlayer>();
    10.             RequireSingletonForUpdate<CameraAnchor>();
    11.             m_Input = World.GetExistingSystem<InputSystem>();
    12.         }
    13.  
    14.         protected override void OnUpdate()
    15.         {
    16.             if (HasSingleton<ThinClientComponent>())
    17.                 return;
    18.             var inUI = HasSingleton<InUI>();
    19.             if (inUI)
    20.             {
    21.                 if (m_Input.DownThisFrame(GameInputs.ToggleUIMode) && !GetSingleton<InUI>().Any)
    22.                     EntityManager.RemoveComponent<InUI>(GetSingletonEntity<LocalPlayer>());
    23.                 Cursor.lockState = CursorLockMode.None;
    24.             }
    25.             else
    26.             {
    27.                 Cursor.lockState = CursorLockMode.Locked;
    28.                 if (m_Input.DownThisFrame(GameInputs.ToggleUIMode))
    29.                 {
    30.                     EntityManager.AddComponent<InUI>(GetSingletonEntity<LocalPlayer>());
    31.                     return;
    32.                 }
    33.  
    34.                 if (m_Input.DownThisFrame(GameInputs.DrawWeapon))
    35.                 {
    36.                     this.AddIntent(new ToggleWeaponIntent { });
    37.                 }
    38.  
    39.                 if (m_Input.DownThisFrame(GameInputs.Interact))
    40.                 {
    41.                     // Occasionally HasSingleton returns False and GetSingleton throws a matching Exception
    42.                     Logger.Log("Has Camera Anchor: " + HasSingleton<CameraAnchor>());
    43.                     var target = EntityManager.GetComponentData<Target>(GetSingletonEntity<CameraAnchor>()).Value;
    44.  
    45.                     if (target == Entity.Null)
    46.                         return;
    47.                     if (EntityManager.HasComponent<Talkable>(target))
    48.                         this.AddIntent(new StartTalkingIntent { GhostId = EntityManager.GetComponentData<GhostComponent>(target).ghostId });
    49.                     else if (EntityManager.HasComponent<Interactable>(target))
    50.                         this.AddIntent(new InteractIntent { GhostId = EntityManager.GetComponentData<GhostComponent>(target).ghostId });
    51.                 }
    52.             }
    53.  
    54.             if (m_Input.DownThisFrame(GameInputs.Talents))
    55.             {
    56.                 // Occasionally throws InvalidOperationException: Trying to get iterator for Client.InventoryUIAction but the required component type was not declared in the EntityQuery.
    57.                 this.GetOrCreateSingleton<TalentUIAction>();
    58.             }
    59.  
    60.             if (m_Input.DownThisFrame(GameInputs.Inventory))
    61.             {
    62.                 // Occasionally throws InvalidOperationException: Trying to get iterator for Client.InventoryUIAction but the required component type was not declared in the EntityQuery.
    63.                 this.GetOrCreateSingleton<InventoryUIAction>();
    64.             }
    65.         }
    66.     }
    67.  
     
    Last edited: Feb 16, 2020
  7. Cell-i-Zenit

    Cell-i-Zenit

    Joined:
    Mar 11, 2016
    Posts:
    290
    How do you create the singleton?

    I found that you first need to create an entity, then add the data to it in a separate query (both with EntityManager)
     
  8. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    wouldnt line 19 be conflicting with line 21? whats the full error message?
     
  9. Wobbers

    Wobbers

    Joined:
    Dec 31, 2017
    Posts:
    55
    I don't know how NetworkStreamConnection is created. But the others that I have the issues with are Tag components, so there is no data on them.

    Which error message do you mean? line 57 and 63 throw the stack trace at the end of the first post. Line 43 throws
    GetSingletonEntity() requires that exactly one exists but there are 0.
    , which both don't really make sense.

    I don't see a conflict between lines 19 and 21. That part works just fine, had no issues with that yet.