A weird Error inside an IJobChunk

Hello Guys,
im facing a weird Error from 2 days now :frowning: any suggestion is welcome

it seems like im having a structural change or something like this.
the error is happening when the PlayersSkinsManagerSystem executes this line:
PostUpdateCommands.AddComponent(entity, new PlayerSkinPrefabIndex { value = indexRef});

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.Jobs;
using Unity.Physics.Systems;
using CWBR.ClientAndServer.Components;
using Utils._Math;

namespace CWBR.ClientAndServer.Systems
{
    // Move Players using on their Inputs
    //[DisableAutoCreation]
    [UpdateInGroup(typeof(SimulationSystemGroup))]
    public class [B][SIZE=5]PlayersMovementSystem [/SIZE][/B]: JobComponentSystem
    {
        private EntityQuery m_Group;


        protected override void OnCreate()
        {
            //this.Enabled = false;
            // Cached access to a set of ComponentData based on a specific query
            m_Group = GetEntityQuery(ComponentType.ReadOnly<MoveSpeed>(), ComponentType.ReadOnly<MouvementInput>(), ComponentType.ReadOnly<AimInput>(), typeof(Rotation), typeof(PhysicsVelocity), typeof(PhysicsMass));
        }



        //Using Translation and Rotation Components ( Depricated )

        /// <summary>
        /// IJobChunk is used to create 1 Worker thread by chunk of 16kB
        /// Prevent from abusing of Thread creation
        /// </summary>
        [BurstCompile]
        struct MouvePlayersJob : IJobChunk
        {

            [ReadOnly] public float defaultMovementSpeed;
            [ReadOnly] public float defaultAimMoveSpeed;
            [ReadOnly] public float defaultRotationSpeed;
            [ReadOnly] public float deltaTime;

            [ReadOnly] public ArchetypeChunkComponentType<MoveSpeed> MoveSpeedType;
            [ReadOnly] public ArchetypeChunkComponentType<MouvementInput> MoveInputType;
            [ReadOnly] public ArchetypeChunkComponentType<AimInput> AimInputType;

            public ArchetypeChunkComponentType<PhysicsMass> PhysicsMassType;
            public ArchetypeChunkComponentType<Rotation> RotationType;
            public ArchetypeChunkComponentType<PhysicsVelocity> PhysicsVelocityType;


            // One Thread by Chunck
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
            {

                var chunkMoveSpeeds = chunk.GetNativeArray(MoveSpeedType);
                var chunkMoveInputs = chunk.GetNativeArray(MoveInputType);
                var chunkAimInputs = chunk.GetNativeArray(AimInputType);

                var chunkPhysicsMass = chunk.GetNativeArray(PhysicsMassType);
                var chunkRotations = chunk.GetNativeArray(RotationType);
                var chunkPhysicsVelocities = chunk.GetNativeArray(PhysicsVelocityType);


                for (int i = 0; i < chunk.Count; i++)
                {
                    var physicsVelocity = chunkPhysicsVelocities[chunkIndex]; [SIZE=5][COLOR=#ff4d4d][B]// Line : 73[/B][/COLOR][/SIZE]

                    // This computation dont need to run if there is no imputs and the player Completed  sync the velocity
                    if (chunkMoveInputs[chunkIndex].Value.Equals(float2.zero) && chunkAimInputs[chunkIndex].Value.Equals(float2.zero))
                    {
                        if (physicsVelocity.Linear.Equals(float3.zero))
                        {
                            continue;
                        }
                        else
                        {
                            physicsVelocity.Linear.x = 0;
                            physicsVelocity.Linear.z = 0;
                            chunkPhysicsVelocities[chunkIndex] = physicsVelocity;
                            continue;
                        }
                    }
                    
                    // Character Moving Only 
                    if (!chunkMoveInputs[chunkIndex].Value.Equals(float2.zero) && chunkAimInputs[chunkIndex].Value.Equals(float2.zero))
                    {
                        // Movement
                        physicsVelocity.Linear = new float3(chunkMoveInputs[chunkIndex].Value.x, 0, chunkMoveInputs[chunkIndex].Value.y) * defaultMovementSpeed  * deltaTime * chunkMoveSpeeds[chunkIndex].value;
                        physicsVelocity.Linear.y = -1;
                        chunkPhysicsVelocities[chunkIndex] = physicsVelocity;



                        // Rotation
                        var finalRotation = Quaternion.LookRotation(math.normalize(new float3(chunkMoveInputs[chunkIndex].Value.x, 0, chunkMoveInputs[chunkIndex].Value.y)), _MathUtils.float3_UP);
                        // fixes the Rotation when using burst
                        //finalRotation.z = 0f;
                        //finalRotation.x = 0f;
                        chunkRotations[chunkIndex] = new Rotation
                        {
                            Value = Quaternion.Slerp(chunkRotations[chunkIndex].Value, finalRotation, defaultRotationSpeed * deltaTime)
                        };
                        
                    }
                    else if (chunkMoveInputs[chunkIndex].Value.Equals(float2.zero) && !chunkAimInputs[chunkIndex].Value.Equals(float2.zero))
                    {   // Character Rotating Only

                        // Rotation
                        var finalRotation = Quaternion.LookRotation(math.normalize(new float3(chunkAimInputs[chunkIndex].Value.x, 0, chunkAimInputs[chunkIndex].Value.y)), _MathUtils.float3_UP);
                        // fixes the Rotation when using burst
                        //finalRotation.z = 0f;
                        //finalRotation.x = 0f;

                        chunkRotations[chunkIndex] = new Rotation
                        {
                            Value = Quaternion.Slerp(chunkRotations[chunkIndex].Value, finalRotation, defaultRotationSpeed * deltaTime)
                        };

                    }
                    else // Character Moving and Rotating
                    {
                        // Movement
                        physicsVelocity.Linear = new float3(chunkMoveInputs[chunkIndex].Value.x, 0, chunkMoveInputs[chunkIndex].Value.y) * defaultMovementSpeed * deltaTime * chunkMoveSpeeds[chunkIndex].value;
                        chunkPhysicsVelocities[chunkIndex] = physicsVelocity;

                        // Rotation
                        var finalRotation = Quaternion.LookRotation(math.normalize(new float3(chunkAimInputs[chunkIndex].Value.x, 0, chunkAimInputs[chunkIndex].Value.y)), _MathUtils.float3_UP);
                     
                        chunkRotations[chunkIndex] = new Rotation
                        {
#if SERVER_BUILD
                            Value = Quaternion.Slerp(chunkRotations[chunkIndex].Value, finalRotation, ServerGameBootstrap.playerDefault_RotationSpeed * deltaTime)
#elif CLIENT_BUILD
                            Value = Quaternion.Slerp(chunkRotations[chunkIndex].Value, finalRotation, ClientGameBootstrap.playerDefault_RotationSpeed * deltaTime)
#endif
                        }; 
                    }



                }


            }
        }

        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            var moveSpeedType = GetArchetypeChunkComponentType<MoveSpeed>(true);
            var moveInputType = GetArchetypeChunkComponentType<MouvementInput>(true);
            var aimInputType = GetArchetypeChunkComponentType<AimInput>(true);

            var physicsMassType = GetArchetypeChunkComponentType<PhysicsMass>(false);
            var rotationType = GetArchetypeChunkComponentType<Rotation>(false);
            var physicsVelocityType = GetArchetypeChunkComponentType<PhysicsVelocity>(false);

            var moveJob = new MouvePlayersJob
            {
#if CLIENT_BUILD
                defaultAimMoveSpeed = ClientGameBootstrap.playerDefault_AimMoveSpeed,
                defaultMovementSpeed = ClientGameBootstrap.playerDefault_MovementSpeed,
                defaultRotationSpeed = ClientGameBootstrap.playerDefault_RotationSpeed,
                deltaTime = ClientGameBootstrap.SimulationDeltaTime,

#elif SERVER_BUILD
                defaultAimMoveSpeed = ServerGameBootstrap.playerDefault_AimMoveSpeed,
                defaultMovementSpeed = ServerGameBootstrap.playerDefault_MovementSpeed,
                defaultRotationSpeed = ServerGameBootstrap.playerDefault_RotationSpeed,
                deltaTime = ServerGameBootstrap.SimulationDeltaTime,
#endif




                MoveInputType = moveInputType,
                MoveSpeedType = moveSpeedType,
                AimInputType = aimInputType,
                PhysicsMassType = physicsMassType,
                RotationType = rotationType,
                PhysicsVelocityType = physicsVelocityType
            };

            return moveJob.Schedule(m_Group, inputDeps);
        }



    }


}
#if CLIENT_BUILD

using CWBR.Client.Components;
using CWBR.Client.Mono;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Utils._Math;

/// <summary>
/// Manage all Players Skins (GO) Instantiation. (it's a Pool of Skins)
/// </summary>
//[DisableAutoCreation]
[UpdateInGroup(typeof(PresentationSystemGroup))]
public class PlayersSkinsManagerSystem : ComponentSystem
{
    public EntityQuery m_PlayersRequireSkinsQuery;
    public EntityQuery m_PlayersRequireSkinRecycleQuery;


    public EntityQuery m_PlayersWithSkinsToMoveQuery;

    private float3 localPlayerPosition;

   

    // Delegate to reduce GC
    EntityQueryBuilder.F_EDDDD<PlayerSkinId, PlayerSkinPrefabIndex, Translation, Rotation> SkinsToMoveDelegate;
    EntityQueryBuilder.F_EDDD<PlayerSkinId, Translation, Rotation> linkPlayersSkinsDelegate;
    EntityQueryBuilder.F_EDD<PlayerSkinId, PlayerSkinPrefabIndex> RecyclePlayersSkinsDelegate;

    #region Value to space Skins Checks Time

    float timePassedSinceLastCheck = 0f; // this value is initialized to 0.6f instead of 0 to instantly show players after game start
    float timebetweenChecks = 1f;

    PlayerSkinsGameObjectPool skinsPool;

    #endregion
    protected override void OnCreate()
    {

        // Players with Skin To Move
        m_PlayersWithSkinsToMoveQuery = GetEntityQuery( ComponentType.ReadOnly<PlayerSkinId>(), ComponentType.ReadOnly<PlayerSkinPrefabIndex>(), ComponentType.ReadOnly<Translation>(), ComponentType.ReadOnly<Rotation>());
        
        // Players Require Skin
        var m_PlayersRequireSkinsQueryDesc = new EntityQueryDesc
        {
            All = new ComponentType[] { ComponentType.ReadOnly<PlayerSkinId>(), ComponentType.ReadOnly<Translation>(), ComponentType.ReadOnly<Rotation>() },
            None = new ComponentType[] { ComponentType.ReadOnly<PlayerSkinPrefabIndex>()}
        };
        m_PlayersRequireSkinsQuery = GetEntityQuery(m_PlayersRequireSkinsQueryDesc);
       
        // Players with Skins to Recycle
        m_PlayersRequireSkinRecycleQuery = GetEntityQuery(ComponentType.ReadOnly<PlayerSkinId>(), ComponentType.ReadOnly<PlayerSkinRecycleTag>(), ComponentType.ReadOnly<PlayerSkinPrefabIndex>());


        // Skins Pool
        skinsPool = new PlayerSkinsGameObjectPool();


        // Local Functions to reduce GC
        SkinsToMoveDelegate = MoveSkins;
        linkPlayersSkinsDelegate = LinkSkins;
        RecyclePlayersSkinsDelegate = RecycleSkins;

        this.Enabled = false;
    }

    

    PlayerSkinPrefabHolder cachedSkinHolder;
    /// <summary>
    /// Link a Skin + animator to a Close Entity (close to the LocalPlayer)
    /// </summary>
    /// <param name="playerSkinId"></param>
    private void LinkSkins(Entity entity, ref PlayerSkinId playerSkinId, ref Translation position, ref Rotation rotation)
    {
       
        if(math.distance(position.Value, localPlayerPosition) - ClientGameBootstrap.playerDefault_MinimumDistanceFromLocalPlayerToShowSkins <= _MathUtils.Epsilon)
        {
            /*
            cachedSkinHolder = skinsPool.GetSkinPrefabHolder(playerSkinId.value,out var indexRef);
           
            EntityManager.AddComponentObject(entity, cachedSkinHolder._transform);
            EntityManager.AddComponentData(entity, new PlayerPrefabIndex { value= indexRef});
            EntityManager.AddComponentData(entity, new CopyTransformToGameObject {}); */

           cachedSkinHolder = skinsPool.GetSkinPrefabHolder(playerSkinId.value,ref position.Value, ref rotation.Value, out var indexRef);
           PostUpdateCommands.AddComponent(entity, new PlayerSkinPrefabIndex { value = indexRef});
        }
    }

    private void MoveSkins(Entity entity, ref PlayerSkinId playerSkinId, ref PlayerSkinPrefabIndex playerPrefabIndex, ref Translation position, ref Rotation rotation)
    {

     
        if (math.distance(position.Value, localPlayerPosition) - ClientGameBootstrap.playerDefault_MaximumDistanceFromPlayerToHideSkins <= _MathUtils.Epsilon)
        {
            // cachedSkinHolder = skinsPool.GetSkinPrefabHolder(playerPrefabIndex.value);
            /*
             EntityManager.RemoveComponent(entity, typeof(Transform));
             EntityManager.RemoveComponent(entity, typeof(CopyTransformToGameObject));
             skinsPool.RecycleSkin(playerSkinId.value, playerPrefabIndex.value);
             EntityManager.RemoveComponent(entity, typeof(PlayerPrefabIndex));*/
            //skinsPool.RecycleSkin(playerSkinId.value, playerPrefabIndex.value);
            // EntityManager.RemoveComponent(entity, typeof(PlayerPrefabIndex));


            skinsPool.MoveObject(playerPrefabIndex.value, ref position.Value, ref rotation.Value);
        }
        else
        {
            EntityManager.AddComponentData(entity, new PlayerSkinRecycleTag{});
        }
    }


    private void RecycleSkins(Entity entity, ref PlayerSkinId playerSkinId, ref PlayerSkinPrefabIndex playerPrefabIndex  )
    { 
        skinsPool.RecycleSkin(playerSkinId.value, playerPrefabIndex.value);
        EntityManager.RemoveComponent(entity, typeof(PlayerSkinPrefabIndex));
        EntityManager.RemoveComponent(entity, typeof(PlayerSkinRecycleTag));
    }

    protected override void OnUpdate()
    {
        // TODO: Emplement this verification in a more elegant way
        if (ClientGameManager.LocalPlayerEntity == Entity.Null)
            return;


        // Players Entities To Move their Skins GOs
      //  Entities.With(m_PlayersWithSkinsToMoveQuery).ForEach(SkinsToMoveDelegate);



        timePassedSinceLastCheck += Time.deltaTime;

        // check if 1/2 seconde has passed since the last check
        if (timePassedSinceLastCheck < timebetweenChecks)
            return;
        else
            timePassedSinceLastCheck = 0;

        // get Local Player Positon
        localPlayerPosition = EntityManager.GetComponentData<Translation>(ClientGameManager.LocalPlayerEntity).Value;

        // Link Players Entities to Skins GOs
        Entities.With(m_PlayersRequireSkinsQuery).ForEach(linkPlayersSkinsDelegate);

        // Players Entities Require Skins GOs Recycle
       // Entities.With(m_PlayersRequireSkinRecycleQuery).ForEach(RecyclePlayersSkinsDelegate);
        
    }




    protected override void OnDestroy()
    {
        skinsPool.Dispose();
    }

}
#endif

ERROR:
IndexOutOfRangeException: Index 1 is out of range of ‘1’ Length.
Unity.Collections.NativeArray1[T].FailOutOfRangeError (System.Int32 index) (at <5e35e4589c1948aa8af5b8e64eea8798>:0) Unity.Collections.NativeArray1[T].CheckElementReadAccess (System.Int32 index) (at <5e35e4589c1948aa8af5b8e64eea8798>:0)
Unity.Collections.NativeArray1[T].get_Item (System.Int32 index) (at <5e35e4589c1948aa8af5b8e64eea8798>:0) CWBR.ClientAndServer.Systems.PlayersMovementSystem+MouvePlayersJob.Execute (Unity.Entities.ArchetypeChunk chunk, System.Int32 chunkIndex, System.Int32 firstEntityIndex) (at Assets/Scripts/ECS/Systems/Client&Server/PlayersMouvementSystem.cs:73) Unity.Entities.JobChunkExtensions+JobChunk_Process1[T].ExecuteInternal (Unity.Entities.JobChunkExtensions+JobChunkData1[T]& jobData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/IJobChunk.cs:183) Unity.Entities.JobChunkExtensions+JobChunk_Process1[T].Execute (Unity.Entities.JobChunkExtensions+JobChunkData`1[T]& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.entities@0.1.1-preview/Unity.Entities/IJobChunk.cs:170)

If you using AddComponent(Entity e, ComponentType type) you should provide ComponentType. If you want to add component type and set it value you should use AddComponent(Entity e, T component). It’s diferent things.

by simply providing the value to AddComponent it will predict the T type.

by writing

 PostUpdateCommands.AddComponent<PlayerSkinPrefabIndex>(entity, new PlayerSkinPrefabIndex { value = indexRef});

as you suggested the VS provide a quick simplification to the

PostUpdateCommands.AddComponent(entity, new PlayerSkinPrefabIndex { value = indexRef});

Yep generic prediction, forgot about that. Yeah in this case it’ll be correct overload.

The Error is weird cause it’s being thrown from ECS boilerplate Code. it’s pratically when i get the ArchetypeChunkComponentType Array from the Chunk and try to access it by the chunkIndex.

[BurstCompile]
        struct MouvePlayersJob : IJobChunk
        { 
            public ArchetypeChunkComponentType<PhysicsVelocity> PhysicsVelocityType;


            // One Thread by Chunck
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
            { 
                var chunkPhysicsVelocities = chunk.GetNativeArray(PhysicsVelocityType);

                for (int i = 0; i < chunk.Count; i++)
                {
                    var physicsVelocity = chunkPhysicsVelocities[chunkIndex];  // Error here
                 }

Error:
ERROR:
IndexOutOfRangeException: Index 1 is out of range of ‘1’ Length.

the only reason that seems logic to me in this case is a structural change caused by the PostUpdateCommands.AddComponent .

I looked to your error closely and now all obvious. Your problem not relates to PostUpdateCommands.AddComponent(entity, new PlayerSkinPrefabIndex { value = indexRef}); your problem is you trying to get element outside of array bounds. In your 73 line var physicsVelocity = chunkPhysicsVelocities[chunkIndex], it should be “i” instead of chunkIndex, and in all next places inside loop. When you get array from chunk you get it in 0-chunk.Count range, which not related to chunkIndex at all. ChunkIndex is for calculating chunk data offset in some global range (for example in array of Transforms for all archetype), arrays itself at 0 to chunk.Count. Thus you of course get out of range exception, cause your array is 1 item with index 0 and you try to access it by index 1.

It doesn’t doing any structural changes itself. All structural changes will be later at buffer playback time in barrier system.

1 Like

XD OMG for an unkown reason i was using the chunkIndex instead of the “i” and it was working cause all of them are being part of the same Chunk until i use the PostUpdateCommands.AddComponent and add a componentData to one of this chunk Entities. which will create a second chunk with a different index.

XD thank you it salved the problem