adrien-bon / bevy_ecs_tiled

Helpers for working with 2D tilemaps created with the Tiled map editor
MIT License
34 stars 8 forks source link

Panic when loading infinite map: index out of bounds: the len is 256 but the index is 256 #46

Closed 40110 closed 1 week ago

40110 commented 2 weeks ago

The code:

fn main() {
    better_panic::install();

    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(TilemapPlugin)
        .add_plugins(TiledMapPlugin)
        .add_systems(Startup, startup)
        .run();
}

pub fn startup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());

    let map_handle: Handle<TiledMap> = asset_server.load("maps/test.tmx");
    commands.spawn(TiledMapBundle {
        tiled_map: map_handle,
        ..Default::default()
    });
}

The map file:

<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="128" tileheight="128" infinite="1" nextlayerid="2" nextobjectid="1">
 <tileset firstgid="1" source="../tilesets/test.tsx"/>
 <layer id="1" name="Tile Layer 1" width="30" height="20">
  <data encoding="csv">
   <chunk x="0" y="0" width="16" height="16">
1,1,1,1,1,2,1,1,1,1,0,0,0,0,0,0,
1,1,1,1,1,1,1,2,1,1,0,0,0,0,0,0,
1,2,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,
3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,
3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,
3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk>
  </data>
 </layer>
</map>

Backtrace with better-panic = "0.3.0":

INFO bevy_ecs_tiled::loader: Loaded map: maps/test.tmx
INFO bevy_ecs_tiled::loader: Spawning map Some(maps/test.tmx)
INFO bevy_ecs_tiled::loader: topleft: (0, 0), bottomright: (0, 0)
INFO bevy_ecs_tiled::loader: map size: TilemapSize { x: 16, y: 16 }
Backtrace (most recent call first):
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/index.rs", line 307, in <usize as core::slice::index::SliceIndex<[T]>>::index_mut
    &mut (*slice)[self]
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/index.rs", line 27, in core::slice::index::<impl core::ops::index::IndexMut<I> for [T]>::index_mut
    index.index_mut(self)
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs", line 3356, in <alloc::vec::Vec<T,A> as core::ops::index::IndexMut<I>>::index_mut
    IndexMut::index_mut(&mut **self, index)
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs_tilemap-0.14.0/src/tiles/storage.rs", line 65, in bevy_ecs_tilemap::tiles::storage::TileStorage::set
    self.tiles[tile_pos.to_index(&self.size)].replace(tile_entity);
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs_tiled-0.3.10/src/loader.rs", line 839, in bevy_ecs_tiled::loader::load_infinite_tiles_layer
    tile_storage.set(&tile_pos, tile_entity);
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs_tiled-0.3.10/src/loader.rs", line 577, in bevy_ecs_tiled::loader::load_tiles_layer
    let (storage, new_map_size, origin) = load_infinite_tiles_layer(
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs_tiled-0.3.10/src/loader.rs", line 455, in bevy_ecs_tiled::loader::load_map
    load_tiles_layer(
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs_tiled-0.3.10/src/loader.rs", line 291, in bevy_ecs_tiled::loader::process_loaded_maps
    load_map(
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs", line 166, in core::ops::function::FnMut::call_mut
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs", line 294, in core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
    (*self).call_mut(args)
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs-0.14.2/src/system/function_system.rs", line 710, in <Func as bevy_ecs::system::function_system::SystemParamFunction<fn(F0,F1,F2,F3,F4) .> Out>>::run::call_inner
    f($($param,)*)
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs-0.14.2/src/system/function_system.rs", line 713, in <Func as bevy_ecs::system::function_system::SystemParamFunction<fn(F0,F1,F2,F3,F4) .> Out>>::run
    call_inner(self, $($param),*)
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs-0.14.2/src/system/function_system.rs", line 534, in <bevy_ecs::system::function_system::FunctionSystem<Marker,F> as bevy_ecs::system::system::System>::run_unsafe
    let out = self.func.run(input, params);
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs-0.14.2/src/schedule/executor/mod.rs", line 151, in bevy_ecs::schedule::executor::__rust_begin_short_backtrace::run_unsafe
    system.run_unsafe((), world);
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs-0.14.2/src/schedule/executor/multi_threaded.rs", line 593, in bevy_ecs::schedule::executor::multi_threaded::ExecutorState::spawn_system_task::{{closure}}::{{closure}}
    __rust_begin_short_backtrace::run_unsafe(
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs", line 250, in core::ops::function::FnOnce::call_once
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs", line 272, in <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
    (self.0)()
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 557, in std::panicking::try::do_call
    data.r = ManuallyDrop::new(f());
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 520, in std::panicking::try
    return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs", line 358, in std::panic::catch_unwind
    unsafe { panicking::r#try(f) }
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_ecs-0.14.2/src/schedule/executor/multi_threaded.rs", line 587, in bevy_ecs::schedule::executor::multi_threaded::ExecutorState::spawn_system_task::{{closure}}
    let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs", line 297, in <core::panic::unwind_safe::AssertUnwindSafe<F> as core::future::future::Future>::poll
    F::poll(pinned_field, cx)
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.3.0/src/future.rs", line 588, in <futures_lite::future::CatchUnwind<F> as core::future::future::Future>::poll::{{closure}}
    catch_unwind(AssertUnwindSafe(|| this.inner.poll(cx)))?.map(Ok)
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs", line 272, in <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
    (self.0)()
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 557, in std::panicking::try::do_call
    data.r = ManuallyDrop::new(f());
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 520, in std::panicking::try
    return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs", line 358, in std::panic::catch_unwind
    unsafe { panicking::r#try(f) }
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.3.0/src/future.rs", line 588, in <futures_lite::future::CatchUnwind<F> as core::future::future::Future>::poll
    catch_unwind(AssertUnwindSafe(|| this.inner.poll(cx)))?.map(Ok)
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.13.1/src/lib.rs", line 250, in async_executor::Executor::spawn_inner::{{closure}}
    future.await
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.1/src/raw.rs", line 550, in async_task::raw::RawTask<F,T,S,M>::run::{{closure}}
    <F as Future>::poll(Pin::new_unchecked(&mut *raw.future), cx)
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs", line 250, in core::ops::function::FnOnce::call_once
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs", line 272, in <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
    (self.0)()
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 557, in std::panicking::try::do_call
    data.r = ManuallyDrop::new(f());
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 520, in std::panicking::try
    return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs", line 358, in std::panic::catch_unwind
    unsafe { panicking::r#try(f) }
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.1/src/raw.rs", line 549, in async_task::raw::RawTask<F,T,S,M>::run
    match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.1/src/runnable.rs", line 781, in async_task::runnable::Runnable<M>::run
    unsafe { ((*header).vtable.run)(ptr) }
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.13.1/src/lib.rs", line 741, in async_executor::State::run::{{closure}}::{{closure}}
    runnable.run();
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.3.0/src/future.rs", line 449, in <futures_lite::future::Or<F1,F2> as core::future::future::Future>::poll
    if let Poll::Ready(t) = this.future2.poll(cx) {
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.13.1/src/lib.rs", line 748, in async_executor::State::run::{{closure}}
    future.or(run_forever).await
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.13.1/src/lib.rs", line 344, in async_executor::Executor::run::{{closure}}
    self.state().run(future).await
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.3.0/src/future.rs", line 99, in futures_lite::future::block_on::{{closure}}
    match future.as_mut().poll(cx) {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs", line 283, in std::thread::local::LocalKey<T>::try_with
    Ok(f(thread_local))
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs", line 260, in std::thread::local::LocalKey<T>::with
    self.try_with(f).expect(
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.3.0/src/future.rs", line 78, in futures_lite::future::block_on
    CACHE.with(|cache| {
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_tasks-0.14.2/src/task_pool.rs", line 176, in bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}::{{closure}}::{{closure}}
    block_on(ex.run(tick_forever.or(shutdown_rx.recv())))
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 557, in std::panicking::try::do_call
    data.r = ManuallyDrop::new(f());
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 520, in std::panicking::try
    return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs", line 358, in std::panic::catch_unwind
    unsafe { panicking::r#try(f) }
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_tasks-0.14.2/src/task_pool.rs", line 170, in bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}::{{closure}}
    let res = std::panic::catch_unwind(|| {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs", line 283, in std::thread::local::LocalKey<T>::try_with
    Ok(f(thread_local))
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs", line 260, in std::thread::local::LocalKey<T>::with
    self.try_with(f).expect(
  File "$HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_tasks-0.14.2/src/task_pool.rs", line 163, in bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}
    TaskPool::LOCAL_EXECUTOR.with(|local_executor| {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs", line 154, in std::sys::backtrace::__rust_begin_short_backtrace
    let result = f();
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs", line 541, in std::thread::Builder::spawn_unchecked_::{{closure}}::{{closure}}
    crate::sys::backtrace::__rust_begin_short_backtrace(f)
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs", line 272, in <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
    (self.0)()
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 557, in std::panicking::try::do_call
    data.r = ManuallyDrop::new(f());
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs", line 520, in std::panicking::try
    return if intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs", line 358, in std::panic::catch_unwind
    unsafe { panicking::r#try(f) }
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs", line 540, in std::thread::Builder::spawn_unchecked_::{{closure}}
    let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
  File "$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs", line 250, in core::ops::function::FnOnce::call_once{{vtable.shim}}
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
  File "rust:library/alloc/src/boxed.rs", line 2454, in <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
  File "rust:library/alloc/src/boxed.rs", line 2454, in <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
  File "rust:library/std/src/sys/pal/unix/thread.rs", line 105, in std::sys::pal::unix::thread::Thread::new::thread_start

The application panicked (crashed).
  index out of bounds: the len is 256 but the index is 256
adrien-bon commented 1 week ago

Hi! Thanks for the issue :) Seems like this crash happens when you use an infinite map with a tile in the top-left corner. I'll try to get a fix but in the meantime, I believe you could work-around this: