Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArrayIndexOutOfBoundsException #159

Closed
AnderWoche opened this issue Oct 24, 2024 · 7 comments
Closed

ArrayIndexOutOfBoundsException #159

AnderWoche opened this issue Oct 24, 2024 · 7 comments
Labels
bug Something isn't working
Milestone

Comments

@AnderWoche
Copy link

AnderWoche commented Oct 24, 2024

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
Copy link
Author

AnderWoche commented Oct 24, 2024

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
Copy link
Owner

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.

@AnderWoche
Copy link
Author

AnderWoche commented Oct 25, 2024

OMG,
It took me ages to recreate the problem but I managed to do it, can you take a look?

class AnComponent : Component<AnComponent> {
    override fun type() = AnComponent
    companion object : ComponentType<AnComponent>()
}

class StartSystem : IteratingSystem(family = family {
    all(AnComponent)
}) {
    override fun onTickEntity(entity: Entity) {}
}

class AddLaterSystem(world: World, tag: EntityTag) : IteratingSystem(world = world, family = world.family {
    all(tag)
}), FamilyOnAdd {
    override fun onInit() {}
    override fun onTickEntity(entity: Entity) {}
    override fun onAddEntity(entity: Entity) {}
}

fun main() {
    val world = configureWorld {
        systems {
            add(StartSystem())
        }
    }

    for (i in 0 until 62) {
        val entityTag = object : EntityTag() {}
        world.family { all(entityTag) }
    }

    val entityTag = object : EntityTag() {} // crash happens if the ID is EXACTLY 63!!!
    println("entityTagID: ${entityTag.id}")
    val addLaterSystem = AddLaterSystem(world, entityTag)
    println("family: ${addLaterSystem.family}")
    world.add(addLaterSystem) // crash here
}

Output:

entityTagID: 63
family: Family(allOf=0000000000000000000000000000000000000000000000000000000000000001, noneOf=null, anyOf=null, numEntities=0)
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.veryimportant.TestKt.main(Test.kt:48)
	at com.toiletpapertycoon.veryimportant.TestKt.main(Test.kt)

@Quillraven
Copy link
Owner

Nice, thank you! Seems like an issue if someone has more than 64 components. That requires a second long in the BitArray class which seems to crash the hashcode function of it.

I try to have a look on the weekend.

@AnderWoche
Copy link
Author

Yes, you're right, this is the short version of the Crash:

    val bitArray = BitArray(1)
    bitArray.set(63)
    println(bitArray.hashCode()) // crash

AnderWoche added a commit to AnderWoche/Fleks that referenced this issue Oct 25, 2024
AnderWoche added a commit to AnderWoche/Fleks that referenced this issue Oct 25, 2024
@Quillraven
Copy link
Owner

It is quite interesting because I think I just use LibGDX Bits hashCode from https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/utils/Bits.java

Either that is already broken, which I doubt, or I did a mistake somewhere in the class.

But now that we know the issue thanks to you, I think it should not be too difficult to solve it .

AnderWoche added a commit to AnderWoche/Fleks that referenced this issue Oct 26, 2024
AnderWoche added a commit to AnderWoche/Fleks that referenced this issue Oct 26, 2024
@Quillraven Quillraven added the bug Something isn't working label Oct 26, 2024
@Quillraven Quillraven added this to the 2.10 milestone Oct 26, 2024
@Quillraven
Copy link
Owner

Is the problem solved? 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants