Quillraven / Fleks

Fast, lightweight, multi-platform entity component system in Kotlin
MIT License
179 stars 20 forks source link

ArrayIndexOutOfBoundsException #159

Open AnderWoche opened 3 hours ago

AnderWoche commented 3 hours ago
override fun show() {
        val world = engine.world
        loadScreenSystem(TreasureSystem(world))
        loadScreenSystem(TileMapPathFinderSystem(world))
        loadScreenSystem(TilePathMovementSystem(world))
        loadScreenSystem(AutomaticMoveSystem(world))
        loadScreenSystem(BulletShooterSystem(world))
        loadScreenSystem(EnemyWayWalkerSystem(world)) // crash on this system
    }

    override fun hide() {
        disposeDefenceSystems()
    }

    fun loadScreenSystem(system: IntervalSystem) {
        engine.world.add(system) // it crashed here
        loadedDefenceSystems.add(system)
    }

stackTrace:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
    at com.github.quillraven.fleks.collection.BitArray.hashCode(bitArray.kt:164)
    at com.github.quillraven.fleks.Family.hashCode(family.kt)
    at java.base/java.util.HashMap.hash(HashMap.java:338)
    at java.base/java.util.HashMap.getNode(HashMap.java:568)
    at java.base/java.util.LinkedHashMap.get(LinkedHashMap.java:441)
    at com.github.quillraven.fleks.World.updateAggregatedFamilyHooks(world.kt:506)
    at com.github.quillraven.fleks.World.add(world.kt:232)
    at com.github.quillraven.fleks.World.add(world.kt:242)
    at com.toiletpapertycoon.screen.screens.defence.AbstractDefenceRXScreen.loadScreenSystem(AbstractDefenceRXScreen.kt:31)

The System:

class EnemyWayWalkerSystem(world: World) : IteratingSystem(world = world, family = world.family {
    all(EnemyWayWalkerComponent)
}), FamilyOnAdd {

    private lateinit var treasureSystem: TreasureSystem

    override fun onInit() {
        treasureSystem = world.system()
    }

    override fun onAddEntity(entity: Entity) {
// empty funktion and still crashes
    }
AnderWoche commented 2 hours ago

If I remove the FamilyOnAdd interface, the error does not occur.

Die world.kt datei

    private fun updateAggregatedFamilyHooks(family: Family) {
        // system validation like in initAggregatedFamilyHooks is not necessary
        // because it is already validated before (in initAggregatedFamilyHooks and in add/remove system)

        // update family add hook by adding systems' onAddEntity calls after its original world cfg hook
        val addSystems = systems.filter { it is IteratingSystem && it is FamilyOnAdd && it.family == family }
        val ownAddHook = worldCfgFamilyAddHooks[family]  // it crashed here
        family.addHook = if (ownAddHook != null) { entity ->
            ownAddHook(this, entity)
            addSystems.forEach { (it as FamilyOnAdd).onAddEntity(entity) }
        } else { entity ->
            addSystems.forEach { (it as FamilyOnAdd).onAddEntity(entity) }
        }
Quillraven commented 2 hours ago

Can you provide a simple example on how to reproduce it?

In general: it is not a good practice to link systems together. I see you reference a TreasureSystem and a TileMapPathFinderSystem in your EnemyWayWalkerSystem. That is not a good design. Systems should be separate and ideally don't know from each other.

One solution would be to trigger events and a system reacts on that and does its thing. Another solution is to add components/remove components to an entity to trigger a specific other system.

Second: You are modifying the entity configuration within a family add hook (=onAddEntity). I don't know if that works and that might be the problem with the ArrayIndexOutOfBounds, but I am not 100% sure.

Anyway, to track down the issue, please provide a simplified example where that happens. But maybe it solves itself already when you redesign your approach a little bit, keeping the best practices in my mind that I mentioned above.